Explorar o código

Merge remote-tracking branch 'origin/develop' into develop

anhaohao hai 1 ano
pai
achega
fdd5d55fc8
Modificáronse 100 ficheiros con 2119 adicións e 906 borrados
  1. BIN=BIN
      .image/common/mall-preview.png
  2. 0 2
      README.md
  3. 5 4
      pom.xml
  4. 1 1
      script/shell/deploy.sh
  5. 1180 474
      sql/mysql/ruoyi-vue-pro.sql
  6. 20 35
      yudao-dependencies/pom.xml
  7. 0 4
      yudao-framework/pom.xml
  8. 2 2
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/PageUtils.java
  9. 0 1
      yudao-framework/yudao-spring-boot-starter-banner/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  10. 0 49
      yudao-framework/yudao-spring-boot-starter-biz-error-code/pom.xml
  11. 0 1
      yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  12. 0 82
      yudao-framework/yudao-spring-boot-starter-biz-sms/pom.xml
  13. 0 21
      yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/config/YudaoSmsAutoConfiguration.java
  14. 0 50
      yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/enums/SmsFrameworkErrorCodeConstants.java
  15. 0 1
      yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  16. 0 38
      yudao-framework/yudao-spring-boot-starter-desensitize/pom.xml
  17. 0 7
      yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml
  18. 17 1
      yudao-framework/yudao-spring-boot-starter-web/pom.xml
  19. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/banner/config/YudaoBannerAutoConfiguration.java
  20. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/banner/core/BannerApplicationRunner.java
  21. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/banner/package-info.java
  22. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/annotation/DesensitizeBy.java
  23. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/handler/DesensitizationHandler.java
  24. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/serializer/StringDesensitizeSerializer.java
  25. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/annotation/EmailDesensitize.java
  26. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/annotation/RegexDesensitize.java
  27. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/handler/AbstractRegexDesensitizationHandler.java
  28. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/handler/DefaultRegexDesensitizationHandler.java
  29. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/handler/EmailDesensitizationHandler.java
  30. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/BankCardDesensitize.java
  31. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/CarLicenseDesensitize.java
  32. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/ChineseNameDesensitize.java
  33. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/FixedPhoneDesensitize.java
  34. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/IdCardDesensitize.java
  35. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/MobileDesensitize.java
  36. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/PasswordDesensitize.java
  37. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/SliderDesensitize.java
  38. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/AbstractSliderDesensitizationHandler.java
  39. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/BankCardDesensitization.java
  40. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/CarLicenseDesensitization.java
  41. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/ChineseNameDesensitization.java
  42. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/DefaultDesensitizationHandler.java
  43. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/FixedPhoneDesensitization.java
  44. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/IdCardDesensitization.java
  45. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/MobileDesensitization.java
  46. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/PasswordDesensitization.java
  47. 1 1
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/package-info.java
  48. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/errorcode/config/ErrorCodeProperties.java
  49. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/errorcode/config/YudaoErrorCodeAutoConfiguration.java
  50. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/errorcode/core/generator/ErrorCodeAutoGenerator.java
  51. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/errorcode/core/generator/ErrorCodeAutoGeneratorImpl.java
  52. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/errorcode/core/loader/ErrorCodeLoader.java
  53. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/errorcode/core/loader/ErrorCodeLoaderImpl.java
  54. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/errorcode/package-info.java
  55. 2 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  56. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/resources/banner.txt
  57. 8 12
      yudao-framework/yudao-spring-boot-starter-web/src/test/java/cn/iocoder/yudao/framework/desensitize/core/DesensitizeTest.java
  58. 1 1
      yudao-framework/yudao-spring-boot-starter-web/src/test/java/cn/iocoder/yudao/framework/desensitize/core/annotation/Address.java
  59. 0 0
      yudao-framework/yudao-spring-boot-starter-web/src/test/java/cn/iocoder/yudao/framework/desensitize/core/handler/AddressHandler.java
  60. 0 1
      yudao-module-crm/pom.xml
  61. 6 0
      yudao-module-crm/yudao-module-crm-biz/pom.xml
  62. 1 1
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java
  63. 1 1
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessStatusTypeController.java
  64. 1 1
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java
  65. 9 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmCluePageReqVO.java
  66. 35 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueRespVO.java
  67. 47 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueSaveReqVO.java
  68. 1 1
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueTranslateReqVO.java
  69. 5 7
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/CrmOperateLogController.java
  70. 44 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogV2RespVO.java
  71. 3 5
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/permission/CrmPermissionConvert.java
  72. 36 7
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/clue/CrmClueDO.java
  73. 5 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerDO.java
  74. 3 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/clue/CrmClueMapper.java
  75. 8 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java
  76. 29 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/util/CrmPermissionUtils.java
  77. 27 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/job/customer/CrmCustomerAutoPutPoolJob.java
  78. 4 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/job/package-info.java
  79. 2 2
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueService.java
  80. 24 29
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java
  81. 8 2
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactBusinessServiceImpl.java
  82. 12 4
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java
  83. 71 30
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java
  84. 2 2
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordService.java
  85. 11 18
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordServiceImpl.java
  86. 0 2
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/bo/CrmUpdateFollowUpReqBO.java
  87. 24 0
      yudao-module-erp/pom.xml
  88. 9 6
      yudao-module-erp/yudao-module-erp-api/pom.xml
  89. 4 0
      yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/package-info.java
  90. 10 0
      yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/DictTypeConstants.java
  91. 15 0
      yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java
  92. 12 0
      yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/LogRecordConstants.java
  93. 40 0
      yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/sale/ErpSaleOrderStatusEnum.java
  94. 72 0
      yudao-module-erp/yudao-module-erp-biz/pom.xml
  95. 4 0
      yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.http
  96. 100 0
      yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java
  97. 39 0
      yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderPageReqVO.java
  98. 78 0
      yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java
  99. 74 0
      yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderSaveReqVO.java
  100. 6 0
      yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/package-info.java

BIN=BIN
.image/common/mall-preview.png


+ 0 - 2
README.md

@@ -235,8 +235,6 @@
 
 ![功能图](/.image/common/mall-preview.png)
 
-_前端基于 crmeb uniapp 经过授权重构,优化代码实现,接入芋道快速开发平台_
-
 演示地址:<https://doc.iocoder.cn/mall-preview/>
 
 ### 会员中心

+ 5 - 4
pom.xml

@@ -15,13 +15,14 @@
         <!-- 各种 module 拓展 -->
         <module>yudao-module-system</module>
         <module>yudao-module-infra</module>
-        <module>yudao-module-member</module>
+<!--        <module>yudao-module-member</module>-->
 <!--        <module>yudao-module-bpm</module>-->
 <!--        <module>yudao-module-report</module>-->
 <!--        <module>yudao-module-mp</module>-->
-        <module>yudao-module-pay</module>
-        <module>yudao-module-mall</module>
+<!--        <module>yudao-module-pay</module>-->
+<!--        <module>yudao-module-mall</module>-->
         <module>yudao-module-crm</module>
+        <module>yudao-module-erp</module>
         <!-- 示例项目 -->
 <!--        <module>yudao-example</module>-->
     </modules>
@@ -41,7 +42,7 @@
         <flatten-maven-plugin.version>1.5.0</flatten-maven-plugin.version>
         <!-- 看看咋放到 bom 里 -->
         <lombok.version>1.18.30</lombok.version>
-        <spring.boot.version>3.2.0</spring.boot.version>
+        <spring.boot.version>3.2.2</spring.boot.version>
         <mapstruct.version>1.5.5.Final</mapstruct.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     </properties>

+ 1 - 1
script/shell/deploy.sh

@@ -74,7 +74,7 @@ function stop() {
                 if [ -n "$PID" ]; then
                     echo -e ".\c"
                 else
-                    echo '[stop] 停止 $BASE_PATH/$SERVER_NAME 成功'
+                    echo "[stop] 停止 $BASE_PATH/$SERVER_NAME 成功"
                     break
                 fi
 		    done

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1180 - 474
sql/mysql/ruoyi-vue-pro.sql


+ 20 - 35
yudao-dependencies/pom.xml

@@ -17,47 +17,47 @@
         <revision>2.0.0-snapshot</revision>
         <flatten-maven-plugin.version>1.5.0</flatten-maven-plugin.version>
         <!-- 统一依赖管理 -->
-        <spring.boot.version>3.2.0</spring.boot.version>
+        <spring.boot.version>3.2.2</spring.boot.version>
         <!-- Web 相关 -->
         <springdoc.version>2.2.0</springdoc.version>
         <knife4j.version>4.3.0</knife4j.version>
         <!-- DB 相关 -->
-        <druid.version>1.2.20</druid.version>
-        <mybatis-plus.version>3.5.4.1</mybatis-plus.version>
-        <mybatis-plus-generator.version>3.5.4.1</mybatis-plus-generator.version>
-        <dynamic-datasource.version>4.2.0</dynamic-datasource.version>
-        <mybatis-plus-join.version>1.4.8.1</mybatis-plus-join.version>
-        <redisson.version>3.25.0</redisson.version>
+        <druid.version>1.2.21</druid.version>
+        <mybatis-plus.version>3.5.5</mybatis-plus.version>
+        <mybatis-plus-generator.version>3.5.5</mybatis-plus-generator.version>
+        <dynamic-datasource.version>4.3.0</dynamic-datasource.version>
+        <mybatis-plus-join.version>1.4.10</mybatis-plus-join.version>
+        <redisson.version>3.26.0</redisson.version>
         <dm8.jdbc.version>8.1.3.62</dm8.jdbc.version>
         <!-- 消息队列 -->
         <rocketmq-spring.version>2.2.3</rocketmq-spring.version>
         <!-- 服务保障相关 -->
-        <lock4j.version>2.2.5</lock4j.version>
+        <lock4j.version>2.2.7</lock4j.version>
         <resilience4j.version>2.1.0</resilience4j.version>
         <!-- 监控相关 -->
         <skywalking.version>9.0.0</skywalking.version>
-        <spring-boot-admin.version>3.1.8</spring-boot-admin.version>
+        <spring-boot-admin.version>3.2.1</spring-boot-admin.version>
         <opentracing.version>0.33.0</opentracing.version>
         <!-- Test 测试相关 -->
-        <podam.version>8.0.0.RELEASE</podam.version>
-        <jedis-mock.version>1.0.12</jedis-mock.version>
+        <podam.version>8.0.1.RELEASE</podam.version>
+        <jedis-mock.version>1.0.13</jedis-mock.version>
         <mockito-inline.version>5.2.0</mockito-inline.version>
         <!-- Bpm 工作流相关 -->
-        <flowable.version>7.0.0</flowable.version>
+        <flowable.version>7.0.1</flowable.version>
         <!-- 工具类相关 -->
         <captcha-plus.version>2.0.3</captcha-plus.version>
-        <jsoup.version>1.17.1</jsoup.version>
+        <jsoup.version>1.17.2</jsoup.version>
         <lombok.version>1.18.30</lombok.version>
         <mapstruct.version>1.5.5.Final</mapstruct.version>
-        <hutool-5.version>5.8.23</hutool-5.version>
-        <hutool-6.version>6.0.0-M8</hutool-6.version>
+        <hutool-5.version>5.8.25</hutool-5.version>
+        <hutool-6.version>6.0.0-M10</hutool-6.version>
         <easyexcel.verion>3.3.3</easyexcel.verion>
         <velocity.version>2.3</velocity.version>
         <screw.version>1.0.5</screw.version>
         <fastjson.version>1.2.83</fastjson.version>
-        <guava.version>32.1.3-jre</guava.version>
+        <guava.version>33.0.0-jre</guava.version>
         <guice.version>5.1.0</guice.version>
-        <transmittable-thread-local.version>2.14.4</transmittable-thread-local.version>
+        <transmittable-thread-local.version>2.14.5</transmittable-thread-local.version>
         <commons-net.version>3.10.0</commons-net.version>
         <jsch.version>0.1.55</jsch.version>
         <tika-core.version>2.9.1</tika-core.version>
@@ -66,15 +66,15 @@
         <!-- 三方云服务相关 -->
         <okio.version>3.5.0</okio.version>
         <okhttp3.version>4.11.0</okhttp3.version>
-        <commons-io.version>2.11.0</commons-io.version>
-        <minio.version>8.5.6</minio.version>
+        <commons-io.version>2.15.1</commons-io.version>
+        <minio.version>8.5.7</minio.version>
         <aliyun-java-sdk-core.version>4.6.4</aliyun-java-sdk-core.version>
         <aliyun-java-sdk-dysmsapi.version>2.2.1</aliyun-java-sdk-dysmsapi.version>
         <tencentcloud-sdk-java.version>3.1.880</tencentcloud-sdk-java.version>
         <justauth.version>2.0.5</justauth.version>
         <jimureport.version>1.6.6-beta2</jimureport.version>
         <xercesImpl.version>2.12.2</xercesImpl.version>
-        <weixin-java.version>4.5.7.B</weixin-java.version>
+        <weixin-java.version>4.6.0</weixin-java.version>
         <ureport2.version>2.2.9</ureport2.version>
     </properties>
 
@@ -90,11 +90,6 @@
             </dependency>
 
             <!-- 业务组件 -->
-            <dependency>
-                <groupId>cn.iocoder.boot</groupId>
-                <artifactId>yudao-spring-boot-starter-banner</artifactId>
-                <version>${revision}</version>
-            </dependency>
             <dependency>
                 <groupId>cn.iocoder.boot</groupId>
                 <artifactId>yudao-spring-boot-starter-biz-operatelog</artifactId>
@@ -116,11 +111,6 @@
                 <artifactId>yudao-spring-boot-starter-biz-dict</artifactId>
                 <version>${revision}</version>
             </dependency>
-            <dependency>
-                <groupId>cn.iocoder.boot</groupId>
-                <artifactId>yudao-spring-boot-starter-biz-sms</artifactId>
-                <version>${revision}</version>
-            </dependency>
             <dependency>
                 <groupId>cn.iocoder.boot</groupId>
                 <artifactId>yudao-spring-boot-starter-biz-pay</artifactId>
@@ -136,11 +126,6 @@
                 <artifactId>yudao-spring-boot-starter-biz-data-permission</artifactId>
                 <version>${revision}</version>
             </dependency>
-            <dependency>
-                <groupId>cn.iocoder.boot</groupId>
-                <artifactId>yudao-spring-boot-starter-biz-error-code</artifactId>
-                <version>${revision}</version>
-            </dependency>
             <dependency>
                 <groupId>cn.iocoder.boot</groupId>
                 <artifactId>yudao-spring-boot-starter-biz-ip</artifactId>

+ 0 - 4
yudao-framework/pom.xml

@@ -11,7 +11,6 @@
     <packaging>pom</packaging>
     <modules>
         <module>yudao-common</module>
-        <module>yudao-spring-boot-starter-banner</module>
         <module>yudao-spring-boot-starter-mybatis</module>
         <module>yudao-spring-boot-starter-redis</module>
         <module>yudao-spring-boot-starter-web</module>
@@ -28,18 +27,15 @@
 
         <module>yudao-spring-boot-starter-biz-operatelog</module>
         <module>yudao-spring-boot-starter-biz-dict</module>
-        <module>yudao-spring-boot-starter-biz-sms</module>
 
         <module>yudao-spring-boot-starter-biz-pay</module>
         <module>yudao-spring-boot-starter-biz-tenant</module>
         <module>yudao-spring-boot-starter-biz-data-permission</module>
-        <module>yudao-spring-boot-starter-biz-error-code</module>
         <module>yudao-spring-boot-starter-biz-ip</module>
 
         <module>yudao-spring-boot-starter-flowable</module>
         <module>yudao-spring-boot-starter-captcha</module>
         <module>yudao-spring-boot-starter-websocket</module>
-        <module>yudao-spring-boot-starter-desensitize</module>
     </modules>
 
     <artifactId>yudao-framework</artifactId>

+ 2 - 2
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/PageUtils.java

@@ -9,7 +9,7 @@ import cn.iocoder.yudao.framework.common.pojo.SortablePageParam;
 import cn.iocoder.yudao.framework.common.pojo.SortingField;
 import org.springframework.util.Assert;
 
-import java.util.List;
+import static java.util.Collections.singletonList;
 
 /**
  * {@link cn.iocoder.yudao.framework.common.pojo.PageParam} 工具类
@@ -60,7 +60,7 @@ public class PageUtils {
      */
     public static <T> void buildDefaultSortingField(SortablePageParam sortablePageParam, Func1<T, ?> func) {
         if (sortablePageParam != null && CollUtil.isEmpty(sortablePageParam.getSortingFields())) {
-            sortablePageParam.setSortingFields(List.of(buildSortingField(func)));
+            sortablePageParam.setSortingFields(singletonList(buildSortingField(func)));
         }
     }
 

+ 0 - 1
yudao-framework/yudao-spring-boot-starter-banner/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

@@ -1 +0,0 @@
-cn.iocoder.yudao.framework.banner.config.YudaoBannerAutoConfiguration

+ 0 - 49
yudao-framework/yudao-spring-boot-starter-biz-error-code/pom.xml

@@ -1,49 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <parent>
-        <artifactId>yudao-framework</artifactId>
-        <groupId>cn.iocoder.boot</groupId>
-        <version>${revision}</version>
-    </parent>
-    <modelVersion>4.0.0</modelVersion>
-    <artifactId>yudao-spring-boot-starter-biz-error-code</artifactId>
-    <packaging>jar</packaging>
-
-    <name>${project.artifactId}</name>
-    <description>
-        错误码 ErrorCode 的自动配置功能,提供如下功能:
-        1. 远程读取:项目启动时,从 system-server 服务,读取数据库中的 ErrorCode 错误码,实现错误码的提示可配置;
-        2. 自动更新:管理员在管理后台修数据库中的 ErrorCode 错误码时,项目自动从 system-server 服务加载最新的 ErrorCode 错误码;
-        3. 自动写入:项目启动时,将项目本地的错误码写到 system-server 服务中,方便管理员在管理后台编辑;
-    </description>
-    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
-
-    <dependencies>
-        <dependency>
-            <groupId>cn.iocoder.boot</groupId>
-            <artifactId>yudao-common</artifactId>
-        </dependency>
-
-        <!-- Spring 核心 -->
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter</artifactId>
-        </dependency>
-
-        <!-- 业务组件 -->
-        <dependency>
-            <groupId>cn.iocoder.boot</groupId>
-            <artifactId>yudao-module-system-api</artifactId> <!-- 需要使用它,进行操作日志的记录 -->
-            <version>${revision}</version>
-        </dependency>
-
-        <dependency>
-            <groupId>jakarta.validation</groupId>
-            <artifactId>jakarta.validation-api</artifactId>
-            <scope>provided</scope> <!-- 设置为 provided,主要是 ErrorCodeProperties 使用到 -->
-        </dependency>
-    </dependencies>
-
-</project>

+ 0 - 1
yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

@@ -1 +0,0 @@
-cn.iocoder.yudao.framework.errorcode.config.YudaoErrorCodeAutoConfiguration

+ 0 - 82
yudao-framework/yudao-spring-boot-starter-biz-sms/pom.xml

@@ -1,82 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <parent>
-        <groupId>cn.iocoder.boot</groupId>
-        <artifactId>yudao-framework</artifactId>
-        <version>${revision}</version>
-    </parent>
-    <modelVersion>4.0.0</modelVersion>
-    <artifactId>yudao-spring-boot-starter-biz-sms</artifactId>
-    <packaging>jar</packaging>
-
-    <name>${project.artifactId}</name>
-    <description>短信拓展,支持阿里云、腾讯云</description>
-    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
-
-    <dependencies>
-        <dependency>
-            <groupId>cn.iocoder.boot</groupId>
-            <artifactId>yudao-common</artifactId>
-        </dependency>
-
-        <!-- Spring 核心 -->
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter</artifactId>
-        </dependency>
-
-        <!-- 监控相关 -->
-        <dependency>
-            <groupId>io.opentracing</groupId>
-            <artifactId>opentracing-util</artifactId> <!-- aliyun 短信需要,进行链路追踪 -->
-        </dependency>
-
-        <!-- Test 测试相关 -->
-        <dependency>
-            <groupId>cn.iocoder.boot</groupId>
-            <artifactId>yudao-spring-boot-starter-test</artifactId>
-            <scope>test</scope>
-        </dependency>
-
-        <!-- 工具类相关 -->
-        <dependency>
-            <groupId>com.google.guava</groupId>
-            <artifactId>guava</artifactId>
-            <optional>true</optional> <!-- 设置为可选,因为使用到 @VisibleForTesting 用于单元测试 -->
-        </dependency>
-
-        <dependency>
-            <groupId>com.fasterxml.jackson.core</groupId>
-            <artifactId>jackson-databind</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.fasterxml.jackson.core</groupId>
-            <artifactId>jackson-core</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>jakarta.validation</groupId>
-            <artifactId>jakarta.validation-api</artifactId>
-        </dependency>
-
-        <!-- 三方云服务相关 -->
-
-        <!-- SMS SDK begin -->
-        <dependency>
-            <groupId>com.aliyun</groupId>
-            <artifactId>aliyun-java-sdk-core</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.aliyun</groupId>
-            <artifactId>aliyun-java-sdk-dysmsapi</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.tencentcloudapi</groupId>
-            <artifactId>tencentcloud-sdk-java-sms</artifactId>
-        </dependency>
-        <!-- SMS SDK end -->
-    </dependencies>
-
-</project>

+ 0 - 21
yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/config/YudaoSmsAutoConfiguration.java

@@ -1,21 +0,0 @@
-package cn.iocoder.yudao.framework.sms.config;
-
-import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;
-import cn.iocoder.yudao.framework.sms.core.client.impl.SmsClientFactoryImpl;
-import org.springframework.boot.autoconfigure.AutoConfiguration;
-import org.springframework.context.annotation.Bean;
-
-/**
- * 短信配置类
- *
- * @author 芋道源码
- */
-@AutoConfiguration
-public class YudaoSmsAutoConfiguration {
-
-    @Bean
-    public SmsClientFactory smsClientFactory() {
-        return new SmsClientFactoryImpl();
-    }
-
-}

+ 0 - 50
yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/enums/SmsFrameworkErrorCodeConstants.java

@@ -1,50 +0,0 @@
-package cn.iocoder.yudao.framework.sms.core.enums;
-
-import cn.iocoder.yudao.framework.common.exception.ErrorCode;
-
-/**
- * 短信框架的错误码枚举
- *
- * 短信框架,使用 2-001-000-000 段
- *
- * @author 芋道源码
- */
-public interface SmsFrameworkErrorCodeConstants {
-
-    ErrorCode SMS_UNKNOWN = new ErrorCode(2_001_000_000, "未知错误,需要解析");
-
-    // ========== 权限 / 限流等相关 2-001-000-100 ==========
-
-    ErrorCode SMS_PERMISSION_DENY = new ErrorCode(2_001_000_100, "没有发送短信的权限");
-    ErrorCode SMS_IP_DENY = new ErrorCode(2_001_000_100, "IP 不允许发送短信");
-
-    // 阿里云:将短信发送频率限制在正常的业务限流范围内。默认短信验证码:使用同一签名,对同一个手机号验证码,支持 1 条 / 分钟,5 条 / 小时,累计 10 条 / 天。
-    ErrorCode SMS_SEND_BUSINESS_LIMIT_CONTROL = new ErrorCode(2_001_000_102, "指定手机的发送限流");
-    // 阿里云:已经达到您在控制台设置的短信日发送量限额值。在国内消息设置 > 安全设置,修改发送总量阈值。
-    ErrorCode SMS_SEND_DAY_LIMIT_CONTROL = new ErrorCode(2_001_000_103, "每天的发送限流");
-
-    ErrorCode SMS_SEND_CONTENT_INVALID = new ErrorCode(2_001_000_104, "短信内容有敏感词");
-
-    // 腾讯云:为避免骚扰用户,营销短信只允许在8点到22点发送。
-    ErrorCode SMS_SEND_MARKET_LIMIT_CONTROL = new ErrorCode(2_001_000_105, "营销短信发送时间限制");
-
-    // ========== 模板相关 2-001-000-200 ==========
-    ErrorCode SMS_TEMPLATE_INVALID = new ErrorCode(2_001_000_200, "短信模板不合法"); // 包括短信模板不存在
-    ErrorCode SMS_TEMPLATE_PARAM_ERROR = new ErrorCode(2_001_000_201, "模板参数不正确");
-
-    // ========== 签名相关 2-001-000-300 ==========
-    ErrorCode SMS_SIGN_INVALID = new ErrorCode(2_001_000_300, "短信签名不可用");
-
-    // ========== 账户相关 2-001-000-400 ==========
-    ErrorCode SMS_ACCOUNT_MONEY_NOT_ENOUGH = new ErrorCode(2_001_000_400, "账户余额不足");
-    ErrorCode SMS_ACCOUNT_INVALID = new ErrorCode(2_001_000_401, "apiKey 不存在");
-
-    // ========== 其它相关 2-001-000-900 开头 ==========
-    ErrorCode SMS_API_PARAM_ERROR = new ErrorCode(2_001_000_900, "请求参数缺失");
-    ErrorCode SMS_MOBILE_INVALID = new ErrorCode(2_001_000_901, "手机格式不正确");
-    ErrorCode SMS_MOBILE_BLACK = new ErrorCode(2_001_000_902, "手机号在黑名单中");
-    ErrorCode SMS_APP_ID_INVALID = new ErrorCode(2_001_000_903, "SdkAppId不合法");
-
-    ErrorCode EXCEPTION = new ErrorCode(2_001_000_999, "调用异常");
-
-}

+ 0 - 1
yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

@@ -1 +0,0 @@
-cn.iocoder.yudao.framework.sms.config.YudaoSmsAutoConfiguration

+ 0 - 38
yudao-framework/yudao-spring-boot-starter-desensitize/pom.xml

@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-    <parent>
-        <artifactId>yudao-framework</artifactId>
-        <groupId>cn.iocoder.boot</groupId>
-        <version>${revision}</version>
-    </parent>
-
-    <artifactId>yudao-spring-boot-starter-desensitize</artifactId>
-    <description>脱敏组件:支持 JSON 返回数据时,将邮箱、手机等字段进行脱敏</description>
-
-    <dependencies>
-        <dependency>
-            <groupId>cn.iocoder.boot</groupId>
-            <artifactId>yudao-common</artifactId>
-        </dependency>
-
-        <!-- jackson -->
-        <dependency>
-            <groupId>com.fasterxml.jackson.core</groupId>
-            <artifactId>jackson-annotations</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.fasterxml.jackson.core</groupId>
-            <artifactId>jackson-databind</artifactId>
-        </dependency>
-
-        <!-- Test 测试相关 -->
-        <dependency>
-            <groupId>cn.iocoder.boot</groupId>
-            <artifactId>yudao-spring-boot-starter-test</artifactId>
-            <scope>test</scope>
-        </dependency>
-    </dependencies>
-</project>

+ 0 - 7
yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml

@@ -77,13 +77,6 @@
             <groupId>com.github.yulichang</groupId>
             <artifactId>mybatis-plus-join-boot-starter</artifactId> <!-- MyBatis 联表查询 -->
         </dependency>
-
-        <!-- TODO 芋艿:临时解决 spring boot 3.x 适配 -->
-        <dependency>
-            <groupId>org.mybatis</groupId>
-            <artifactId>mybatis-spring</artifactId>
-            <version>3.0.3</version>
-        </dependency>
     </dependencies>
 
 </project>

+ 17 - 1
yudao-framework/yudao-spring-boot-starter-web/pom.xml

@@ -12,7 +12,7 @@
     <packaging>jar</packaging>
 
     <name>${project.artifactId}</name>
-    <description>Web 框架,全局异常、API 日志等</description>
+    <description>Web 框架,全局异常、API 日志、脱敏、错误码等</description>
     <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
 
     <dependencies>
@@ -54,6 +54,11 @@
             <artifactId>yudao-module-infra-api</artifactId> <!-- 需要使用它,进行操作日志的记录 -->
             <version>${revision}</version>
         </dependency>
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-module-system-api</artifactId> <!-- 需要使用它,进行错误码的记录 -->
+            <version>${revision}</version>
+        </dependency>
 
         <!-- xss -->
         <dependency>
@@ -61,6 +66,17 @@
             <artifactId>jsoup</artifactId>
         </dependency>
 
+        <!-- Test 测试相关 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-inline</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
 </project>

+ 0 - 0
yudao-framework/yudao-spring-boot-starter-banner/src/main/java/cn/iocoder/yudao/framework/banner/config/YudaoBannerAutoConfiguration.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/banner/config/YudaoBannerAutoConfiguration.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-banner/src/main/java/cn/iocoder/yudao/framework/banner/core/BannerApplicationRunner.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/banner/core/BannerApplicationRunner.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-banner/src/main/java/cn/iocoder/yudao/framework/banner/package-info.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/banner/package-info.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/annotation/DesensitizeBy.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/annotation/DesensitizeBy.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/handler/DesensitizationHandler.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/handler/DesensitizationHandler.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/serializer/StringDesensitizeSerializer.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/serializer/StringDesensitizeSerializer.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/annotation/EmailDesensitize.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/annotation/EmailDesensitize.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/annotation/RegexDesensitize.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/annotation/RegexDesensitize.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/handler/AbstractRegexDesensitizationHandler.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/handler/AbstractRegexDesensitizationHandler.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/handler/DefaultRegexDesensitizationHandler.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/handler/DefaultRegexDesensitizationHandler.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/handler/EmailDesensitizationHandler.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/handler/EmailDesensitizationHandler.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/BankCardDesensitize.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/BankCardDesensitize.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/CarLicenseDesensitize.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/CarLicenseDesensitize.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/ChineseNameDesensitize.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/ChineseNameDesensitize.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/FixedPhoneDesensitize.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/FixedPhoneDesensitize.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/IdCardDesensitize.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/IdCardDesensitize.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/MobileDesensitize.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/MobileDesensitize.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/PasswordDesensitize.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/PasswordDesensitize.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/SliderDesensitize.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/SliderDesensitize.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/AbstractSliderDesensitizationHandler.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/AbstractSliderDesensitizationHandler.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/BankCardDesensitization.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/BankCardDesensitization.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/CarLicenseDesensitization.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/CarLicenseDesensitization.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/ChineseNameDesensitization.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/ChineseNameDesensitization.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/DefaultDesensitizationHandler.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/DefaultDesensitizationHandler.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/FixedPhoneDesensitization.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/FixedPhoneDesensitization.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/IdCardDesensitization.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/IdCardDesensitization.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/MobileDesensitization.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/MobileDesensitization.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/PasswordDesensitization.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/PasswordDesensitization.java


+ 1 - 1
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/package-info.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/desensitize/package-info.java

@@ -1,4 +1,4 @@
 /**
  * 脱敏组件:支持 JSON 返回数据时,将邮箱、手机等字段进行脱敏
  */
-package cn.iocoder.yudao.framework.desensitize.core;
+package cn.iocoder.yudao.framework.desensitize;

+ 0 - 0
yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/java/cn/iocoder/yudao/framework/errorcode/config/ErrorCodeProperties.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/errorcode/config/ErrorCodeProperties.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/java/cn/iocoder/yudao/framework/errorcode/config/YudaoErrorCodeAutoConfiguration.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/errorcode/config/YudaoErrorCodeAutoConfiguration.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/java/cn/iocoder/yudao/framework/errorcode/core/generator/ErrorCodeAutoGenerator.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/errorcode/core/generator/ErrorCodeAutoGenerator.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/java/cn/iocoder/yudao/framework/errorcode/core/generator/ErrorCodeAutoGeneratorImpl.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/errorcode/core/generator/ErrorCodeAutoGeneratorImpl.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/java/cn/iocoder/yudao/framework/errorcode/core/loader/ErrorCodeLoader.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/errorcode/core/loader/ErrorCodeLoader.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/java/cn/iocoder/yudao/framework/errorcode/core/loader/ErrorCodeLoaderImpl.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/errorcode/core/loader/ErrorCodeLoaderImpl.java


+ 0 - 0
yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/java/cn/iocoder/yudao/framework/errorcode/package-info.java → yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/errorcode/package-info.java


+ 2 - 0
yudao-framework/yudao-spring-boot-starter-web/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

@@ -3,3 +3,5 @@ cn.iocoder.yudao.framework.jackson.config.YudaoJacksonAutoConfiguration
 cn.iocoder.yudao.framework.swagger.config.YudaoSwaggerAutoConfiguration
 cn.iocoder.yudao.framework.web.config.YudaoWebAutoConfiguration
 cn.iocoder.yudao.framework.xss.config.YudaoXssAutoConfiguration
+cn.iocoder.yudao.framework.banner.config.YudaoBannerAutoConfiguration
+cn.iocoder.yudao.framework.errorcode.config.YudaoErrorCodeAutoConfiguration

+ 0 - 0
yudao-framework/yudao-spring-boot-starter-banner/src/main/resources/banner.txt → yudao-framework/yudao-spring-boot-starter-web/src/main/resources/banner.txt


+ 8 - 12
yudao-framework/yudao-spring-boot-starter-desensitize/src/test/java/cn/iocoder/yudao/framework/desensitize/core/DesensitizeTest.java → yudao-framework/yudao-spring-boot-starter-web/src/test/java/cn/iocoder/yudao/framework/desensitize/core/DesensitizeTest.java

@@ -1,27 +1,23 @@
 package cn.iocoder.yudao.framework.desensitize.core;
 
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
+import cn.iocoder.yudao.framework.desensitize.core.annotation.Address;
 import cn.iocoder.yudao.framework.desensitize.core.regex.annotation.EmailDesensitize;
 import cn.iocoder.yudao.framework.desensitize.core.regex.annotation.RegexDesensitize;
-import cn.iocoder.yudao.framework.desensitize.core.annotation.Address;
-import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.BankCardDesensitize;
-import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.CarLicenseDesensitize;
-import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.ChineseNameDesensitize;
-import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.FixedPhoneDesensitize;
-import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.IdCardDesensitize;
-import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.PasswordDesensitize;
-import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.MobileDesensitize;
-import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.SliderDesensitize;
-import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
+import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.*;
 import lombok.Data;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
 
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 
 /**
  * {@link DesensitizeTest} 的单元测试
  */
-public class DesensitizeTest extends BaseMockitoUnitTest {
+@ExtendWith(MockitoExtension.class)
+public class DesensitizeTest {
 
     @Test
     public void test() {

+ 1 - 1
yudao-framework/yudao-spring-boot-starter-desensitize/src/test/java/cn/iocoder/yudao/framework/desensitize/core/annotation/Address.java → yudao-framework/yudao-spring-boot-starter-web/src/test/java/cn/iocoder/yudao/framework/desensitize/core/annotation/Address.java

@@ -1,8 +1,8 @@
 package cn.iocoder.yudao.framework.desensitize.core.annotation;
 
 import cn.iocoder.yudao.framework.desensitize.core.DesensitizeTest;
-import cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;
 import cn.iocoder.yudao.framework.desensitize.core.handler.AddressHandler;
+import cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;
 import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
 
 import java.lang.annotation.Documented;

+ 0 - 0
yudao-framework/yudao-spring-boot-starter-desensitize/src/test/java/cn/iocoder/yudao/framework/desensitize/core/handler/AddressHandler.java → yudao-framework/yudao-spring-boot-starter-web/src/test/java/cn/iocoder/yudao/framework/desensitize/core/handler/AddressHandler.java


+ 0 - 1
yudao-module-crm/pom.xml

@@ -18,7 +18,6 @@
     <packaging>pom</packaging>
 
     <name>${project.artifactId}</name>
-
     <description>
         crm 包下,客户关系管理(Customer Relationship Management)。
         例如说:客户、联系人、商机、合同、回款等等

+ 6 - 0
yudao-module-crm/yudao-module-crm-biz/pom.xml

@@ -70,5 +70,11 @@
             <groupId>cn.iocoder.boot</groupId>
             <artifactId>yudao-spring-boot-starter-test</artifactId>
         </dependency>
+
+        <!-- TODO @puhui999:放的位置,要整齐哈。 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
+        </dependency>
     </dependencies>
 </project>

+ 1 - 1
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java

@@ -41,7 +41,7 @@ import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.E
 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CUSTOMER_NOT_EXISTS;
 
-@Tag(name = "管理后台 - 商机")
+@Tag(name = "管理后台 - CRM 商机")
 @RestController
 @RequestMapping("/crm/business")
 @Validated

+ 1 - 1
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessStatusTypeController.java

@@ -40,7 +40,7 @@ import java.util.Set;
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
 
-@Tag(name = "管理后台 - 商机状态类型")
+@Tag(name = "管理后台 - CRM 商机状态类型")
 @RestController
 @RequestMapping("/crm/business-status-type")
 @Validated

+ 1 - 1
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java

@@ -99,7 +99,7 @@ public class CrmClueController {
     @PostMapping("/transform")
     @Operation(summary = "线索转化为客户")
     @PreAuthorize("@ss.hasPermission('crm:clue:update')")
-    public CommonResult<Boolean> translateCustomer(@Valid @RequestBody CrmClueTransformReqVO reqVO) {
+    public CommonResult<Boolean> translateCustomer(@Valid @RequestBody CrmClueTranslateReqVO reqVO) {
         clueService.translateCustomer(reqVO, getLoginUserId());
         return success(Boolean.TRUE);
     }

+ 9 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmCluePageReqVO.java

@@ -30,4 +30,13 @@ public class CrmCluePageReqVO extends PageParam {
     @Schema(description = "是否为公海数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
     private Boolean pool; // null 则表示为不是公海数据
 
+    @Schema(description = "所属行业", example = "1")
+    private Integer industryId;
+
+    @Schema(description = "客户等级", example = "1")
+    private Integer level;
+
+    @Schema(description = "客户来源", example = "1")
+    private Integer source;
+
 }

+ 35 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueRespVO.java

@@ -77,4 +77,39 @@ public class CrmClueRespVO {
     @ExcelProperty("创建时间")
     private LocalDateTime createTime;
 
+    @Schema(description = "所属行业", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")
+    @ExcelProperty(value = "所属行业", converter = DictConvert.class)
+    @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY)
+    private Integer industryId;
+
+    @Schema(description = "客户等级", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")
+    @ExcelProperty(value = "客户等级", converter = DictConvert.class)
+    @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_LEVEL)
+    private Integer level;
+
+    @Schema(description = "客户来源", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")
+    @ExcelProperty(value = "客户来源", converter = DictConvert.class)
+    @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_SOURCE)
+    private Integer source;
+
+    @Schema(description = "网址", example = "25682")
+    @ExcelProperty("网址")
+    private String website;
+
+    @Schema(description = "QQ", example = "25682")
+    @ExcelProperty("QQ")
+    private String qq;
+
+    @Schema(description = "wechat", example = "25682")
+    @ExcelProperty("wechat")
+    private String wechat;
+
+    @Schema(description = "email", example = "25682")
+    @ExcelProperty("email")
+    private String email;
+
+    @Schema(description = "客户描述", example = "25682")
+    @ExcelProperty("客户描述")
+    private String description;
+
 }

+ 47 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueSaveReqVO.java

@@ -1,16 +1,25 @@
 package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
 
+import cn.iocoder.yudao.framework.common.validation.InEnum;
 import cn.iocoder.yudao.framework.common.validation.Mobile;
 import cn.iocoder.yudao.framework.common.validation.Telephone;
+import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
+import cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLevelEnum;
+import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerIndustryParseFunction;
+import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerLevelParseFunction;
+import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerSourceParseFunction;
 import com.mzt.logapi.starter.annotation.DiffLogField;
 import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.Email;
 import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.Size;
 import lombok.Data;
 import org.springframework.format.annotation.DateTimeFormat;
 
 import java.time.LocalDateTime;
 
 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY;
 
 @Schema(description = "管理后台 - CRM 线索 创建/更新 Request VO")
 @Data
@@ -55,4 +64,42 @@ public class CrmClueSaveReqVO {
     @DiffLogField(name = "备注")
     private String remark;
 
+    @Schema(description = "所属行业", example = "1")
+    @DiffLogField(name = "所属行业", function = CrmCustomerIndustryParseFunction.NAME)
+    @DictFormat(CRM_CUSTOMER_INDUSTRY)
+    private Integer industryId;
+
+    @Schema(description = "客户等级", example = "2")
+    @DiffLogField(name = "客户等级", function = CrmCustomerLevelParseFunction.NAME)
+    @InEnum(CrmCustomerLevelEnum.class)
+    private Integer level;
+
+    @Schema(description = "客户来源", example = "3")
+    @DiffLogField(name = "客户来源", function = CrmCustomerSourceParseFunction.NAME)
+    private Integer source;
+
+    @Schema(description = "网址", example = "https://www.baidu.com")
+    @DiffLogField(name = "网址")
+    private String website;
+
+    @Schema(description = "QQ", example = "123456789")
+    @DiffLogField(name = "QQ")
+    @Size(max = 20, message = "QQ长度不能超过 20 个字符")
+    private String qq;
+
+    @Schema(description = "微信", example = "123456789")
+    @DiffLogField(name = "微信")
+    @Size(max = 255, message = "微信长度不能超过 255 个字符")
+    private String wechat;
+
+    @Schema(description = "邮箱", example = "123456789@qq.com")
+    @DiffLogField(name = "邮箱")
+    @Email(message = "邮箱格式不正确")
+    @Size(max = 255, message = "邮箱长度不能超过 255 个字符")
+    private String email;
+
+    @Schema(description = "客户描述", example = "任意文字")
+    @DiffLogField(name = "客户描述")
+    @Size(max = 4096, message = "客户描述长度不能超过 4096 个字符")
+    private String description;
 }

+ 1 - 1
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueTransformReqVO.java → yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueTranslateReqVO.java

@@ -8,7 +8,7 @@ import java.util.Set;
 
 @Schema(description = "管理后台 - 线索转化为客户 Request VO")
 @Data
-public class CrmClueTransformReqVO {
+public class CrmClueTranslateReqVO {
 
     @Schema(description = "线索编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1024, 1025]")
     @NotEmpty(message = "线索编号不能为空")

+ 5 - 7
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/CrmOperateLogController.java

@@ -2,14 +2,14 @@ package cn.iocoder.yudao.module.crm.controller.admin.operatelog;
 
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo.CrmOperateLogPageReqVO;
+import cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo.CrmOperateLogV2RespVO;
 import cn.iocoder.yudao.module.crm.enums.LogRecordConstants;
 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
 import cn.iocoder.yudao.module.system.api.logger.OperateLogApi;
 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2PageReqDTO;
-import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO;
 import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.annotation.Resource;
 import jakarta.validation.Valid;
@@ -51,16 +51,14 @@ public class CrmOperateLogController {
         BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType(), CRM_RECEIVABLE_PLAN_TYPE);
     }
 
-    // TODO @puhui999:还是搞个 VO 出来哈
     @GetMapping("/page")
     @Operation(summary = "获得操作日志")
-    @Parameter(name = "id", description = "客户编号", required = true)
-    @PreAuthorize("@ss.hasPermission('crm:customer:query')")
-    public CommonResult<PageResult<OperateLogV2RespDTO>> getCustomerOperateLog(@Valid CrmOperateLogPageReqVO pageReqVO) {
+    @PreAuthorize("@ss.hasPermission('crm:operate-log:query')")
+    public CommonResult<PageResult<CrmOperateLogV2RespVO>> getCustomerOperateLog(@Valid CrmOperateLogPageReqVO pageReqVO) {
         OperateLogV2PageReqDTO reqDTO = new OperateLogV2PageReqDTO();
         reqDTO.setPageSize(PAGE_SIZE_NONE); // 默认不分页,需要分页需注释
         reqDTO.setBizType(BIZ_TYPE_MAP.get(pageReqVO.getBizType())).setBizId(pageReqVO.getBizId());
-        return success(operateLogApi.getOperateLogPage(reqDTO));
+        return success(BeanUtils.toBean(operateLogApi.getOperateLogPage(reqDTO), CrmOperateLogV2RespVO.class));
     }
 
 }

+ 44 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogV2RespVO.java

@@ -0,0 +1,44 @@
+package cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - CRM 跟进 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class CrmOperateLogV2RespVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")
+    private Long id;
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    private Long userId;
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
+    private String userName;
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private Integer userType;
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")
+    private String type;
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "修改客户")
+    private String subType;
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")
+    private Long bizId;
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "将什么从什么改为了什么")
+    private String action;
+
+    @Schema(description = "编号", example = "{orderId: 1}")
+    private String extra;
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-01-01")
+    private LocalDateTime createTime;
+
+}

+ 3 - 5
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/permission/CrmPermissionConvert.java

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.crm.convert.permission;
 import cn.hutool.core.collection.CollUtil;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionRespVO;
 import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
@@ -29,13 +30,10 @@ public interface CrmPermissionConvert {
 
     CrmPermissionConvert INSTANCE = Mappers.getMapper(CrmPermissionConvert.class);
 
-    // TODO @puhui999:这个要不也搞到 copy 里
-    List<CrmPermissionRespVO> convert(List<CrmPermissionDO> permission);
-
-    default List<CrmPermissionRespVO> convert(List<CrmPermissionDO> permission, List<AdminUserRespDTO> userList,
+    default List<CrmPermissionRespVO> convert(List<CrmPermissionDO> permissions, List<AdminUserRespDTO> userList,
                                               Map<Long, DeptRespDTO> deptMap, Map<Long, PostRespDTO> postMap) {
         Map<Long, AdminUserRespDTO> userMap = CollectionUtils.convertMap(userList, AdminUserRespDTO::getId);
-        return CollectionUtils.convertList(convert(permission), item -> {
+        return CollectionUtils.convertList(BeanUtils.toBean(permissions, CrmPermissionRespVO.class), item -> {
             findAndThen(userMap, item.getUserId(), user -> {
                 item.setNickname(user.getNickname());
                 findAndThen(deptMap, user.getDeptId(), deptRespDTO -> item.setDeptName(deptRespDTO.getName()));

+ 36 - 7
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/clue/CrmClueDO.java

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.dal.dataobject.clue;
 
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
+import cn.iocoder.yudao.module.crm.enums.DictTypeConstants;
 import com.baomidou.mybatisplus.annotation.KeySequence;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
@@ -72,16 +73,44 @@ public class CrmClueDO extends BaseDO {
      * 备注
      */
     private String remark;
-
     /**
      * 负责人的用户编号
-     *
      * 关联 AdminUserDO 的 id 字段
      */
     private Long ownerUserId;
-
-    // TODO 芋艿:客户级别;
-    // TODO 芋艿:线索来源;
-    // TODO 芋艿:客户行业;
-
+    /**
+     * 所属行业
+     * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_INDUSTRY}
+     */
+    private Integer industryId;
+    /**
+     * 客户等级
+     * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_LEVEL}
+     */
+    private Integer level;
+    /**
+     * 客户来源
+     * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_SOURCE}
+     */
+    private Integer source;
+    /**
+     * 网址
+     */
+    private String website;
+    /**
+     * QQ
+     */
+    private String qq;
+    /**
+     * wechat
+     */
+    private String wechat;
+    /**
+     * email
+     */
+    private String email;
+    /**
+     * 客户描述
+     */
+    private String description;
 }

+ 5 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerDO.java

@@ -114,10 +114,15 @@ public class CrmCustomerDO extends BaseDO {
      */
     private String detailAddress;
 
+    /**
+     * 最后接收时间
+     */
+    private LocalDateTime receiveTime;
     /**
      * 最后跟进时间
      */
     private LocalDateTime contactLastTime;
+
     /**
      * 最后跟进内容
      */

+ 3 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/clue/CrmClueMapper.java

@@ -37,6 +37,9 @@ public interface CrmClueMapper extends BaseMapperX<CrmClueDO> {
                 .likeIfPresent(CrmClueDO::getName, pageReqVO.getName())
                 .likeIfPresent(CrmClueDO::getTelephone, pageReqVO.getTelephone())
                 .likeIfPresent(CrmClueDO::getMobile, pageReqVO.getMobile())
+                .eqIfPresent(CrmClueDO::getIndustryId, pageReqVO.getIndustryId())
+                .eqIfPresent(CrmClueDO::getLevel, pageReqVO.getLevel())
+                .eqIfPresent(CrmClueDO::getSource, pageReqVO.getSource())
                 .orderByDesc(CrmClueDO::getId);
         return selectJoinPage(pageReqVO, CrmClueDO.class, query);
     }

+ 8 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java

@@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageR
 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
 import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import org.apache.ibatis.annotations.Mapper;
 import org.springframework.lang.Nullable;
@@ -99,4 +100,11 @@ public interface CrmCustomerMapper extends BaseMapperX<CrmCustomerDO> {
         return selectJoinPage(pageReqVO, CrmCustomerDO.class, query);
     }
 
+    default List<CrmCustomerDO> selectListByLockStatusAndOwnerUserIdNotNull(Boolean lockStatus) {
+        return selectList(new LambdaQueryWrapper<CrmCustomerDO>()
+                .eq(CrmCustomerDO::getLockStatus, lockStatus)
+                // TODO @puhui999:not null 可以转化成大于 0
+                .isNotNull(CrmCustomerDO::getOwnerUserId));
+    }
+
 }

+ 29 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/util/CrmPermissionUtils.java

@@ -1,9 +1,17 @@
 package cn.iocoder.yudao.module.crm.framework.permission.core.util;
 
+import cn.hutool.core.util.ObjUtil;
 import cn.hutool.extra.spring.SpringUtil;
+import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
+import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
+import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionRoleCodeEnum;
+import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
 import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
 
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.anyMatch;
 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
 
 /**
@@ -22,6 +30,22 @@ public class CrmPermissionUtils {
         return SingletonManager.getPermissionApi().hasAnyRoles(getLoginUserId(), CrmPermissionRoleCodeEnum.CRM_ADMIN.getCode());
     }
 
+    // TODO @puhui999:这个貌似直接放到 CrmPermissionService 会更好?
+    /**
+     * 校验权限
+     *
+     * @param bizType   数据类型,关联 {@link CrmBizTypeEnum}
+     * @param bizId     数据编号,关联 {@link CrmBizTypeEnum} 对应模块 DO#getId()
+     * @param userId    用户编号
+     * @param levelEnum 权限级别
+     * @return boolean
+     */
+    public static boolean hasPermission(Integer bizType, Long bizId, Long userId, CrmPermissionLevelEnum levelEnum) {
+        List<CrmPermissionDO> permissionList = SingletonManager.getCrmPermissionService().getPermissionListByBiz(bizType, bizId);
+        return anyMatch(permissionList, permission ->
+                ObjUtil.equal(permission.getUserId(), userId) && ObjUtil.equal(permission.getLevel(), levelEnum.getLevel()));
+    }
+
     /**
      * 静态内部类实现单例获取
      *
@@ -30,11 +54,16 @@ public class CrmPermissionUtils {
     private static class SingletonManager {
 
         private static final PermissionApi PERMISSION_API = SpringUtil.getBean(PermissionApi.class);
+        private static final CrmPermissionService CRM_PERMISSION_SERVICE = SpringUtil.getBean(CrmPermissionService.class);
 
         public static PermissionApi getPermissionApi() {
             return PERMISSION_API;
         }
 
+        public static CrmPermissionService getCrmPermissionService() {
+            return CRM_PERMISSION_SERVICE;
+        }
+
     }
 
 }

+ 27 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/job/customer/CrmCustomerAutoPutPoolJob.java

@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.crm.job.customer;
+
+import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
+import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
+import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Component;
+
+/**
+ * 客户自动掉入公海 Job
+ *
+ * @author 芋道源码
+ */
+@Component
+public class CrmCustomerAutoPutPoolJob implements JobHandler {
+
+    @Resource
+    private CrmCustomerService customerService;
+
+    @Override
+    @TenantJob
+    public String execute(String param) {
+        int count = customerService.customerAutoPutPoolBySystem();
+        return String.format("掉入公海客户 %s 个", count);
+    }
+
+}

+ 4 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/job/package-info.java

@@ -0,0 +1,4 @@
+/**
+ * TODO 芋艿:临时占位,后续可删除
+ */
+package cn.iocoder.yudao.module.crm.job;

+ 2 - 2
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueService.java

@@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO;
 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueSaveReqVO;
 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransferReqVO;
-import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransformReqVO;
+import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTranslateReqVO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;
 import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
 import jakarta.validation.Valid;
@@ -88,6 +88,6 @@ public interface CrmClueService {
      * @param reqVO  线索编号
      * @param userId 用户编号
      */
-    void translateCustomer(CrmClueTransformReqVO reqVO, Long userId);
+    void translateCustomer(CrmClueTranslateReqVO reqVO, Long userId);
 
 }

+ 24 - 29
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java

@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.crm.service.clue;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.collection.ListUtil;
 import cn.hutool.core.lang.Assert;
-import cn.hutool.core.util.ObjUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.extra.spring.SpringUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
@@ -11,11 +10,10 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO;
 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueSaveReqVO;
 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransferReqVO;
-import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransformReqVO;
+import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTranslateReqVO;
 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerSaveReqVO;
 import cn.iocoder.yudao.module.crm.convert.clue.CrmClueConvert;
 import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;
-import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;
 import cn.iocoder.yudao.module.crm.dal.mysql.clue.CrmClueMapper;
 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
@@ -177,7 +175,7 @@ public class CrmClueServiceImpl implements CrmClueService {
     @Override
     @Transactional(rollbackFor = Exception.class)
     @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
-    public void translateCustomer(CrmClueTransformReqVO reqVO, Long userId) {
+    public void translateCustomer(CrmClueTranslateReqVO reqVO, Long userId) {
         // 1.1 校验线索都存在
         Set<Long> clueIds = reqVO.getIds();
         List<CrmClueDO> clues = getClueList(clueIds, userId);
@@ -192,47 +190,44 @@ public class CrmClueServiceImpl implements CrmClueService {
             throw exception(CLUE_ANY_CLUE_ALREADY_TRANSLATED, convertSet(translatedClues, CrmClueDO::getId));
         }
 
-        // 2. 遍历线索(未转化的线索),创建对应的客户
-        // TODO @puhui999:这里不用过滤了;
-        List<CrmClueDO> translateClues = filterList(clues, clue -> ObjUtil.equal(Boolean.FALSE, clue.getTransformStatus()));
-        List<CrmCustomerDO> customers = customerService.createCustomerBatch(convertList(translateClues, clue ->
-                BeanUtils.toBean(clue, CrmCustomerCreateReqBO.class)), userId);
-
-        // TODO @puhui999:这里不用搞一个 clueCustomerIdMap 出来;可以考虑逐个创建,然后把 customerId 设置回 CrmClueDO;避免 name 匹配,极端会有问题哈;
-        // TODO 是不是就直接 foreach 处理好了;因为本身量不大,for 处理性能 ok,可阅读性好
-        Map<Long, Long> clueCustomerIdMap = new HashMap<>(translateClues.size());
-        // 2.1 更新线索
-        clueMapper.updateBatch(convertList(customers, customer -> {
-            CrmClueDO firstClue = findFirst(translateClues, clue -> ObjUtil.equal(clue.getName(), customer.getName()));
-            clueCustomerIdMap.put(firstClue.getId(), customer.getId());
-            return new CrmClueDO().setId(firstClue.getId()).setTransformStatus(Boolean.TRUE).setCustomerId(customer.getId());
-        }));
-        // 2.3 复制跟进
-        updateFollowUpRecords(clueCustomerIdMap);
+        // 2.1 遍历线索(未转化的线索),创建对应的客户
+        clues.forEach(clue -> {
+            Long customerId = customerService.createCustomer(BeanUtils.toBean(clue, CrmCustomerCreateReqBO.class), userId);
+            clue.setCustomerId(customerId);
+        });
+        // 2.2 更新线索
+        clueMapper.updateBatch(convertList(clues, clue -> new CrmClueDO().setId(clue.getId())
+                .setTransformStatus(Boolean.TRUE).setCustomerId(clue.getCustomerId())));
+        // 2.3 复制跟进记录
+        copyFollowUpRecords(clues);
 
         // 3. 记录操作日志
-        for (CrmClueDO clue : translateClues) {
-            // TODO @puhui999:这里优化下,translate 操作日志
-            getSelf().receiveClueLog(clue);
+        for (CrmClueDO clue : clues) {
+            getSelf().translateCustomerLog(clue);
         }
     }
 
-    private void updateFollowUpRecords(Map<Long, Long> clueCustomerIdMap) {
+    /**
+     * 线索被转换客户后,需要将线索的跟进记录,复制到客户上
+     *
+     * @param clues 被转化的线索
+     */
+    private void copyFollowUpRecords(List<CrmClueDO> clues) {
         List<CrmFollowUpRecordDO> followUpRecords = followUpRecordService.getFollowUpRecordByBiz(
-                CrmBizTypeEnum.CRM_LEADS.getType(), clueCustomerIdMap.keySet());
+                CrmBizTypeEnum.CRM_LEADS.getType(), convertSet(clues, CrmClueDO::getId));
         if (CollUtil.isEmpty(followUpRecords)) {
             return;
         }
-
         // 创建跟进
+        Map<Long, CrmClueDO> clueMap = convertMap(clues, CrmClueDO::getId);
         followUpRecordService.createFollowUpRecordBatch(convertList(followUpRecords, followUpRecord ->
                 BeanUtils.toBean(followUpRecord, CrmFollowUpCreateReqBO.class).setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())
-                        .setBizId(clueCustomerIdMap.get(followUpRecord.getBizId()))));
+                        .setBizId(clueMap.get(followUpRecord.getBizId()).getCustomerId())));
     }
 
     @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_TRANSLATE_SUB_TYPE, bizNo = "{{#clue.id}}",
             success = CRM_LEADS_TRANSLATE_SUCCESS)
-    public void receiveClueLog(CrmClueDO clue) {
+    public void translateCustomerLog(CrmClueDO clue) {
         // 记录操作日志上下文
         LogRecordContext.putVariable("clue", clue);
     }

+ 8 - 2
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactBusinessServiceImpl.java

@@ -6,6 +6,9 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
 import cn.iocoder.yudao.module.crm.dal.mysql.contactbusinesslink.CrmContactBusinessMapper;
+import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
+import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
+import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
 import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
 import jakarta.annotation.Resource;
 import org.springframework.context.annotation.Lazy;
@@ -19,7 +22,6 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_NOT_EXISTS;
 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CONTACT_NOT_EXISTS;
 
-// TODO @puhui999:数据权限的校验;每个操作;
 /**
  * 联系人与商机的关联 Service 实现类
  *
@@ -40,6 +42,7 @@ public class CrmContactBusinessServiceImpl implements CrmContactBusinessService
     private CrmContactService contactService;
 
     @Override
+    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#createReqVO.contactId", level = CrmPermissionLevelEnum.WRITE)
     public void createContactBusinessList(CrmContactBusinessReqVO createReqVO) {
         CrmContactDO contact = contactService.getContact(createReqVO.getContactId());
         if (contact == null) {
@@ -65,6 +68,7 @@ public class CrmContactBusinessServiceImpl implements CrmContactBusinessService
     }
 
     @Override
+    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#deleteReqVO.contactId", level = CrmPermissionLevelEnum.WRITE)
     public void deleteContactBusinessList(CrmContactBusinessReqVO deleteReqVO) {
         CrmContactDO contact = contactService.getContact(deleteReqVO.getContactId());
         if (contact == null) {
@@ -76,11 +80,13 @@ public class CrmContactBusinessServiceImpl implements CrmContactBusinessService
     }
 
     @Override
+    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#contactId", level = CrmPermissionLevelEnum.WRITE)
     public void deleteContactBusinessByContactId(Long contactId) {
-        contactBusinessMapper.delete(CrmContactBusinessDO::getContactId,contactId);
+        contactBusinessMapper.delete(CrmContactBusinessDO::getContactId, contactId);
     }
 
     @Override
+    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#contactId", level = CrmPermissionLevelEnum.READ)
     public List<CrmContactBusinessDO> getContactBusinessListByContactId(Long contactId) {
         return contactBusinessMapper.selectListByContactId(contactId);
     }

+ 12 - 4
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java

@@ -100,13 +100,13 @@ public interface CrmCustomerService {
     void updateCustomerFollowUp(CrmUpdateFollowUpReqBO customerUpdateFollowUpReqBO);
 
     /**
-     * 批量创建客户
+     * 创建客户
      *
-     * @param customerCreateReqBOs 请求
-     * @param userId               用户编号
+     * @param customerCreateReq 请求信息
+     * @param userId            用户编号
      * @return 客户列表
      */
-    List<CrmCustomerDO> createCustomerBatch(List<CrmCustomerCreateReqBO> customerCreateReqBOs, Long userId);
+    Long createCustomer(CrmCustomerCreateReqBO customerCreateReq, Long userId);
 
     // ==================== 公海相关操作 ====================
 
@@ -126,4 +126,12 @@ public interface CrmCustomerService {
      */
     void receiveCustomer(List<Long> ids, Long ownerUserId, Boolean isReceive);
 
+    // TODO @puhui999:autoPutCustomerPool,注释说明是系统就好哈;
+    /**
+     * 【系统】客户自动掉入公海
+     *
+     * @return 掉入公海数量
+     */
+    int customerAutoPutPoolBySystem();
+
 }

+ 71 - 30
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java

@@ -2,9 +2,11 @@ package cn.iocoder.yudao.module.crm.service.customer;
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.lang.Assert;
+import cn.hutool.core.util.ObjUtil;
 import cn.hutool.extra.spring.SpringUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerLockReqVO;
 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO;
@@ -13,6 +15,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerTrans
 import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert;
 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO;
+import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;
 import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper;
 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
@@ -31,6 +34,7 @@ import com.mzt.logapi.context.LogRecordContext;
 import com.mzt.logapi.service.impl.DiffParseFunction;
 import com.mzt.logapi.starter.annotation.LogRecord;
 import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -43,12 +47,10 @@ import java.util.Collections;
 import java.util.List;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
 import static cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum.CUSTOMER_LOCK_LIMIT;
 import static cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum.CUSTOMER_OWNER_LIMIT;
-import static java.util.Collections.emptyList;
 import static java.util.Collections.singletonList;
 
 /**
@@ -57,6 +59,7 @@ import static java.util.Collections.singletonList;
  * @author Wanwan
  */
 @Service
+@Slf4j
 @Validated
 public class CrmCustomerServiceImpl implements CrmCustomerService {
 
@@ -69,6 +72,9 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
     private CrmCustomerLimitConfigService customerLimitConfigService;
     @Resource
     @Lazy
+    private CrmCustomerPoolConfigService customerPoolConfigService;
+    @Resource
+    @Lazy
     private CrmContactService contactService;
     @Resource
     @Lazy
@@ -91,9 +97,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
 
         // 2. 插入客户
         CrmCustomerDO customer = BeanUtils.toBean(createReqVO, CrmCustomerDO.class)
-                .setLockStatus(false).setDealStatus(false)
-                .setContactLastTime(LocalDateTime.now());
-        // TODO @puhui999:可能要加个 receiveTime 字段,记录最后接收时间
+                .setLockStatus(false).setDealStatus(false).setContactLastTime(LocalDateTime.now());
         customerMapper.insert(customer);
 
         // 3. 创建数据权限
@@ -214,24 +218,24 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public List<CrmCustomerDO> createCustomerBatch(List<CrmCustomerCreateReqBO> customerCreateReqBOs, Long userId) {
-        if (CollUtil.isEmpty(customerCreateReqBOs)) {
-            return emptyList();
-        }
+    @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_CREATE_SUB_TYPE, bizNo = "{{#customer.id}}",
+            success = CRM_CUSTOMER_CREATE_SUCCESS)
+    public Long createCustomer(CrmCustomerCreateReqBO customerCreateReq, Long userId) {
+        // 1. 插入客户
+        CrmCustomerDO customer = BeanUtils.toBean(customerCreateReq, CrmCustomerDO.class).setOwnerUserId(userId)
+                .setLockStatus(false).setDealStatus(false).setReceiveTime(LocalDateTime.now());
+        customerMapper.insert(customer);
 
-        // 创建客户
-        List<CrmCustomerDO> customers = convertList(customerCreateReqBOs, customerBO ->
-                BeanUtils.toBean(customerBO, CrmCustomerDO.class).setOwnerUserId(userId));
-        customerMapper.insertBatch(customers);
+        // 2. 创建数据权限
+        permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())
+                .setBizId(customer.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人
 
-        // 创建负责人数据权限
-        permissionService.createPermissionBatch(convertList(customers, customer -> new CrmPermissionCreateReqBO()
-                .setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()).setBizId(customer.getId()).setUserId(userId)
-                .setLevel(CrmPermissionLevelEnum.OWNER.getLevel())));
-        return customers;
+        // 3. 记录操作日志上下文
+        LogRecordContext.putVariable("customer", customer);
+        return customer.getId();
     }
 
-    // ==================== 公海相关操作 ====================
+// ==================== 公海相关操作 ====================
 
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -249,17 +253,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
         // 1.3. 校验客户是否锁定
         validateCustomerIsLocked(customer, true);
 
-        // 2.1 设置负责人为 NULL
-        int updateOwnerUserIncr = customerMapper.updateOwnerUserIdById(customer.getId(), null);
-        if (updateOwnerUserIncr == 0) {
-            throw exception(CUSTOMER_UPDATE_OWNER_USER_FAIL);
-        }
-        // 2.2 删除负责人数据权限
-        permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), customer.getId(),
-                CrmPermissionLevelEnum.OWNER.getLevel());
-
-        // 3. 联系人的负责人,也要设置为 null。因为:因为领取后,负责人也要关联过来,这块和 receiveCustomer 是对应的
-        contactService.updateOwnerUserIdByCustomerId(customer.getId(), null);
+        // 2. 客户放入公海
+        putCustomerPool(customer);
 
         // 记录操作日志上下文
         LogRecordContext.putVariable("customerName", customer.getName());
@@ -317,6 +312,52 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
         }
     }
 
+    @Override
+    public int customerAutoPutPoolBySystem() {
+        CrmCustomerPoolConfigDO poolConfig = customerPoolConfigService.getCustomerPoolConfig();
+        if (poolConfig == null || !poolConfig.getEnabled()) {
+            return 0;
+        }
+        // 1. 获取没有锁定的不在公海的客户
+        List<CrmCustomerDO> customerList = customerMapper.selectListByLockStatusAndOwnerUserIdNotNull(Boolean.FALSE);
+        List<CrmCustomerDO> poolCustomerList = CollectionUtils.filterList(customerList, customer -> {
+            // TODO @puhui999:建议这里作为一个查询条件哈,不放内存里过滤;
+            // 1.1 未成交放入公海
+            if (!customer.getDealStatus()) {
+                return (poolConfig.getDealExpireDays() - LocalDateTimeUtils.between(customer.getCreateTime())) <= 0;
+            }
+            // 1.2 未跟进放入公海
+            LocalDateTime lastTime = ObjUtil.defaultIfNull(customer.getContactLastTime(), customer.getCreateTime());
+            return (poolConfig.getContactExpireDays() - LocalDateTimeUtils.between(lastTime)) <= 0;
+        });
+
+        // 2. 逐个放入公海
+        int count = 0;
+        for (CrmCustomerDO customer : poolCustomerList) {
+            try {
+                getSelf().putCustomerPool(customer);
+                count++;
+            } catch (Throwable e) {
+                log.error("[customerAutoPutPoolBySystem][Customer 客户({}) 放入公海异常]", customer.getId(), e);
+            }
+        }
+        return count;
+    }
+
+    private void putCustomerPool(CrmCustomerDO customer) {
+        // 1. 设置负责人为 NULL
+        int updateOwnerUserIncr = customerMapper.updateOwnerUserIdById(customer.getId(), null);
+        if (updateOwnerUserIncr == 0) {
+            throw exception(CUSTOMER_UPDATE_OWNER_USER_FAIL);
+        }
+        // 2. 删除负责人数据权限
+        permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), customer.getId(),
+                CrmPermissionLevelEnum.OWNER.getLevel());
+
+        // 3. 联系人的负责人,也要设置为 null。因为:因为领取后,负责人也要关联过来,这块和 receiveCustomer 是对应的
+        contactService.updateOwnerUserIdByCustomerId(customer.getId(), null);
+    }
+
     @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_RECEIVE_SUB_TYPE, bizNo = "{{#customer.id}}",
             success = CRM_CUSTOMER_RECEIVE_SUCCESS)
     public void receiveCustomerLog(CrmCustomerDO customer, String ownerUserName) {

+ 2 - 2
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordService.java

@@ -28,9 +28,9 @@ public interface CrmFollowUpRecordService {
     /**
      * 创建更进
      *
-     * @param followUpCreateReqBOs 请求
+     * @param list 请求
      */
-    void createFollowUpRecordBatch(List<CrmFollowUpCreateReqBO> followUpCreateReqBOs);
+    void createFollowUpRecordBatch(List<CrmFollowUpCreateReqBO> list);
 
     /**
      * 删除跟进记录 (数据权限基于 bizType、 bizId)

+ 11 - 18
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordServiceImpl.java

@@ -7,7 +7,6 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordPageReqVO;
 import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordSaveReqVO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;
-import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
 import cn.iocoder.yudao.module.crm.dal.mysql.followup.CrmFollowUpRecordMapper;
 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
@@ -31,10 +30,10 @@ import java.util.Collections;
 import java.util.List;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.anyMatch;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.FOLLOW_UP_RECORD_DELETE_DENIED;
 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.FOLLOW_UP_RECORD_NOT_EXISTS;
+import static cn.iocoder.yudao.module.crm.framework.permission.core.util.CrmPermissionUtils.hasPermission;
 
 /**
  * 跟进记录 Service 实现类
@@ -94,26 +93,25 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService {
             customerService.updateCustomerFollowUp(updateFollowUpReqBO);
         }
 
-        // TODO @puhui999:这两个,不更新 contactLastTime、contactLastContent,只更新 nextTime
-        // 3.1 更新 contactIds 对应的记录
+        // 3.1 更新 contactIds 对应的记录,不更新 lastTime 和 lastContent
         if (CollUtil.isNotEmpty(createReqVO.getContactIds())) {
-            contactService.updateContactFollowUpBatch(convertList(createReqVO.getContactIds(), updateFollowUpReqBO::setBizId));
+            contactService.updateContactFollowUpBatch(convertList(createReqVO.getContactIds(),
+                    contactId -> updateFollowUpReqBO.setBizId(contactId).setContactLastTime(null).setContactLastContent(null)));
         }
-        // 3.2 需要更新 businessIds、contactIds 对应的记录
+        // 3.2 需要更新 businessIds 对应的记录,不更新 lastTime 和 lastContent
         if (CollUtil.isNotEmpty(createReqVO.getBusinessIds())) {
-            businessService.updateBusinessFollowUpBatch(convertList(createReqVO.getBusinessIds(), updateFollowUpReqBO::setBizId));
+            businessService.updateBusinessFollowUpBatch(convertList(createReqVO.getBusinessIds(),
+                    businessId -> updateFollowUpReqBO.setBizId(businessId).setContactLastTime(null).setContactLastContent(null)));
         }
         return followUpRecord.getId();
     }
 
     @Override
-    public void createFollowUpRecordBatch(List<CrmFollowUpCreateReqBO> followUpCreateReqBOs) {
-        if (CollUtil.isEmpty(followUpCreateReqBOs)) {
+    public void createFollowUpRecordBatch(List<CrmFollowUpCreateReqBO> list) {
+        if (CollUtil.isEmpty(list)) {
             return;
         }
-
-        List<CrmFollowUpRecordDO> followUpRecords = BeanUtils.toBean(followUpCreateReqBOs, CrmFollowUpRecordDO.class);
-        crmFollowUpRecordMapper.insertBatch(followUpRecords);
+        crmFollowUpRecordMapper.insertBatch(BeanUtils.toBean(list, CrmFollowUpRecordDO.class));
     }
 
     @Override
@@ -121,12 +119,7 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService {
         // 校验存在
         CrmFollowUpRecordDO followUpRecord = validateFollowUpRecordExists(id);
         // 校验权限
-        // TODO @puhui999:是不是封装一个 hasPermission,更简介一点;
-        List<CrmPermissionDO> permissionList = permissionService.getPermissionListByBiz(
-                followUpRecord.getBizType(), followUpRecord.getBizId());
-        boolean hasPermission = anyMatch(permissionList, permission ->
-                ObjUtil.equal(permission.getUserId(), userId) && ObjUtil.equal(permission.getLevel(), CrmPermissionLevelEnum.OWNER.getLevel()));
-        if (!hasPermission) {
+        if (!hasPermission(followUpRecord.getBizType(), followUpRecord.getBizId(), userId, CrmPermissionLevelEnum.OWNER)) {
             throw exception(FOLLOW_UP_RECORD_DELETE_DENIED);
         }
 

+ 0 - 2
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/bo/CrmUpdateFollowUpReqBO.java

@@ -21,7 +21,6 @@ public class CrmUpdateFollowUpReqBO {
 
     @Schema(description = "最后跟进时间")
     @DiffLogField(name = "最后跟进时间")
-    @NotNull(message = "最后跟进时间不能为空")
     private LocalDateTime contactLastTime;
 
     @Schema(description = "下次联系时间")
@@ -30,7 +29,6 @@ public class CrmUpdateFollowUpReqBO {
 
     @Schema(description = "最后更进内容")
     @DiffLogField(name = "最后更进内容")
-    @NotNull(message = "最后更进内容不能为空")
     private String contactLastContent;
 
 }

+ 24 - 0
yudao-module-erp/pom.xml

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>cn.iocoder.boot</groupId>
+        <artifactId>yudao</artifactId>
+        <version>${revision}</version>
+    </parent>
+    <modules>
+        <module>yudao-module-erp-api</module>
+        <module>yudao-module-erp-biz</module>
+    </modules>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>yudao-module-erp</artifactId>
+    <packaging>pom</packaging>
+
+    <name>${project.artifactId}</name>
+    <description>
+        erp 包下,企业资源管理(Enterprise Resource Planning)。
+        例如说:采购、销售、库存、财务、产品等等
+    </description>
+
+</project>

+ 9 - 6
yudao-framework/yudao-spring-boot-starter-banner/pom.xml → yudao-module-erp/yudao-module-erp-api/pom.xml

@@ -3,17 +3,18 @@
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
-        <artifactId>yudao-framework</artifactId>
         <groupId>cn.iocoder.boot</groupId>
+        <artifactId>yudao-module-erp</artifactId>
         <version>${revision}</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
-    <artifactId>yudao-spring-boot-starter-banner</artifactId>
+    <artifactId>yudao-module-erp-api</artifactId>
     <packaging>jar</packaging>
 
     <name>${project.artifactId}</name>
-    <description>Banner 用于在 console 控制台,打印开发文档、接口文档等</description>
-    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
+    <description>
+        erp 模块 API,暴露给其它模块调用
+    </description>
 
     <dependencies>
         <dependency>
@@ -21,10 +22,12 @@
             <artifactId>yudao-common</artifactId>
         </dependency>
 
+        <!-- 参数校验 -->
         <dependency>
             <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter</artifactId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+            <optional>true</optional>
         </dependency>
     </dependencies>
 
-</project>
+</project>

+ 4 - 0
yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/package-info.java

@@ -0,0 +1,4 @@
+/**
+ * erp API 包,定义暴露给其它模块的 API
+ */
+package cn.iocoder.yudao.module.erp.api;

+ 10 - 0
yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/DictTypeConstants.java

@@ -0,0 +1,10 @@
+package cn.iocoder.yudao.module.erp.enums;
+
+/**
+ * ERP 字典类型的枚举类
+ *
+ * @author 芋道源码
+ */
+public interface DictTypeConstants {
+
+}

+ 15 - 0
yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java

@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.module.erp.enums;
+
+import cn.iocoder.yudao.framework.common.exception.ErrorCode;
+
+/**
+ * ERP 错误码枚举类
+ * <p>
+ * erp 系统,使用 1-030-000-000 段
+ */
+public interface ErrorCodeConstants {
+
+    // ========== 销售订单(1-030-000-000) ==========
+    ErrorCode SALE_ORDER_NOT_EXISTS = new ErrorCode(1_030_000_000, "销售订单不存在");
+
+}

+ 12 - 0
yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/LogRecordConstants.java

@@ -0,0 +1,12 @@
+package cn.iocoder.yudao.module.erp.enums;
+
+/**
+ * ERP 操作日志枚举
+ * 目的:统一管理,也减少 Service 里各种“复杂”字符串
+ *
+ * @author 芋道源码
+ */
+public interface LogRecordConstants {
+
+
+}

+ 40 - 0
yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/sale/ErpSaleOrderStatusEnum.java

@@ -0,0 +1,40 @@
+package cn.iocoder.yudao.module.erp.enums.sale;
+
+import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * ERP 销售订单的状态枚举
+ *
+ * @author 芋道源码
+ */
+@AllArgsConstructor
+@Getter
+public enum ErpSaleOrderStatusEnum implements IntArrayValuable {
+
+    AUDIT_NONE(0, "未审核"),
+    AUDIT_PASS(10, "已审核"),
+    SALE_PART(20, "部分销售"),
+    SALE_ALL(21, "完成销售"),
+    ;
+
+    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ErpSaleOrderStatusEnum::getStatus).toArray();
+
+    /**
+     * 状态
+     */
+    private final Integer status;
+    /**
+     * 状态名
+     */
+    private final String name;
+
+    @Override
+    public int[] array() {
+        return ARRAYS;
+    }
+
+}

+ 72 - 0
yudao-module-erp/yudao-module-erp-biz/pom.xml

@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>cn.iocoder.boot</groupId>
+        <artifactId>yudao-module-erp</artifactId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>yudao-module-erp-biz</artifactId>
+
+    <name>${project.artifactId}</name>
+    <description>
+        erp 包下,企业资源管理(Enterprise Resource Planning)。
+        例如说:采购、销售、库存、财务、产品等等
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-module-system-api</artifactId>
+            <version>${revision}</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-module-erp-api</artifactId>
+            <version>${revision}</version>
+        </dependency>
+
+        <!-- 业务组件 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-biz-operatelog</artifactId>
+        </dependency>
+
+        <!-- Web 相关 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-web</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-security</artifactId>
+        </dependency>
+
+        <!-- DB 相关 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-mybatis</artifactId>
+        </dependency>
+
+        <!-- 工具类相关 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-excel</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-biz-dict</artifactId>
+        </dependency>
+
+        <!-- Test 测试相关 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-test</artifactId>
+        </dependency>
+
+    </dependencies>
+
+</project>

+ 4 - 0
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.http

@@ -0,0 +1,4 @@
+### 请求 /transfer
+GET {{baseUrl}}/erp/sale-order/demo
+Authorization: Bearer {{token}}
+tenant-id: {{adminTenentId}}

+ 100 - 0
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java

@@ -0,0 +1,100 @@
+package cn.iocoder.yudao.module.erp.controller.admin.sale;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
+import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderPageReqVO;
+import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderRespVO;
+import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderSaveReqVO;
+import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO;
+import cn.iocoder.yudao.module.erp.service.sale.ErpSaleOrderService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.io.IOException;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
+
+
+@Tag(name = "管理后台 - ERP 销售订单")
+@RestController
+@RequestMapping("/erp/sale-order")
+@Validated
+public class ErpSaleOrderController {
+
+    @Resource
+    private ErpSaleOrderService saleOrderService;
+
+    // TODO 芋艿:待 review
+    @PostMapping("/create")
+    @Operation(summary = "创建ERP 销售订单")
+    @PreAuthorize("@ss.hasPermission('erp:sale-order:create')")
+    public CommonResult<Long> createSaleOrder(@Valid @RequestBody ErpSaleOrderSaveReqVO createReqVO) {
+        return success(saleOrderService.createSaleOrder(createReqVO));
+    }
+
+    // TODO 芋艿:待 review
+    @PutMapping("/update")
+    @Operation(summary = "更新ERP 销售订单")
+    @PreAuthorize("@ss.hasPermission('erp:sale-order:update')")
+    public CommonResult<Boolean> updateSaleOrder(@Valid @RequestBody ErpSaleOrderSaveReqVO updateReqVO) {
+        saleOrderService.updateSaleOrder(updateReqVO);
+        return success(true);
+    }
+
+    // TODO 芋艿:待 review
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除ERP 销售订单")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('erp:sale-order:delete')")
+    public CommonResult<Boolean> deleteSaleOrder(@RequestParam("id") Long id) {
+        saleOrderService.deleteSaleOrder(id);
+        return success(true);
+    }
+
+    // TODO 芋艿:待 review
+    @GetMapping("/get")
+    @Operation(summary = "获得ERP 销售订单")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('erp:sale-order:query')")
+    public CommonResult<ErpSaleOrderRespVO> getSaleOrder(@RequestParam("id") Long id) {
+        ErpSaleOrderDO saleOrder = saleOrderService.getSaleOrder(id);
+        return success(BeanUtils.toBean(saleOrder, ErpSaleOrderRespVO.class));
+    }
+
+    // TODO 芋艿:待 review
+    @GetMapping("/page")
+    @Operation(summary = "获得ERP 销售订单分页")
+    @PreAuthorize("@ss.hasPermission('erp:sale-order:query')")
+    public CommonResult<PageResult<ErpSaleOrderRespVO>> getSaleOrderPage(@Valid ErpSaleOrderPageReqVO pageReqVO) {
+        PageResult<ErpSaleOrderDO> pageResult = saleOrderService.getSaleOrderPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, ErpSaleOrderRespVO.class));
+    }
+
+    // TODO 芋艿:待 review
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出ERP 销售订单 Excel")
+    @PreAuthorize("@ss.hasPermission('erp:sale-order:export')")
+    @OperateLog(type = EXPORT)
+    public void exportSaleOrderExcel(@Valid ErpSaleOrderPageReqVO pageReqVO,
+              HttpServletResponse response) throws IOException {
+        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        List<ErpSaleOrderDO> list = saleOrderService.getSaleOrderPage(pageReqVO).getList();
+        // 导出 Excel
+        ExcelUtils.write(response, "ERP 销售订单.xls", "数据", ErpSaleOrderRespVO.class,
+                        BeanUtils.toBean(list, ErpSaleOrderRespVO.class));
+    }
+
+}

+ 39 - 0
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderPageReqVO.java

@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - ERP 销售订单分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ErpSaleOrderPageReqVO extends PageParam {
+
+    @Schema(description = "销售单编号", example = "XS001")
+    private String no;
+
+    @Schema(description = "客户编号", example = "1724")
+    private Long customerId;
+
+    @Schema(description = "下单时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] orderTime;
+
+    @Schema(description = "备注", example = "你猜")
+    private String description;
+
+    @Schema(description = "销售状态", example = "2")
+    private Integer status;
+
+    @Schema(description = "创建者")
+    private String creator;
+
+}

+ 78 - 0
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java

@@ -0,0 +1,78 @@
+package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+// TODO 芋艿:导出最后搞
+@Schema(description = "管理后台 - ERP 销售订单 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class ErpSaleOrderRespVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17386")
+    @ExcelProperty("编号")
+    private Long id;
+
+    @Schema(description = "销售单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "XS001")
+    @ExcelProperty("销售单编号")
+    private String no;
+
+    @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1724")
+    @ExcelProperty("客户编号")
+    private Long customerId;
+
+    @Schema(description = "下单时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    @ExcelProperty("下单时间")
+    private LocalDateTime orderTime;
+
+    // TODO 芋艿:example 后面
+    @Schema(description = "销售员编号数组")
+    @ExcelProperty("销售员编号数组")
+    private String salePersonIds;
+
+    @Schema(description = "合计价格,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "26094")
+    @ExcelProperty("合计价格,单位:元")
+    private BigDecimal totalPrice;
+
+    @Schema(description = "优惠率,百分比", requiredMode = Schema.RequiredMode.REQUIRED, example = "99.88")
+    @ExcelProperty("优惠率,百分比")
+    private BigDecimal discountPercent;
+
+    @Schema(description = "优惠金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "44.52")
+    @ExcelProperty("优惠金额,单位:元")
+    private BigDecimal discountPrice;
+
+    @Schema(description = "支付金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "322.40")
+    @ExcelProperty("支付金额,单位:元")
+    private BigDecimal payPrice;
+
+    @Schema(description = "定金金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "71.27")
+    @ExcelProperty("定金金额,单位:元")
+    private BigDecimal depositPrice;
+
+    @Schema(description = "附件地址", example = "https://www.iocoder.cn")
+    @ExcelProperty("附件地址")
+    private String fileUrl;
+
+    @Schema(description = "结算账户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "311.89")
+    @ExcelProperty("结算账户编号")
+    private Long accountId;
+
+    @Schema(description = "备注", example = "你猜")
+    @ExcelProperty("备注")
+    private String description;
+
+    @Schema(description = "销售状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
+    @ExcelProperty("销售状态")
+    private Integer status;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    @ExcelProperty("创建时间")
+    private LocalDateTime createTime;
+
+}

+ 74 - 0
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderSaveReqVO.java

@@ -0,0 +1,74 @@
+package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order;
+
+import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSalesOrderItemDO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Schema(description = "管理后台 - ERP 销售订单新增/修改 Request VO")
+@Data
+public class ErpSaleOrderSaveReqVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17386")
+    private Long id;
+
+    @Schema(description = "销售单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "XS001")
+    @NotEmpty(message = "销售单编号不能为空")
+    private String no;
+
+    @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1724")
+    @NotNull(message = "客户编号不能为空")
+    private Long customerId;
+
+    @Schema(description = "下单时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotNull(message = "下单时间不能为空")
+    private LocalDateTime orderTime;
+
+    @Schema(description = "销售员编号数组")
+    private String salePersonIds;
+
+    @Schema(description = "合计价格,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "26094")
+    @NotNull(message = "合计价格,单位:元不能为空")
+    private BigDecimal totalPrice;
+
+    @Schema(description = "优惠率,百分比", requiredMode = Schema.RequiredMode.REQUIRED, example = "99.88")
+    @NotNull(message = "优惠率,百分比不能为空")
+    private BigDecimal discountPercent;
+
+    @Schema(description = "优惠金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "4452")
+    @NotNull(message = "优惠金额,单位:元不能为空")
+    private BigDecimal discountPrice;
+
+    // TODO 芋艿:后面删除
+//    @Schema(description = "支付金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "32240")
+//    @NotNull(message = "支付金额,单位:元不能为空")
+//    private BigDecimal payPrice;
+
+    @Schema(description = "定金金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127")
+    @NotNull(message = "定金金额,单位:元不能为空")
+    private BigDecimal depositPrice;
+
+    @Schema(description = "附件地址", example = "https://www.iocoder.cn")
+    private String fileUrl;
+
+    @Schema(description = "结算账户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31189")
+    @NotNull(message = "结算账户编号不能为空")
+    private Long accountId;
+
+    @Schema(description = "备注", example = "你猜")
+    private String description;
+
+    // TODO 芋艿:后面删除
+//    @Schema(description = "销售状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
+//    @NotNull(message = "销售状态不能为空")
+//    private Integer status;
+
+    @Schema(description = "ERP 销售订单明细列表")
+    private List<ErpSalesOrderItemDO> salesOrderItems;
+
+}

+ 6 - 0
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/package-info.java

@@ -0,0 +1,6 @@
+/**
+ * 提供 RESTful API 给前端:
+ * 1. admin 包:提供给管理后台 yudao-ui-admin 前端项目
+ * 2. app 包:提供给用户 APP yudao-ui-app 前端项目,它的 Controller 和 VO 都要添加 App 前缀,用于和管理后台进行区分
+ */
+package cn.iocoder.yudao.module.erp.controller;

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio