Ver Fonte

Merge remote-tracking branch 'origin/feature/crm'

Joey há 1 ano atrás
pai
commit
fdaa08c258
100 ficheiros alterados com 3967 adições e 16 exclusões
  1. 1 0
      pom.xml
  2. 65 0
      sql/mysql/crm.sql
  3. 20 0
      sql/mysql/crm_data.sql
  4. 0 0
      sql/mysql/crm_menu.sql
  5. 97 15
      sql/mysql/ruoyi-vue-pro.sql
  6. 4 1
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/enums/ServiceErrorCodeRange.java
  7. 28 0
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/Telephone.java
  8. 25 0
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/TelephoneValidator.java
  9. 25 0
      yudao-module-crm/pom.xml
  10. 33 0
      yudao-module-crm/yudao-module-crm-api/pom.xml
  11. 4 0
      yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/api/package-info.java
  12. 15 0
      yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/DictTypeConstants.java
  13. 45 0
      yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
  14. 31 0
      yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/PermissionTypeEnum.java
  15. 38 0
      yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/TransferTypeEnum.java
  16. 39 0
      yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/customer/CrmCustomerLevelEnum.java
  17. 66 0
      yudao-module-crm/yudao-module-crm-biz/pom.xml
  18. 4 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/api/package-info.java
  19. 11 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.http
  20. 99 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java
  21. 4 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/package-info.java
  22. 57 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessBaseVO.java
  23. 16 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessCreateReqVO.java
  24. 75 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessExcelVO.java
  25. 74 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessExportReqVO.java
  26. 18 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessPageReqVO.java
  27. 19 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessRespVO.java
  28. 22 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessUpdateReqVO.java
  29. 34 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmTransferBusinessReqVO.java
  30. 119 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatus/CrmBusinessStatusController.java
  31. 33 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatus/vo/CrmBusinessStatusBaseVO.java
  32. 14 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatus/vo/CrmBusinessStatusCreateReqVO.java
  33. 30 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatus/vo/CrmBusinessStatusExcelVO.java
  34. 23 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatus/vo/CrmBusinessStatusExportReqVO.java
  35. 18 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatus/vo/CrmBusinessStatusPageReqVO.java
  36. 15 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatus/vo/CrmBusinessStatusRespVO.java
  37. 20 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatus/vo/CrmBusinessStatusUpdateReqVO.java
  38. 110 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatustype/CrmBusinessStatusTypeController.java
  39. 27 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatustype/vo/CrmBusinessStatusTypeBaseVO.java
  40. 15 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatustype/vo/CrmBusinessStatusTypeCreateReqVO.java
  41. 32 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatustype/vo/CrmBusinessStatusTypeExcelVO.java
  42. 29 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatustype/vo/CrmBusinessStatusTypeExportReqVO.java
  43. 21 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatustype/vo/CrmBusinessStatusTypePageReqVO.java
  44. 19 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatustype/vo/CrmBusinessStatusTypeRespVO.java
  45. 21 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatustype/vo/CrmBusinessStatusTypeUpdateReqVO.java
  46. 89 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java
  47. 4 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/package-info.java
  48. 52 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueBaseVO.java
  49. 14 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueCreateReqVO.java
  50. 66 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueExcelVO.java
  51. 52 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueExportReqVO.java
  52. 24 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmCluePageReqVO.java
  53. 27 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueRespVO.java
  54. 20 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueUpdateReqVO.java
  55. 108 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/ContactController.java
  56. 61 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactBaseVO.java
  57. 14 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactCreateReqVO.java
  58. 59 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactExcelVO.java
  59. 55 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactExportReqVO.java
  60. 61 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactPageReqVO.java
  61. 20 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactRespVO.java
  62. 17 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactUpdateReqVO.java
  63. 28 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmTransferContactReqVO.java
  64. 98 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/ContractController.java
  65. 82 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractBaseVO.java
  66. 14 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractCreateReqVO.java
  67. 70 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractExcelVO.java
  68. 37 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractExportReqVO.java
  69. 42 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractPageReqVO.java
  70. 22 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractRespVO.java
  71. 20 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractUpdateReqVO.java
  72. 29 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmTransferContractReqVO.java
  73. 4 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.http
  74. 98 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java
  75. 80 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerBaseVO.java
  76. 16 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerCreateReqVO.java
  77. 93 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerExcelVO.java
  78. 17 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerExportReqVO.java
  79. 22 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerPageReqVO.java
  80. 41 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerRespVO.java
  81. 20 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerUpdateReqVO.java
  82. 28 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmTransferCustomerReqVO.java
  83. 4 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/package-info.java
  84. 89 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/ReceivableController.java
  85. 89 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/ReceivablePlanController.java
  86. 4 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/package-info.java
  87. 80 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/ReceivableBaseVO.java
  88. 14 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/ReceivableCreateReqVO.java
  89. 80 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/ReceivableExcelVO.java
  90. 72 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/ReceivableExportReqVO.java
  91. 75 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/ReceivablePageReqVO.java
  92. 72 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/ReceivablePlanBaseVO.java
  93. 14 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/ReceivablePlanCreateReqVO.java
  94. 71 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/ReceivablePlanExcelVO.java
  95. 52 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/ReceivablePlanExportReqVO.java
  96. 58 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/ReceivablePlanPageReqVO.java
  97. 19 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/ReceivablePlanRespVO.java
  98. 18 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/ReceivablePlanUpdateReqVO.java
  99. 19 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/ReceivableRespVO.java
  100. 18 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/ReceivableUpdateReqVO.java

+ 1 - 0
pom.xml

@@ -21,6 +21,7 @@
 <!--        <module>yudao-module-mp</module>-->
 <!--        <module>yudao-module-pay</module>-->
 <!--        <module>yudao-module-mall</module>-->
+        <module>yudao-module-crm</module>
         <!-- 示例项目 -->
 <!--        <module>yudao-example</module>-->
     </modules>

+ 65 - 0
sql/mysql/crm.sql

@@ -0,0 +1,65 @@
+SET NAMES utf8mb4;
+
+
+
+
+-- ----------------------------
+-- 回款表
+-- ----------------------------
+DROP TABLE IF EXISTS `crm_receivable`;
+CREATE TABLE `crm_receivable`  (
+   `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
+   `no` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '回款编号',
+   `plan_id` bigint(20) NULL DEFAULT NULL COMMENT '回款计划ID',
+   `customer_id` bigint(20) NULL DEFAULT NULL COMMENT '客户ID',
+   `contract_id` bigint(20) NULL DEFAULT NULL COMMENT '合同ID',
+   `check_status` tinyint(4) NULL DEFAULT NULL COMMENT '审批状态',
+   `process_instance_id` bigint(20) NULL DEFAULT NULL COMMENT '工作流编号',
+   `return_time` datetime NULL DEFAULT NULL COMMENT '回款日期',
+   `return_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '回款方式',
+   `price` decimal(10, 2) NULL DEFAULT NULL COMMENT '回款金额',
+   `owner_user_id` bigint(20) NULL DEFAULT NULL COMMENT '负责人的用户编号',
+   `batch_id` bigint(20) NULL DEFAULT NULL COMMENT '批次',
+   `sort` int(11) NULL DEFAULT NULL COMMENT '显示顺序',
+   `data_scope` tinyint(4) NULL DEFAULT 1 COMMENT '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)',
+   `data_scope_dept_ids` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '数据范围(指定部门数组)',
+   `status` tinyint(4) NOT NULL COMMENT '状态(0正常 1停用)',
+   `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',
+   `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
+   `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+   `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
+   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
+   `tenant_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '租户编号',
+   PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '回款管理' ROW_FORMAT = DYNAMIC;
+
+
+-- ----------------------------
+-- 回款计划表
+-- ----------------------------
+DROP TABLE IF EXISTS `crm_receivable_plan`;
+CREATE TABLE `crm_receivable_plan`  (
+    `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
+    `index_no` bigint(20) NULL DEFAULT NULL COMMENT '期数',
+    `receivable_id` bigint(20) NULL DEFAULT NULL COMMENT '回款ID',
+    `status` tinyint(4) NOT NULL COMMENT '完成状态',
+    `check_status` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '审批状态',
+    `process_instance_id` bigint(20) NULL DEFAULT NULL COMMENT '工作流编号',
+    `price` decimal(10, 2) NULL DEFAULT NULL COMMENT '计划回款金额',
+    `return_time` datetime NULL DEFAULT NULL COMMENT '计划回款日期',
+    `remind_days` bigint(20) NULL DEFAULT NULL COMMENT '提前几天提醒',
+    `remind_time` datetime NULL DEFAULT NULL COMMENT '提醒日期',
+    `customer_id` bigint(20) NULL DEFAULT NULL COMMENT '客户ID',
+    `contract_id` bigint(20) NULL DEFAULT NULL COMMENT '合同ID',
+    `owner_user_id` bigint(20) NULL DEFAULT NULL COMMENT '负责人',
+    `sort` int(11) NULL DEFAULT NULL COMMENT '显示顺序',
+    `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',
+    `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
+    `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
+    `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
+    `tenant_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '租户编号',
+    PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '回款计划' ROW_FORMAT = DYNAMIC;

+ 20 - 0
sql/mysql/crm_data.sql

@@ -0,0 +1,20 @@
+
+INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (184, '回款管理审批状态', 'crm_receivable_check_status', 0, '回款管理审批状态(0 未审核 1 审核通过 2 审核拒绝 3 审核中 4 已撤回)', '1', '2023-10-18 21:44:24', '1', '2023-10-18 21:44:24', b'0', '1970-01-01 00:00:00');
+INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (185, '回款管理-回款方式', 'crm_return_type', 0, '回款管理-回款方式', '1', '2023-10-18 21:54:10', '1', '2023-10-18 21:54:10', b'0', '1970-01-01 00:00:00');
+
+
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1389, 0, '未审核', '0', 'crm_receivable_check_status', 0, 'default', '', '0 未审核 ', '1', '2023-10-18 21:46:00', '1', '2023-10-18 21:47:16', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1390, 1, '审核通过', '1', 'crm_receivable_check_status', 0, 'default', '', '1 审核通过', '1', '2023-10-18 21:46:18', '1', '2023-10-18 21:47:08', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1391, 2, '审核拒绝', '2', 'crm_receivable_check_status', 0, 'default', '', ' 2 审核拒绝', '1', '2023-10-18 21:46:58', '1', '2023-10-18 21:47:21', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1392, 3, '审核中', '3', 'crm_receivable_check_status', 0, 'default', '', ' 3 审核中', '1', '2023-10-18 21:47:35', '1', '2023-10-18 21:47:35', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1393, 4, '已撤回', '4', 'crm_receivable_check_status', 0, 'default', '', ' 4 已撤回', '1', '2023-10-18 21:47:46', '1', '2023-10-18 21:47:46', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1394, 1, '支票', '1', 'crm_return_type', 0, 'default', '', '', '1', '2023-10-18 21:54:29', '1', '2023-10-18 21:54:29', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1395, 2, '现金', '2', 'crm_return_type', 0, 'default', '', '', '1', '2023-10-18 21:54:41', '1', '2023-10-18 21:54:41', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1396, 3, '邮政汇款', '3', 'crm_return_type', 0, 'default', '', '', '1', '2023-10-18 21:54:53', '1', '2023-10-18 21:54:53', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1397, 4, '电汇', '4', 'crm_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:07', '1', '2023-10-18 21:55:07', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1398, 5, '网上转账', '5', 'crm_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:24', '1', '2023-10-18 21:55:24', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1399, 6, '支付宝', '6', 'crm_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:38', '1', '2023-10-18 21:55:38', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1400, 7, '微信支付', '7', 'crm_return_type', 0, 'default', '', '', '1', '2023-10-18 21:55:53', '1', '2023-10-18 21:55:53', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1401, 8, '其他', '8', 'crm_return_type', 0, 'default', '', '', '1', '2023-10-18 21:56:06', '1', '2023-10-18 21:56:06', b'0');
+
+

+ 0 - 0
sql/mysql/crm_menu.sql


+ 97 - 15
sql/mysql/ruoyi-vue-pro.sql

@@ -11,7 +11,7 @@
  Target Server Version : 80034
  File Encoding         : 65001
 
- Date: 24/10/2023 20:48:38
+ Date: 29/10/2023 19:22:42
 */
 
 SET NAMES utf8mb4;
@@ -341,7 +341,8 @@ CREATE TABLE `infra_api_access_log`  (
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-  PRIMARY KEY (`id`) USING BTREE
+  PRIMARY KEY (`id`) USING BTREE,
+  INDEX `idx_create_time`(`create_time` ASC) USING BTREE
 ) ENGINE = InnoDB AUTO_INCREMENT = 35832 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'API 访问日志表';
 
 -- ----------------------------
@@ -384,7 +385,7 @@ CREATE TABLE `infra_api_error_log`  (
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 1745 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
+) ENGINE = InnoDB AUTO_INCREMENT = 1750 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
 
 -- ----------------------------
 -- Records of infra_api_error_log
@@ -538,7 +539,7 @@ CREATE TABLE `infra_file`  (
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 1108 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
+) ENGINE = InnoDB AUTO_INCREMENT = 1109 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
 
 -- ----------------------------
 -- Records of infra_file
@@ -587,7 +588,7 @@ CREATE TABLE `infra_file_content`  (
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 201 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
+) ENGINE = InnoDB AUTO_INCREMENT = 202 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
 
 -- ----------------------------
 -- Records of infra_file_content
@@ -768,7 +769,7 @@ CREATE TABLE `member_experience_record`  (
   PRIMARY KEY (`id`) USING BTREE,
   INDEX `idx_user_id`(`user_id` ASC) USING BTREE COMMENT '会员经验记录-用户编号',
   INDEX `idx_user_biz_type`(`user_id` ASC, `biz_type` ASC) USING BTREE COMMENT '会员经验记录-用户业务类型'
-) ENGINE = InnoDB AUTO_INCREMENT = 41 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '会员经验记录';
+) ENGINE = InnoDB AUTO_INCREMENT = 42 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '会员经验记录';
 
 -- ----------------------------
 -- Records of member_experience_record
@@ -884,7 +885,7 @@ CREATE TABLE `member_level_record`  (
   `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
   PRIMARY KEY (`id`) USING BTREE,
   INDEX `idx_user_id`(`user_id` ASC) USING BTREE COMMENT '会员等级记录-用户编号'
-) ENGINE = InnoDB AUTO_INCREMENT = 20 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '会员等级记录';
+) ENGINE = InnoDB AUTO_INCREMENT = 21 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '会员等级记录';
 
 -- ----------------------------
 -- Records of member_level_record
@@ -914,7 +915,7 @@ CREATE TABLE `member_point_record`  (
   PRIMARY KEY (`id`) USING BTREE,
   INDEX `index_userId`(`user_id` ASC) USING BTREE,
   INDEX `index_title`(`title` ASC) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 60 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户积分记录';
+) ENGINE = InnoDB AUTO_INCREMENT = 61 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户积分记录';
 
 -- ----------------------------
 -- Records of member_point_record
@@ -1434,6 +1435,39 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st
 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1395, 22, '订单积分奖励(整单取消)', '22', 'member_point_biz_type', 0, 'default', '', '', '1', '2023-10-11 07:42:55', '1', '2023-10-11 07:43:01', b'0');
 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1396, 23, '订单积分奖励(单个退款)', '23', 'member_point_biz_type', 0, 'default', '', '', '1', '2023-10-11 07:43:16', '1', '2023-10-11 07:43:16', b'0');
 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1397, 13, '下单奖励(单个退款)', '13', 'member_experience_biz_type', 0, 'warning', '', '', '1', '2023-10-11 07:45:24', '1', '2023-10-11 07:45:38', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1402, 1, 'A 农、林、牧、渔业', '1', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:02:15', '1', '2023-10-28 23:02:15', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1403, 2, 'B 采矿业', '2', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:02:29', '1', '2023-10-28 23:02:29', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1404, 3, 'C 制造业', '3', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:02:41', '1', '2023-10-28 23:02:41', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1405, 4, 'D 电力、热力、燃气及水生产和供应业', '4', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:02:54', '1', '2023-10-28 23:02:54', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1406, 5, 'E 建筑业', '5', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:03:03', '1', '2023-10-28 23:03:03', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1407, 6, 'F 批发和零售业', '6', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:03:13', '1', '2023-10-28 23:03:13', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1408, 7, 'G 交通运输、仓储和邮政业', '7', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:03:27', '1', '2023-10-28 23:03:27', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1409, 8, 'H 住宿和餐饮业', '8', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:03:37', '1', '2023-10-28 23:03:37', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1410, 9, 'I 信息传输、软件和信息技术服务业', '9', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:03:47', '1', '2023-10-28 23:03:47', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1411, 10, 'J 金融业', '10', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:03:57', '1', '2023-10-28 23:03:57', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1412, 11, 'K 房地产业', '11', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:04:15', '1', '2023-10-28 23:04:22', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1413, 12, 'L 租赁和商务服务业', '12', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:04:33', '1', '2023-10-28 23:04:33', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1414, 13, 'M 科学研究和技术服务业', '13', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:04:43', '1', '2023-10-28 23:04:43', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1415, 14, 'N 水利、环境和公共设施管理业', '14', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:04:53', '1', '2023-10-28 23:04:53', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1416, 15, 'O 居民服务、修理和其他服务业', '15', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:05:05', '1', '2023-10-28 23:05:05', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1417, 16, 'P 教育', '16', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:05:15', '1', '2023-10-28 23:05:15', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1418, 17, 'Q 卫生和社会工作', '17', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:05:44', '1', '2023-10-28 23:05:44', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1419, 18, 'R 文化、体育和娱乐业', '18', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:05:55', '1', '2023-10-28 23:05:55', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1420, 19, 'S 公共管理、社会保障和社会组织', '19', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:06:05', '1', '2023-10-28 23:06:05', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1421, 20, 'T 国际组织', '20', 'crm_customer_industry', 0, 'default', '', '', '1', '2023-10-28 23:06:15', '1', '2023-10-28 23:06:15', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1422, 1, 'A (重点客户)', '1', 'crm_customer_level', 0, 'primary', '', '', '1', '2023-10-28 23:07:13', '1', '2023-10-28 23:07:13', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1423, 2, 'B (普通客户)', '2', 'crm_customer_level', 0, 'info', '', '', '1', '2023-10-28 23:07:35', '1', '2023-10-28 23:07:35', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1424, 3, 'C (非优先客户)', '3', 'crm_customer_level', 0, 'default', '', '', '1', '2023-10-28 23:07:53', '1', '2023-10-28 23:07:53', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1425, 1, '促销', '1', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:08:29', '1', '2023-10-28 23:08:29', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1426, 2, '搜索引擎', '2', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:08:39', '1', '2023-10-28 23:08:39', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1427, 3, '广告', '3', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:08:47', '1', '2023-10-28 23:08:47', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1428, 4, '转介绍', '4', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:08:58', '1', '2023-10-28 23:08:58', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1429, 5, '线上注册', '5', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:09:12', '1', '2023-10-28 23:09:12', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1430, 6, '线上咨询', '6', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:09:22', '1', '2023-10-28 23:09:22', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1431, 7, '预约上门', '7', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:09:39', '1', '2023-10-28 23:09:39', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1432, 8, '陌拜', '8', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:10:04', '1', '2023-10-28 23:10:04', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1433, 9, '电话咨询', '9', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:10:18', '1', '2023-10-28 23:10:18', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1434, 10, '邮件咨询', '10', 'crm_customer_source', 0, 'default', '', '', '1', '2023-10-28 23:10:33', '1', '2023-10-28 23:10:33', b'0');
 COMMIT;
 
 -- ----------------------------
@@ -1454,7 +1488,7 @@ CREATE TABLE `system_dict_type`  (
   `deleted_time` datetime NULL DEFAULT NULL COMMENT '删除时间',
   PRIMARY KEY (`id`) USING BTREE,
   UNIQUE INDEX `dict_type`(`type` ASC) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 185 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';
+) ENGINE = InnoDB AUTO_INCREMENT = 601 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';
 
 -- ----------------------------
 -- Records of system_dict_type
@@ -1528,6 +1562,9 @@ INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creat
 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (182, '佣金提现银行', 'brokerage_bank_name', 0, NULL, '', '2023-09-28 02:46:05', '', '2023-09-28 02:46:05', b'0', NULL);
 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (183, '砍价记录的状态', 'promotion_bargain_record_status', 0, '', '1', '2023-10-05 10:41:08', '1', '2023-10-05 10:41:08', b'0', '1970-01-01 00:00:00');
 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (184, '拼团记录的状态', 'promotion_combination_record_status', 0, '', '1', '2023-10-08 07:24:25', '1', '2023-10-08 07:24:25', b'0', '1970-01-01 00:00:00');
+INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (186, '客户所属行业', 'crm_customer_industry', 0, 'CRM 客户所属行业', '1', '2023-10-28 22:57:07', '1', '2023-10-28 15:11:16', b'0', NULL);
+INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (187, '客户等级', 'crm_customer_level', 0, 'CRM 客户等级', '1', '2023-10-28 22:59:12', '1', '2023-10-28 15:11:16', b'0', NULL);
+INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (188, '客户来源', 'crm_customer_source', 0, 'CRM 客户来源', '1', '2023-10-28 23:00:34', '1', '2023-10-28 15:11:16', b'0', NULL);
 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (600, 'Banner Position', 'promotion_banner_position', 0, '', '1', '2023-10-08 07:24:25', '1', '2023-10-08 07:24:25', b'0', '1970-01-01 00:00:00');
 COMMIT;
 
@@ -1577,7 +1614,7 @@ CREATE TABLE `system_login_log`  (
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 2620 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
+) ENGINE = InnoDB AUTO_INCREMENT = 2626 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
 
 -- ----------------------------
 -- Records of system_login_log
@@ -2203,6 +2240,49 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2388, '商城首页', '', 2, 1, 2362, 'home', 'ep:home-filled', 'mall/home/index', 'MallHome', 0, b'1', b'1', b'1', '', '2023-10-16 12:10:33', '', '2023-10-16 12:10:33', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2389, '核销订单', '', 2, 2, 2166, 'pick-up-order', 'ep:list', 'mall/trade/delivery/pickUpOrder/index', 'PickUpOrder', 0, b'1', b'1', b'1', '', '2023-10-19 16:09:51', '', '2023-10-19 16:09:51', b'0');
 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2390, '优惠活动', '', 1, 99, 2030, 'youhui', 'ep:aim', '', '', 0, b'1', b'1', b'1', '1', '2023-10-21 19:23:49', '1', '2023-10-21 19:23:49', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2391, '客户管理', '', 2, 0, 2397, 'customer', 'fa:address-book-o', 'crm/customer/index', 'CrmCustomer', 0, b'1', b'1', b'1', '', '2023-10-29 09:04:21', '1', '2023-10-29 17:11:03', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2392, '客户查询', 'crm:customer:query', 3, 1, 2391, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2393, '客户创建', 'crm:customer:create', 3, 2, 2391, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2394, '客户更新', 'crm:customer:update', 3, 3, 2391, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2395, '客户删除', 'crm:customer:delete', 3, 4, 2391, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2396, '客户导出', 'crm:customer:export', 3, 5, 2391, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 09:04:21', '', '2023-10-29 09:04:21', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2397, '客户管理系统', '', 1, 200, 0, '/crm', 'ep:avatar', '', '', 0, b'1', b'1', b'1', '1', '2023-10-29 17:08:30', '1', '2023-10-29 17:08:30', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2398, '合同管理', '', 2, 1, 2397, 'contract', 'ep:notebook', 'crm/contract/index', 'CrmContract', 0, b'1', b'1', b'1', '', '2023-10-29 10:50:41', '1', '2023-10-29 18:55:53', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2399, '合同查询', 'crm:contract:query', 3, 1, 2398, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2400, '合同创建', 'crm:contract:create', 3, 2, 2398, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2401, '合同更新', 'crm:contract:update', 3, 3, 2398, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2402, '合同删除', 'crm:contract:delete', 3, 4, 2398, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2403, '合同导出', 'crm:contract:export', 3, 5, 2398, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 10:50:41', '', '2023-10-29 10:50:41', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2404, '线索管理', '', 2, 0, 2397, 'clue', 'fa:pagelines', 'crm/clue/index', 'CrmClue', 0, b'1', b'1', b'1', '', '2023-10-29 11:06:29', '1', '2023-10-29 19:08:35', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2405, '线索查询', 'crm:clue:query', 3, 1, 2404, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2406, '线索创建', 'crm:clue:create', 3, 2, 2404, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2407, '线索更新', 'crm:clue:update', 3, 3, 2404, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2408, '线索删除', 'crm:clue:delete', 3, 4, 2404, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2409, '线索导出', 'crm:clue:export', 3, 5, 2404, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:06:29', '', '2023-10-29 11:06:29', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2410, '商机管理', '', 2, 0, 2397, 'business', 'fa:bus', 'crm/business/index', 'CrmBusiness', 0, b'1', b'1', b'1', '', '2023-10-29 11:12:35', '1', '2023-10-29 19:13:01', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2411, '商机查询', 'crm:business:query', 3, 1, 2410, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2412, '商机创建', 'crm:business:create', 3, 2, 2410, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2413, '商机更新', 'crm:business:update', 3, 3, 2410, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2414, '商机删除', 'crm:business:delete', 3, 4, 2410, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2415, '商机导出', 'crm:business:export', 3, 5, 2410, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:12:35', '', '2023-10-29 11:12:35', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2416, '联系人管理', '', 2, 0, 2397, 'contact', 'fa:address-book-o', 'crm/contact/index', 'Contact', 0, b'1', b'1', b'1', '', '2023-10-29 11:14:56', '1', '2023-10-29 19:15:32', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2417, '联系人查询', 'crm:contact:query', 3, 1, 2416, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2418, '联系人创建', 'crm:contact:create', 3, 2, 2416, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2419, '联系人更新', 'crm:contact:update', 3, 3, 2416, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2420, '联系人删除', 'crm:contact:delete', 3, 4, 2416, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2421, '联系人导出', 'crm:contact:export', 3, 5, 2416, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:14:56', '', '2023-10-29 11:14:56', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2422, '回款管理', '', 2, 0, 2397, 'receivable', 'ep:money', 'crm/receivable/index', 'CrmReceivable', 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '1', '2023-10-29 19:18:52', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2423, '回款管理查询', 'crm:receivable:query', 3, 1, 2422, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2424, '回款管理创建', 'crm:receivable:create', 3, 2, 2422, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2425, '回款管理更新', 'crm:receivable:update', 3, 3, 2422, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2426, '回款管理删除', 'crm:receivable:delete', 3, 4, 2422, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2427, '回款管理导出', 'crm:receivable:export', 3, 5, 2422, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2428, '回款计划管理', '', 2, 0, 2397, 'receivable-plan', 'fa:money', 'crm/receivablePlan/index', 'CrmReceivablePlan', 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '1', '2023-10-29 19:19:08', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2429, '回款计划查询', 'crm:receivable-plan:query', 3, 1, 2428, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2430, '回款计划创建', 'crm:receivable-plan:create', 3, 2, 2428, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2431, '回款计划更新', 'crm:receivable-plan:update', 3, 3, 2428, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2432, '回款计划删除', 'crm:receivable-plan:delete', 3, 4, 2428, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2433, '回款计划导出', 'crm:receivable-plan:export', 3, 5, 2428, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0');
 COMMIT;
 
 -- ----------------------------
@@ -2320,8 +2400,10 @@ CREATE TABLE `system_oauth2_access_token`  (
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 3130 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
+  PRIMARY KEY (`id`) USING BTREE,
+  INDEX `idx_access_token`(`access_token` ASC) USING BTREE,
+  INDEX `idx_refresh_token`(`refresh_token` ASC) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 3137 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
 
 -- ----------------------------
 -- Records of system_oauth2_access_token
@@ -2443,7 +2525,7 @@ CREATE TABLE `system_oauth2_refresh_token`  (
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 1089 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
+) ENGINE = InnoDB AUTO_INCREMENT = 1094 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
 
 -- ----------------------------
 -- Records of system_oauth2_refresh_token
@@ -2483,7 +2565,7 @@ CREATE TABLE `system_operate_log`  (
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 8757 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录';
+) ENGINE = InnoDB AUTO_INCREMENT = 8766 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录';
 
 -- ----------------------------
 -- Records of system_operate_log
@@ -3834,7 +3916,7 @@ CREATE TABLE `system_users`  (
 -- Records of system_users
 -- ----------------------------
 BEGIN;
-INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '芋道源码', '管理员', 103, '[1]', 'aoteman@126.com', '15612345678', 1, 'http://127.0.0.1:48080/admin-api/infra/file/4/get/37e56010ecbee472cdd821ac4b608e151e62a74d9633f15d085aee026eedeb60.png', 0, '0:0:0:0:0:0:0:1', '2023-10-24 20:20:54', 'admin', '2021-01-05 17:03:47', NULL, '2023-10-24 20:20:54', b'0', 1);
+INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '芋道源码', '管理员', 103, '[1]', 'aoteman@126.com', '15612345678', 1, 'http://127.0.0.1:48080/admin-api/infra/file/4/get/37e56010ecbee472cdd821ac4b608e151e62a74d9633f15d085aee026eedeb60.png', 0, '0:0:0:0:0:0:0:1', '2023-10-29 17:06:12', 'admin', '2021-01-05 17:03:47', NULL, '2023-10-29 17:06:12', b'0', 1);
 INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (100, 'yudao', '$2a$10$11U48RhyJ5pSBYWSn12AD./ld671.ycSzJHbyrtpeoMeYiw31eo8a', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, '', 1, '127.0.0.1', '2022-07-09 23:03:33', '', '2021-01-07 09:07:17', NULL, '2022-07-09 23:03:33', b'0', 1);
 INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (103, 'yuanma', '$2a$10$YMpimV4T6BtDhIaA8jSW.u8UTGBeGhc/qwXP4oxoMr4mOw9.qttt6', '源码', NULL, 106, NULL, 'yuanma@iocoder.cn', '15601701300', 0, '', 0, '127.0.0.1', '2022-07-08 01:26:27', '', '2021-01-13 23:50:35', NULL, '2022-07-08 01:26:27', b'0', 1);
 INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (104, 'test', '$2a$10$GP8zvqHB//TekuzYZSBYAuBQJiNq1.fxQVDYJ.uBCOnWCtDVKE4H6', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, '', 0, '0:0:0:0:0:0:0:1', '2023-09-24 18:21:19', '', '2021-01-21 02:13:53', NULL, '2023-09-24 18:21:19', b'0', 1);

+ 4 - 1
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/enums/ServiceErrorCodeRange.java

@@ -35,9 +35,12 @@ public class ServiceErrorCodeRange {
     // 模块 member 错误码区间 [1-004-000-000 ~ 1-005-000-000)
     // 模块 mp 错误码区间 [1-006-000-000 ~ 1-007-000-000)
     // 模块 pay 错误码区间 [1-007-000-000 ~ 1-008-000-000)
-    // 模块 product 错误码区间 [1-008-000-000 ~ 1-009-000-000)
     // 模块 bpm 错误码区间 [1-009-000-000 ~ 1-010-000-000)
+
+    // 模块 product 错误码区间 [1-008-000-000 ~ 1-009-000-000)
     // 模块 trade 错误码区间 [1-011-000-000 ~ 1-012-000-000)
     // 模块 promotion 错误码区间 [1-013-000-000 ~ 1-014-000-000)
 
+    // 模块 crm 错误码区间 [1-020-000-000 ~ 1-021-000-000)
+
 }

+ 28 - 0
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/Telephone.java

@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.framework.common.validation;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.*;
+
+@Target({
+        ElementType.METHOD,
+        ElementType.FIELD,
+        ElementType.ANNOTATION_TYPE,
+        ElementType.CONSTRUCTOR,
+        ElementType.PARAMETER,
+        ElementType.TYPE_USE
+})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Constraint(
+        validatedBy = TelephoneValidator.class
+)
+public @interface Telephone {
+
+    String message() default "电话格式不正确";
+
+    Class<?>[] groups() default {};
+
+    Class<? extends Payload>[] payload() default {};
+
+}

+ 25 - 0
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/TelephoneValidator.java

@@ -0,0 +1,25 @@
+package cn.iocoder.yudao.framework.common.validation;
+
+import cn.hutool.core.text.CharSequenceUtil;
+import cn.hutool.core.util.PhoneUtil;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+public class TelephoneValidator implements ConstraintValidator<Telephone, String> {
+
+    @Override
+    public void initialize(Telephone annotation) {
+    }
+
+    @Override
+    public boolean isValid(String value, ConstraintValidatorContext context) {
+        // 如果手机号为空,默认不校验,即校验通过
+        if (CharSequenceUtil.isEmpty(value)) {
+            return true;
+        }
+        // 校验手机
+        return PhoneUtil.isTel(value) || PhoneUtil.isPhone(value);
+    }
+
+}

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

@@ -0,0 +1,25 @@
+<?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-crm-api</module>
+        <module>yudao-module-crm-biz</module>
+    </modules>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>yudao-module-crm</artifactId>
+    <packaging>pom</packaging>
+
+    <name>${project.artifactId}</name>
+
+    <description>
+        crm 包下,客户关系管理(Customer Relationship Management)。
+        例如说:客户、联系人、商机、合同、回款等等
+    </description>
+
+</project>

+ 33 - 0
yudao-module-crm/yudao-module-crm-api/pom.xml

@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>cn.iocoder.boot</groupId>
+        <artifactId>yudao-module-crm</artifactId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>yudao-module-crm-api</artifactId>
+    <packaging>jar</packaging>
+
+    <name>${project.artifactId}</name>
+    <description>
+        crm 模块 API,暴露给其它模块调用
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-common</artifactId>
+        </dependency>
+
+        <!-- 参数校验 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+            <optional>true</optional>
+        </dependency>
+    </dependencies>
+
+</project>

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

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

+ 15 - 0
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/DictTypeConstants.java

@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.module.crm.enums;
+
+/**
+ * CRM 字典类型的枚举类
+ *
+ * @author 芋道源码
+ */
+public interface DictTypeConstants {
+
+    // ========== CRM 模块 ==========
+    String CRM_CUSTOMER_INDUSTRY = "crm_customer_industry"; // CRM 客户所属行业
+    String CRM_CUSTOMER_LEVEL = "crm_customer_level"; // CRM 客户等级
+    String CRM_CUSTOMER_SOURCE = "crm_customer_source"; // CRM 客户来源
+
+}

+ 45 - 0
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java

@@ -0,0 +1,45 @@
+package cn.iocoder.yudao.module.crm.enums;
+
+import cn.iocoder.yudao.framework.common.exception.ErrorCode;
+
+/**
+ * CRM 错误码枚举类
+ * <p>
+ * crm 系统,使用 1-020-000-000 段
+ */
+public interface ErrorCodeConstants {
+
+    // ========== 合同管理 1-020-000-000 ==========
+    ErrorCode CONTRACT_NOT_EXISTS = new ErrorCode(1_020_000_000, "合同不存在");
+
+    // ========== 线索管理 1-020-001-000 ==========
+    ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "线索不存在");
+
+    // ========== 商机管理 1-020-002-000 ==========
+    ErrorCode BUSINESS_NOT_EXISTS = new ErrorCode(1_020_002_000, "商机不存在");
+
+    // TODO @lilleo:商机状态、商机类型,都单独错误码段
+
+    ErrorCode BUSINESS_STATUS_TYPE_NOT_EXISTS = new ErrorCode(1_020_002_001, "商机状态类型不存在");
+    ErrorCode BUSINESS_STATUS_NOT_EXISTS = new ErrorCode(1_020_002_002, "商机状态不存在");
+
+    // ========== 联系人管理 1-020-003-000 ==========
+    ErrorCode CONTACT_NOT_EXISTS = new ErrorCode(1_020_003_000, "联系人不存在");
+
+    // TODO @liuhongfeng:错误码分段;
+    ErrorCode RECEIVABLE_NOT_EXISTS = new ErrorCode(1_030_000_001, "回款管理不存在");
+
+    ErrorCode RECEIVABLE_PLAN_NOT_EXISTS = new ErrorCode(1_040_000_001, "回款计划不存在");
+
+    // ========== 客户管理 1_020_006_000 ==========
+    ErrorCode CUSTOMER_NOT_EXISTS = new ErrorCode(1_020_006_000, "客户不存在");
+
+    // TODO @puhui999:权限管理???
+    // ========== 客户管理 1_020_007_000 ==========
+    ErrorCode CRM_PERMISSION_NOT_EXISTS = new ErrorCode(1_020_007_000, "数据权限不存在");
+    ErrorCode CRM_PERMISSION_DENIED = new ErrorCode(1_020_007_001, "{}操作失败,原因:没有权限");
+    ErrorCode CRM_PERMISSION_MODEL_NOT_EXISTS = new ErrorCode(1_020_007_002, "{}不存在");
+    ErrorCode CRM_PERMISSION_MODEL_TRANSFER_FAIL_OWNER_USER_NOT_EXISTS = new ErrorCode(1_020_007_003, "{}操作失败,原因:负责人不存在");
+    ErrorCode CRM_PERMISSION_MODEL_TRANSFER_FAIL_OWNER_USER_EXISTS = new ErrorCode(1_020_007_004, "{}操作失败,原因:转移对象已经是该负责人");
+
+}

+ 31 - 0
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/PermissionTypeEnum.java

@@ -0,0 +1,31 @@
+package cn.iocoder.yudao.module.crm.enums.common;
+
+import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+@Getter
+@AllArgsConstructor
+public enum PermissionTypeEnum implements IntArrayValuable {
+
+    READONLY(1, "只读"),
+    READ_AND_WRITE(2, "读写");
+    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PermissionTypeEnum::getType).toArray();
+
+    /**
+     * 类型
+     */
+    private final Integer type;
+    /**
+     * 类型名
+     */
+    private final String name;
+
+    @Override
+    public int[] array() {
+        return ARRAYS;
+    }
+
+}

+ 38 - 0
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/TransferTypeEnum.java

@@ -0,0 +1,38 @@
+package cn.iocoder.yudao.module.crm.enums.common;
+
+import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+// TODO @puhui999:这个可以不用哈
+/**
+ * Crm 负责人转移后原负责人的处理方式
+ *
+ * @author HUIHUI
+ */
+@Getter
+@AllArgsConstructor
+public enum TransferTypeEnum implements IntArrayValuable {
+
+    REMOVE(1, "移除"),
+    TEAM(2, "转为团队成员");
+
+    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TransferTypeEnum::getType).toArray();
+
+    /**
+     * 类型
+     */
+    private final Integer type;
+    /**
+     * 类型名
+     */
+    private final String name;
+
+    @Override
+    public int[] array() {
+        return ARRAYS;
+    }
+
+}

+ 39 - 0
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/customer/CrmCustomerLevelEnum.java

@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.crm.enums.customer;
+
+import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * CRM 客户等级
+ *
+ * @author Wanwan
+ */
+@Getter
+@AllArgsConstructor
+public enum CrmCustomerLevelEnum implements IntArrayValuable {
+
+    IMPORTANT(1, "A(重点客户)"),
+    GENERAL(2, "B(普通客户)"),
+    LOW_PRIORITY(3, "C(非优先客户)");
+
+    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmCustomerLevelEnum::getStatus).toArray();
+
+    // TODO @wanwan:这里的 status 字段,可以考虑改成 level
+    /**
+     * 状态
+     */
+    private final Integer status;
+    /**
+     * 状态名
+     */
+    private final String name;
+
+    @Override
+    public int[] array() {
+        return ARRAYS;
+    }
+
+}

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

@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xmlns="http://maven.apache.org/POM/4.0.0"
+         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-crm</artifactId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>yudao-module-crm-biz</artifactId>
+
+    <name>${project.artifactId}</name>
+    <description>
+        crm 包下,客户关系管理(Customer Relationship Management)。
+        例如说:客户、联系人、商机、合同、回款等等
+    </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-crm-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>
+
+        <!-- Test 测试相关 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-test</artifactId>
+        </dependency>
+    </dependencies>
+</project>

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

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

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

@@ -0,0 +1,11 @@
+PUT {{baseUrl}}/crm/business/transfer
+Content-Type: application/json
+Authorization: Bearer {{token}}
+tenant-id: {{adminTenentId}}
+
+{
+  "id": 1,
+  "ownerUserId": 2,
+  "transferType": 2,
+  "permissionType": 2
+}

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

@@ -0,0 +1,99 @@
+package cn.iocoder.yudao.module.crm.controller.admin.business;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
+import cn.iocoder.yudao.module.crm.controller.admin.business.vo.*;
+import cn.iocoder.yudao.module.crm.convert.business.CrmBusinessConvert;
+import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
+import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+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;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+
+@Tag(name = "管理后台 - 商机")
+@RestController
+@RequestMapping("/crm/business")
+@Validated
+public class CrmBusinessController {
+
+    @Resource
+    private CrmBusinessService businessService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建商机")
+    @PreAuthorize("@ss.hasPermission('crm:business:create')")
+    public CommonResult<Long> createBusiness(@Valid @RequestBody CrmBusinessCreateReqVO createReqVO) {
+        return success(businessService.createBusiness(createReqVO, getLoginUserId()));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新商机")
+    @PreAuthorize("@ss.hasPermission('crm:business:update')")
+    public CommonResult<Boolean> updateBusiness(@Valid @RequestBody CrmBusinessUpdateReqVO updateReqVO) {
+        businessService.updateBusiness(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除商机")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('crm:business:delete')")
+    public CommonResult<Boolean> deleteBusiness(@RequestParam("id") Long id) {
+        businessService.deleteBusiness(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得商机")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('crm:business:query')")
+    public CommonResult<CrmBusinessRespVO> getBusiness(@RequestParam("id") Long id) {
+        CrmBusinessDO business = businessService.getBusiness(id);
+        return success(CrmBusinessConvert.INSTANCE.convert(business));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得商机分页")
+    @PreAuthorize("@ss.hasPermission('crm:business:query')")
+    public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessPage(@Valid CrmBusinessPageReqVO pageVO) {
+        PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPage(pageVO);
+
+        return success(CrmBusinessConvert.INSTANCE.convertPage(pageResult));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出商机 Excel")
+    @PreAuthorize("@ss.hasPermission('crm:business:export')")
+    @OperateLog(type = EXPORT)
+    public void exportBusinessExcel(@Valid CrmBusinessExportReqVO exportReqVO,
+                                    HttpServletResponse response) throws IOException {
+        List<CrmBusinessDO> list = businessService.getBusinessList(exportReqVO);
+        // 导出 Excel
+        List<CrmBusinessExcelVO> datas = CrmBusinessConvert.INSTANCE.convertList02(list);
+        ExcelUtils.write(response, "商机.xls", "数据", CrmBusinessExcelVO.class, datas);
+    }
+
+    @PutMapping("/transfer")
+    @Operation(summary = "商机转移")
+    @PreAuthorize("@ss.hasPermission('crm:business:update')")
+    public CommonResult<Boolean> transfer(@Valid @RequestBody CrmTransferBusinessReqVO reqVO) {
+        businessService.transferBusiness(reqVO, getLoginUserId());
+        return success(true);
+    }
+
+}

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

@@ -0,0 +1,4 @@
+/**
+ * 商机(销售机会)
+ */
+package cn.iocoder.yudao.module.crm.controller.admin.business;

+ 57 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessBaseVO.java

@@ -0,0 +1,57 @@
+package cn.iocoder.yudao.module.crm.controller.admin.business.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import javax.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+/**
+ * 商机 Base VO,提供给添加、修改、详细的子 VO 使用
+ * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
+ */
+@Data
+public class CrmBusinessBaseVO {
+
+    @Schema(description = "商机名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
+    @NotNull(message = "商机名称不能为空")
+    private String name;
+
+    @Schema(description = "商机状态类型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "25714")
+    @NotNull(message = "商机状态类型不能为空")
+    private Long statusTypeId;
+
+    @Schema(description = "商机状态编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320")
+    @NotNull(message = "商机状态不能为空")
+    private Long statusId;
+
+    @Schema(description = "下次联系时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime contactNextTime;
+
+    @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10299")
+    @NotNull(message = "客户不能为空")
+    private Long customerId;
+
+    @Schema(description = "预计成交日期")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime dealTime;
+
+    @Schema(description = "商机金额", example = "12371")
+    private Integer price;
+
+    // TODO @ljileo:折扣使用 Integer 类型,存储时,默认 * 100;展示的时候,前端需要 / 100;避免精度丢失问题
+    @Schema(description = "整单折扣")
+    private Integer discountPercent;
+
+    @Schema(description = "产品总金额", example = "12025")
+    private BigDecimal productPrice;
+
+    @Schema(description = "备注", example = "随便")
+    private String remark;
+
+}

+ 16 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessCreateReqVO.java

@@ -0,0 +1,16 @@
+package cn.iocoder.yudao.module.crm.controller.admin.business.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+@Schema(description = "管理后台 - 商机创建 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class CrmBusinessCreateReqVO extends CrmBusinessBaseVO {
+
+    // TODO @ljileo:新建的时候,应该可以传递添加的产品;
+
+}

+ 75 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessExcelVO.java

@@ -0,0 +1,75 @@
+package cn.iocoder.yudao.module.crm.controller.admin.business.vo;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.Set;
+
+/**
+ * 商机 Excel VO
+ *
+ * @author ljlleo
+ */
+@Data
+public class CrmBusinessExcelVO {
+
+    @ExcelProperty("主键")
+    private Long id;
+
+    @ExcelProperty("商机名称")
+    private String name;
+
+    @ExcelProperty("商机状态类型编号")
+    private Long statusTypeId;
+
+    @ExcelProperty("商机状态编号")
+    private Long statusId;
+
+    @ExcelProperty("下次联系时间")
+    private LocalDateTime contactNextTime;
+
+    @ExcelProperty("客户编号")
+    private Long customerId;
+
+    @ExcelProperty("预计成交日期")
+    private LocalDateTime dealTime;
+
+    @ExcelProperty("商机金额")
+    private BigDecimal price;
+
+    @ExcelProperty("整单折扣")
+    private BigDecimal discountPercent;
+
+    @ExcelProperty("产品总金额")
+    private BigDecimal productPrice;
+
+    @ExcelProperty("备注")
+    private String remark;
+
+    @ExcelProperty("负责人的用户编号")
+    private Long ownerUserId;
+
+    @ExcelProperty("创建时间")
+    private LocalDateTime createTime;
+
+    @ExcelProperty("只读权限的用户编号数组")
+    private Set<Long> roUserIds;
+
+    @ExcelProperty("读写权限的用户编号数组")
+    private Set<Long> rwUserIds;
+
+    @ExcelProperty("1赢单2输单3无效")
+    private Integer endStatus;
+
+    @ExcelProperty("结束时的备注")
+    private String endRemark;
+
+    @ExcelProperty("最后跟进时间")
+    private LocalDateTime contactLastTime;
+
+    @ExcelProperty("跟进状态")
+    private Integer followUpStatus;
+
+}

+ 74 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessExportReqVO.java

@@ -0,0 +1,74 @@
+package cn.iocoder.yudao.module.crm.controller.admin.business.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 商机 Excel 导出 Request VO,参数和 CrmBusinessPageReqVO 是一致的")
+@Data
+public class CrmBusinessExportReqVO {
+
+    @Schema(description = "商机名称", example = "李四")
+    private String name;
+
+    @Schema(description = "商机状态类型编号", example = "25714")
+    private Long statusTypeId;
+
+    @Schema(description = "商机状态编号", example = "30320")
+    private Long statusId;
+
+    @Schema(description = "下次联系时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] contactNextTime;
+
+    @Schema(description = "客户编号", example = "10299")
+    private Long customerId;
+
+    @Schema(description = "预计成交日期")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] dealTime;
+
+    @Schema(description = "商机金额", example = "12371")
+    private BigDecimal price;
+
+    @Schema(description = "整单折扣")
+    private BigDecimal discountPercent;
+
+    @Schema(description = "产品总金额", example = "12025")
+    private BigDecimal productPrice;
+
+    @Schema(description = "备注", example = "随便")
+    private String remark;
+
+    @Schema(description = "负责人的用户编号", example = "25562")
+    private Long ownerUserId;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+    @Schema(description = "只读权限的用户编号数组")
+    private String roUserIds;
+
+    @Schema(description = "读写权限的用户编号数组")
+    private String rwUserIds;
+
+    @Schema(description = "1赢单2输单3无效", example = "1")
+    private Integer endStatus;
+
+    @Schema(description = "结束时的备注", example = "你说的对")
+    private String endRemark;
+
+    @Schema(description = "最后跟进时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] contactLastTime;
+
+    @Schema(description = "跟进状态", example = "1")
+    private Integer followUpStatus;
+
+}

+ 18 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessPageReqVO.java

@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.module.crm.controller.admin.business.vo;
+
+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;
+
+@Schema(description = "管理后台 - 商机分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class CrmBusinessPageReqVO extends PageParam {
+
+    @Schema(description = "商机名称", example = "李四")
+    private String name;
+
+}

+ 19 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessRespVO.java

@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.module.crm.controller.admin.business.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 商机 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class CrmBusinessRespVO extends CrmBusinessBaseVO {
+
+    @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "32129")
+    private Long id;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDateTime createTime;
+
+}

+ 22 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessUpdateReqVO.java

@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.module.crm.controller.admin.business.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import javax.validation.constraints.NotNull;
+
+@Schema(description = "管理后台 - 商机更新 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class CrmBusinessUpdateReqVO extends CrmBusinessBaseVO {
+
+    @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "32129")
+    @NotNull(message = "主键不能为空")
+    private Long id;
+
+    // TODO @ljileo:修改的时候,应该可以传递添加的产品;
+
+}

+ 34 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmTransferBusinessReqVO.java

@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.crm.controller.admin.business.vo;
+
+import cn.iocoder.yudao.framework.common.validation.InEnum;
+import cn.iocoder.yudao.module.crm.enums.common.PermissionTypeEnum;
+import cn.iocoder.yudao.module.crm.enums.common.TransferTypeEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Schema(description = "管理后台 - 商机转移 Request VO")
+@Data
+public class CrmTransferBusinessReqVO {
+
+    @Schema(description = "商机编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+    @NotNull(message = "联系人编号不能为空")
+    private Long id;
+
+    @Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+    @NotNull(message = "新负责人的用户编号不能为空")
+    private Long ownerUserId;
+
+    @Schema(description = "原负责人移除方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+    @InEnum(TransferTypeEnum.class)
+    @NotNull(message = "原负责人移除方式不能为空")
+    private Integer transferType;
+
+    @Schema(description = "权限类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+    @InEnum(PermissionTypeEnum.class)
+    @NotNull(message = "权限类型不能为空")
+    private Integer permissionType;
+
+
+}

+ 119 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatus/CrmBusinessStatusController.java

@@ -0,0 +1,119 @@
+package cn.iocoder.yudao.module.crm.controller.admin.businessstatus;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
+import cn.iocoder.yudao.module.crm.controller.admin.businessstatus.vo.*;
+import cn.iocoder.yudao.module.crm.convert.businessstatus.CrmBusinessStatusConvert;
+import cn.iocoder.yudao.module.crm.dal.dataobject.businessstatus.CrmBusinessStatusDO;
+import cn.iocoder.yudao.module.crm.service.businessstatus.CrmBusinessStatusService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+import java.io.IOException;
+import java.util.Collection;
+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;
+
+// TODO @lilleo:这个模块,可以挪到 business 下;这样我打开 business 包下,就知道,噢~原来里面有 business 商机、有 type 状态类型、status 具体状态;
+@Tag(name = "管理后台 - 商机状态")
+@RestController
+@RequestMapping("/crm/business-status")
+@Validated
+public class CrmBusinessStatusController {
+
+    @Resource
+    private CrmBusinessStatusService businessStatusService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建商机状态")
+    @PreAuthorize("@ss.hasPermission('crm:business-status:create')")
+    public CommonResult<Long> createBusinessStatus(@Valid @RequestBody CrmBusinessStatusCreateReqVO createReqVO) {
+        return success(businessStatusService.createBusinessStatus(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新商机状态")
+    @PreAuthorize("@ss.hasPermission('crm:business-status:update')")
+    public CommonResult<Boolean> updateBusinessStatus(@Valid @RequestBody CrmBusinessStatusUpdateReqVO updateReqVO) {
+        businessStatusService.updateBusinessStatus(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除商机状态")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('crm:business-status:delete')")
+    public CommonResult<Boolean> deleteBusinessStatus(@RequestParam("id") Long id) {
+        businessStatusService.deleteBusinessStatus(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得商机状态")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('crm:business-status:query')")
+    public CommonResult<CrmBusinessStatusRespVO> getBusinessStatus(@RequestParam("id") Long id) {
+        CrmBusinessStatusDO businessStatus = businessStatusService.getBusinessStatus(id);
+        return success(CrmBusinessStatusConvert.INSTANCE.convert(businessStatus));
+    }
+
+    // TODO @lilleo:这个接口,暂时用不到,可以考虑先删除掉
+    @GetMapping("/list")
+    @Operation(summary = "获得商机状态列表")
+    @Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
+    @PreAuthorize("@ss.hasPermission('crm:business-status:query')")
+    public CommonResult<List<CrmBusinessStatusRespVO>> getBusinessStatusList(@RequestParam("ids") Collection<Long> ids) {
+        List<CrmBusinessStatusDO> list = businessStatusService.getBusinessStatusList(ids);
+        return success(CrmBusinessStatusConvert.INSTANCE.convertList(list));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得商机状态分页")
+    @PreAuthorize("@ss.hasPermission('crm:business-status:query')")
+    public CommonResult<PageResult<CrmBusinessStatusRespVO>> getBusinessStatusPage(@Valid CrmBusinessStatusPageReqVO pageVO) {
+        PageResult<CrmBusinessStatusDO> pageResult = businessStatusService.getBusinessStatusPage(pageVO);
+        return success(CrmBusinessStatusConvert.INSTANCE.convertPage(pageResult));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出商机状态 Excel")
+    @PreAuthorize("@ss.hasPermission('crm:business-status:export')")
+    @OperateLog(type = EXPORT)
+    public void exportBusinessStatusExcel(@Valid CrmBusinessStatusExportReqVO exportReqVO,
+              HttpServletResponse response) throws IOException {
+        List<CrmBusinessStatusDO> list = businessStatusService.getBusinessStatusList(exportReqVO);
+        // 导出 Excel
+        List<CrmBusinessStatusExcelVO> datas = CrmBusinessStatusConvert.INSTANCE.convertList02(list);
+        ExcelUtils.write(response, "商机状态.xls", "数据", CrmBusinessStatusExcelVO.class, datas);
+    }
+
+    // TODO 芋艿:后续再看看
+    @GetMapping("/get-simple-list")
+    @Operation(summary = "获得商机状态列表")
+    @PreAuthorize("@ss.hasPermission('crm:business-status:query')")
+    public CommonResult<List<CrmBusinessStatusRespVO>> getBusinessStatusListByTypeId(@RequestParam("typeId") Integer typeId) {
+        List<CrmBusinessStatusDO> list = businessStatusService.getBusinessStatusListByTypeId(typeId);
+        return success(CrmBusinessStatusConvert.INSTANCE.convertList(list));
+    }
+
+    // TODO 芋艿:后续再看看
+    @GetMapping("/get-all-list")
+    @Operation(summary = "获得商机状态列表")
+    @PreAuthorize("@ss.hasPermission('crm:business-status:query')")
+    public CommonResult<List<CrmBusinessStatusRespVO>> getBusinessStatusList() {
+        List<CrmBusinessStatusDO> list = businessStatusService.getBusinessStatusList();
+        return success(CrmBusinessStatusConvert.INSTANCE.convertList(list));
+    }
+
+}

+ 33 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatus/vo/CrmBusinessStatusBaseVO.java

@@ -0,0 +1,33 @@
+package cn.iocoder.yudao.module.crm.controller.admin.businessstatus.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * 商机状态 Base VO,提供给添加、修改、详细的子 VO 使用
+ * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
+ */
+@Data
+public class CrmBusinessStatusBaseVO {
+
+    // TODO @lilleo:example 要写下
+
+    @Schema(description = "状态类型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22882")
+    @NotNull(message = "状态类型编号不能为空")
+    private Long typeId;
+
+    @Schema(description = "状态名", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
+    @NotNull(message = "状态名不能为空")
+    private String name;
+
+    // TODO @lilleo:percent 应该是 Integer;
+    @Schema(description = "赢单率")
+    private String percent;
+
+    // TODO @lilleo:这个是不是不用前端新增和修改的时候传递,交给顺序计算出来,存储起来就好了;
+    @Schema(description = "排序")
+    private Integer sort;
+
+}

+ 14 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatus/vo/CrmBusinessStatusCreateReqVO.java

@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.module.crm.controller.admin.businessstatus.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import javax.validation.constraints.*;
+
+@Schema(description = "管理后台 - 商机状态创建 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class CrmBusinessStatusCreateReqVO extends CrmBusinessStatusBaseVO {
+
+}

+ 30 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatus/vo/CrmBusinessStatusExcelVO.java

@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.module.crm.controller.admin.businessstatus.vo;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.Data;
+
+// TODO @lilleo:这个暂时不需要;嘿嘿~不是每个模块都需要导出哈
+/**
+ * 商机状态 Excel VO
+ *
+ * @author ljlleo
+ */
+@Data
+public class CrmBusinessStatusExcelVO {
+
+    @ExcelProperty("主键")
+    private Long id;
+
+    @ExcelProperty("状态类型编号")
+    private Long typeId;
+
+    @ExcelProperty("状态名")
+    private String name;
+
+    @ExcelProperty("赢单率")
+    private String percent;
+
+    @ExcelProperty("排序")
+    private Integer sort;
+
+}

+ 23 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatus/vo/CrmBusinessStatusExportReqVO.java

@@ -0,0 +1,23 @@
+package cn.iocoder.yudao.module.crm.controller.admin.businessstatus.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+// TODO @lilleo:这个暂时不需要;嘿嘿~不是每个模块都需要导出哈
+@Schema(description = "管理后台 - 商机状态 Excel 导出 Request VO,参数和 CrmBusinessStatusPageReqVO 是一致的")
+@Data
+public class CrmBusinessStatusExportReqVO {
+
+    @Schema(description = "状态类型编号", example = "22882")
+    private Long typeId;
+
+    @Schema(description = "状态名", example = "李四")
+    private String name;
+
+    @Schema(description = "赢单率")
+    private String percent;
+
+    @Schema(description = "排序")
+    private Integer sort;
+
+}

+ 18 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatus/vo/CrmBusinessStatusPageReqVO.java

@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.module.crm.controller.admin.businessstatus.vo;
+
+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;
+
+@Schema(description = "管理后台 - 商机状态分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class CrmBusinessStatusPageReqVO extends PageParam {
+
+    @Schema(description = "状态类型编号", example = "22882")
+    private Long typeId;
+
+}

+ 15 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatus/vo/CrmBusinessStatusRespVO.java

@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.module.crm.controller.admin.businessstatus.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+
+@Schema(description = "管理后台 - 商机状态 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class CrmBusinessStatusRespVO extends CrmBusinessStatusBaseVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "6802")
+    private Long id;
+
+}

+ 20 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatus/vo/CrmBusinessStatusUpdateReqVO.java

@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.module.crm.controller.admin.businessstatus.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import javax.validation.constraints.NotNull;
+
+@Schema(description = "管理后台 - 商机状态更新 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class CrmBusinessStatusUpdateReqVO extends CrmBusinessStatusBaseVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "6802")
+    @NotNull(message = "编号不能为空")
+    private Long id;
+
+}

+ 110 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatustype/CrmBusinessStatusTypeController.java

@@ -0,0 +1,110 @@
+package cn.iocoder.yudao.module.crm.controller.admin.businessstatustype;
+
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
+import cn.iocoder.yudao.module.crm.controller.admin.businessstatustype.vo.*;
+import cn.iocoder.yudao.module.crm.convert.businessstatustype.CrmBusinessStatusTypeConvert;
+import cn.iocoder.yudao.module.crm.dal.dataobject.businessstatustype.CrmBusinessStatusTypeDO;
+import cn.iocoder.yudao.module.crm.service.businessstatustype.CrmBusinessStatusTypeService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+import java.io.IOException;
+import java.util.Collection;
+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;
+
+// TODO @lilleo:这个模块,可以挪到 business 下;这样我打开 business 包下,就知道,噢~原来里面有 business 商机、有 type 状态类型、status 具体状态;
+@Tag(name = "管理后台 - 商机状态类型")
+@RestController
+@RequestMapping("/crm/business-status-type")
+@Validated
+public class CrmBusinessStatusTypeController {
+
+    @Resource
+    private CrmBusinessStatusTypeService businessStatusTypeService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建商机状态类型")
+    @PreAuthorize("@ss.hasPermission('crm:business-status-type:create')")
+    public CommonResult<Long> createBusinessStatusType(@Valid @RequestBody CrmBusinessStatusTypeCreateReqVO createReqVO) {
+        return success(businessStatusTypeService.createBusinessStatusType(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新商机状态类型")
+    @PreAuthorize("@ss.hasPermission('crm:business-status-type:update')")
+    public CommonResult<Boolean> updateBusinessStatusType(@Valid @RequestBody CrmBusinessStatusTypeUpdateReqVO updateReqVO) {
+        businessStatusTypeService.updateBusinessStatusType(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除商机状态类型")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('crm:business-status-type:delete')")
+    public CommonResult<Boolean> deleteBusinessStatusType(@RequestParam("id") Long id) {
+        businessStatusTypeService.deleteBusinessStatusType(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得商机状态类型")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('crm:business-status-type:query')")
+    public CommonResult<CrmBusinessStatusTypeRespVO> getBusinessStatusType(@RequestParam("id") Long id) {
+        CrmBusinessStatusTypeDO businessStatusType = businessStatusTypeService.getBusinessStatusType(id);
+        return success(CrmBusinessStatusTypeConvert.INSTANCE.convert(businessStatusType));
+    }
+
+    // TODO @lilleo:这个接口,暂时用不到,可以考虑先删除掉
+    @GetMapping("/list")
+    @Operation(summary = "获得商机状态类型列表")
+    @Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
+    @PreAuthorize("@ss.hasPermission('crm:business-status-type:query')")
+    public CommonResult<List<CrmBusinessStatusTypeRespVO>> getBusinessStatusTypeList(@RequestParam("ids") Collection<Long> ids) {
+        List<CrmBusinessStatusTypeDO> list = businessStatusTypeService.getBusinessStatusTypeList(ids);
+        return success(CrmBusinessStatusTypeConvert.INSTANCE.convertList(list));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得商机状态类型分页")
+    @PreAuthorize("@ss.hasPermission('crm:business-status-type:query')")
+    public CommonResult<PageResult<CrmBusinessStatusTypeRespVO>> getBusinessStatusTypePage(@Valid CrmBusinessStatusTypePageReqVO pageVO) {
+        PageResult<CrmBusinessStatusTypeDO> pageResult = businessStatusTypeService.getBusinessStatusTypePage(pageVO);
+        return success(CrmBusinessStatusTypeConvert.INSTANCE.convertPage(pageResult));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出商机状态类型 Excel")
+    @PreAuthorize("@ss.hasPermission('crm:business-status-type:export')")
+    @OperateLog(type = EXPORT)
+    public void exportBusinessStatusTypeExcel(@Valid CrmBusinessStatusTypeExportReqVO exportReqVO,
+              HttpServletResponse response) throws IOException {
+        List<CrmBusinessStatusTypeDO> list = businessStatusTypeService.getBusinessStatusTypeList(exportReqVO);
+        // 导出 Excel
+        List<CrmBusinessStatusTypeExcelVO> datas = CrmBusinessStatusTypeConvert.INSTANCE.convertList02(list);
+        ExcelUtils.write(response, "商机状态类型.xls", "数据", CrmBusinessStatusTypeExcelVO.class, datas);
+    }
+
+    @GetMapping("/get-simple-list")
+    @Operation(summary = "获得商机状态类型列表")
+    @PreAuthorize("@ss.hasPermission('crm:business-status-type:query')")
+    public CommonResult<List<CrmBusinessStatusTypeRespVO>> getBusinessStatusTypeList() {
+        List<CrmBusinessStatusTypeDO> list = businessStatusTypeService.getBusinessStatusTypeListByStatus(CommonStatusEnum.ENABLE.getStatus());
+        return success(CrmBusinessStatusTypeConvert.INSTANCE.convertList(list));
+    }
+
+}

+ 27 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatustype/vo/CrmBusinessStatusTypeBaseVO.java

@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.crm.controller.admin.businessstatustype.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * 商机状态类型 Base VO,提供给添加、修改、详细的子 VO 使用
+ * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
+ */
+@Data
+public class CrmBusinessStatusTypeBaseVO {
+
+    @Schema(description = "状态类型名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
+    @NotNull(message = "状态类型名不能为空")
+    private String name;
+
+    @Schema(description = "使用的部门编号", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotNull(message = "使用的部门编号不能为空")
+    private String deptIds;
+
+    @Schema(description = "开启状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "开启状态不能为空")
+    private Boolean status;
+
+}

+ 15 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatustype/vo/CrmBusinessStatusTypeCreateReqVO.java

@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.module.crm.controller.admin.businessstatustype.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+// TODO 状态类型和状态添加,是在一个请求里,所以需要把 CrmBusinessStatusCreateReqVO 融合进来;
+@Schema(description = "管理后台 - 商机状态类型创建 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class CrmBusinessStatusTypeCreateReqVO extends CrmBusinessStatusTypeBaseVO {
+
+}

+ 32 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatustype/vo/CrmBusinessStatusTypeExcelVO.java

@@ -0,0 +1,32 @@
+package cn.iocoder.yudao.module.crm.controller.admin.businessstatustype.vo;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+// TODO @lilleo:这个暂时不需要;嘿嘿~不是每个模块都需要导出哈
+/**
+ * 商机状态类型 Excel VO
+ *
+ * @author ljlleo
+ */
+@Data
+public class CrmBusinessStatusTypeExcelVO {
+
+    @ExcelProperty("主键")
+    private Long id;
+
+    @ExcelProperty("状态类型名")
+    private String name;
+
+    @ExcelProperty("使用的部门编号")
+    private String deptIds;
+
+    @ExcelProperty("开启状态")
+    private Boolean status;
+
+    @ExcelProperty("创建时间")
+    private LocalDateTime createTime;
+
+}

+ 29 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatustype/vo/CrmBusinessStatusTypeExportReqVO.java

@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.module.crm.controller.admin.businessstatustype.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+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;
+
+// TODO @lilleo:这个暂时不需要;嘿嘿~不是每个模块都需要导出哈
+@Schema(description = "管理后台 - 商机状态类型 Excel 导出 Request VO,参数和 CrmBusinessStatusTypePageReqVO 是一致的")
+@Data
+public class CrmBusinessStatusTypeExportReqVO {
+
+    @Schema(description = "状态类型名", example = "芋艿")
+    private String name;
+
+    @Schema(description = "使用的部门编号")
+    private String deptIds;
+
+    @Schema(description = "开启状态", example = "1")
+    private Boolean status;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}

+ 21 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatustype/vo/CrmBusinessStatusTypePageReqVO.java

@@ -0,0 +1,21 @@
+package cn.iocoder.yudao.module.crm.controller.admin.businessstatustype.vo;
+
+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;
+
+@Schema(description = "管理后台 - 商机状态类型分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class CrmBusinessStatusTypePageReqVO extends PageParam {
+
+    @Schema(description = "状态类型名", example = "芋艿")
+    private String name;
+
+    @Schema(description = "开启状态", example = "1")
+    private Boolean status;
+
+}

+ 19 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatustype/vo/CrmBusinessStatusTypeRespVO.java

@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.module.crm.controller.admin.businessstatustype.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 商机状态类型 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class CrmBusinessStatusTypeRespVO extends CrmBusinessStatusTypeBaseVO {
+
+    @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "24019")
+    private Long id;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDateTime createTime;
+
+}

+ 21 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/businessstatustype/vo/CrmBusinessStatusTypeUpdateReqVO.java

@@ -0,0 +1,21 @@
+package cn.iocoder.yudao.module.crm.controller.admin.businessstatustype.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import javax.validation.constraints.NotNull;
+
+// TODO 状态类型和状态添加,是在一个请求里,所以需要把 CrmBusinessStatusUpdateReqVO 融合进来;
+@Schema(description = "管理后台 - 商机状态类型更新 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class CrmBusinessStatusTypeUpdateReqVO extends CrmBusinessStatusTypeBaseVO {
+
+    @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "24019")
+    @NotNull(message = "主键不能为空")
+    private Long id;
+
+}

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

@@ -0,0 +1,89 @@
+package cn.iocoder.yudao.module.crm.controller.admin.clue;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
+import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.*;
+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.service.clue.CrmClueService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+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 = "管理后台 - 线索")
+@RestController
+@RequestMapping("/crm/clue")
+@Validated
+public class CrmClueController {
+
+    @Resource
+    private CrmClueService clueService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建线索")
+    @PreAuthorize("@ss.hasPermission('crm:clue:create')")
+    public CommonResult<Long> createClue(@Valid @RequestBody CrmClueCreateReqVO createReqVO) {
+        return success(clueService.createClue(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新线索")
+    @PreAuthorize("@ss.hasPermission('crm:clue:update')")
+    public CommonResult<Boolean> updateClue(@Valid @RequestBody CrmClueUpdateReqVO updateReqVO) {
+        clueService.updateClue(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除线索")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('crm:clue:delete')")
+    public CommonResult<Boolean> deleteClue(@RequestParam("id") Long id) {
+        clueService.deleteClue(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得线索")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('crm:clue:query')")
+    public CommonResult<CrmClueRespVO> getClue(@RequestParam("id") Long id) {
+        CrmClueDO clue = clueService.getClue(id);
+        return success(CrmClueConvert.INSTANCE.convert(clue));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得线索分页")
+    @PreAuthorize("@ss.hasPermission('crm:clue:query')")
+    public CommonResult<PageResult<CrmClueRespVO>> getCluePage(@Valid CrmCluePageReqVO pageVO) {
+        PageResult<CrmClueDO> pageResult = clueService.getCluePage(pageVO);
+        return success(CrmClueConvert.INSTANCE.convertPage(pageResult));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出线索 Excel")
+    @PreAuthorize("@ss.hasPermission('crm:clue:export')")
+    @OperateLog(type = EXPORT)
+    public void exportClueExcel(@Valid CrmClueExportReqVO exportReqVO,
+              HttpServletResponse response) throws IOException {
+        List<CrmClueDO> list = clueService.getClueList(exportReqVO);
+        // 导出 Excel
+        List<CrmClueExcelVO> datas = CrmClueConvert.INSTANCE.convertList02(list);
+        ExcelUtils.write(response, "线索.xls", "数据", CrmClueExcelVO.class, datas);
+    }
+
+}

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

@@ -0,0 +1,4 @@
+/**
+ * 线索
+ */
+package cn.iocoder.yudao.module.crm.controller.admin.clue;

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

@@ -0,0 +1,52 @@
+package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
+
+import cn.iocoder.yudao.framework.common.validation.Mobile;
+import cn.iocoder.yudao.framework.common.validation.Telephone;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+/**
+ * 线索 Base VO,提供给添加、修改、详细的子 VO 使用
+ * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
+ */
+@Data
+public class CrmClueBaseVO {
+
+    @Schema(description = "线索名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "线索xxx")
+    @NotEmpty(message = "线索名称不能为空")
+    private String name;
+
+    @Schema(description = "客户 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "520")
+    @NotNull(message = "客户不能为空")
+    private Long customerId;
+
+    @Schema(description = "下次联系时间", example = "2023-10-18 01:00:00")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime contactNextTime;
+
+    @Schema(description = "电话", example = "18000000000")
+    @Telephone
+    private String telephone;
+
+    @Schema(description = "手机号", example = "18000000000")
+    @Mobile
+    private String mobile;
+
+    @Schema(description = "地址", example = "北京市海淀区")
+    private String address;
+
+    @Schema(description = "最后跟进时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime contactLastTime;
+
+    @Schema(description = "备注", example = "随便")
+    private String remark;
+
+}

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

@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import javax.validation.constraints.*;
+
+@Schema(description = "管理后台 - 线索创建 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class CrmClueCreateReqVO extends CrmClueBaseVO {
+
+}

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

@@ -0,0 +1,66 @@
+package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
+
+import cn.iocoder.yudao.module.infra.enums.DictTypeConstants;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
+import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
+
+/**
+ * 线索 Excel VO
+ *
+ * @author Wanwan
+ */
+@Data
+public class CrmClueExcelVO {
+
+    @ExcelProperty("编号")
+    private Long id;
+
+    @ExcelProperty(value = "转化状态", converter = DictConvert.class)
+    @DictFormat(DictTypeConstants.BOOLEAN_STRING)
+    private Boolean transformStatus;
+
+    @ExcelProperty(value = "跟进状态", converter = DictConvert.class)
+    @DictFormat(DictTypeConstants.BOOLEAN_STRING)
+    private Boolean followUpStatus;
+
+    @ExcelProperty("线索名称")
+    private String name;
+
+    // TODO 这里需要导出成客户名称
+    @ExcelProperty("客户id")
+    private Long customerId;
+
+    @ExcelProperty("下次联系时间")
+    private LocalDateTime contactNextTime;
+
+    @ExcelProperty("电话")
+    private String telephone;
+
+    @ExcelProperty("手机号")
+    private String mobile;
+
+    @ExcelProperty("地址")
+    private String address;
+
+    @ExcelProperty("负责人的用户编号")
+    private Long ownerUserId;
+
+    @ExcelProperty("最后跟进时间")
+    private LocalDateTime contactLastTime;
+
+    @ExcelProperty("备注")
+    private String remark;
+
+    @ExcelProperty("创建时间")
+    private LocalDateTime createTime;
+
+}

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

@@ -0,0 +1,52 @@
+package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import java.time.LocalDateTime;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 线索 Excel 导出 Request VO,参数和 CrmCluePageReqVO 是一致的")
+@Data
+public class CrmClueExportReqVO {
+
+    @Schema(description = "转化状态", example = "true")
+    private Boolean transformStatus;
+
+    @Schema(description = "跟进状态", example = "true")
+    private Boolean followUpStatus;
+
+    @Schema(description = "线索名称", example = "线索xxx")
+    private String name;
+
+    @Schema(description = "客户id", example = "520")
+    private Long customerId;
+
+    @Schema(description = "下次联系时间", example = "2023-10-18 01:00:00")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] contactNextTime;
+
+    @Schema(description = "电话", example = "18000000000")
+    private String telephone;
+
+    @Schema(description = "手机号", example = "18000000000")
+    private String mobile;
+
+    @Schema(description = "地址", example = "北京市海淀区")
+    private String address;
+
+    @Schema(description = "负责人的用户编号", example = "27199")
+    private Long ownerUserId;
+
+    @Schema(description = "最后跟进时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] contactLastTime;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}

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

@@ -0,0 +1,24 @@
+package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
+
+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;
+
+@Schema(description = "管理后台 - 线索分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class CrmCluePageReqVO extends PageParam {
+
+    @Schema(description = "线索名称", example = "线索xxx")
+    private String name;
+
+    @Schema(description = "电话", example = "18000000000")
+    private String telephone;
+
+    @Schema(description = "手机号", example = "18000000000")
+    private String mobile;
+
+}

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

@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+
+import javax.validation.constraints.NotNull;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 线索 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class CrmClueRespVO extends CrmClueBaseVO {
+
+    @Schema(description = "编号,主键自增", requiredMode = Schema.RequiredMode.REQUIRED, example = "10969")
+    private Long id;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDateTime createTime;
+
+    @Schema(description = "转化状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+    private Boolean transformStatus;
+
+    @Schema(description = "跟进状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+    private Boolean followUpStatus;
+
+}

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

@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import javax.validation.constraints.NotNull;
+
+@Schema(description = "管理后台 - 线索更新 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class CrmClueUpdateReqVO extends CrmClueBaseVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10969")
+    @NotNull(message = "编号不能为空")
+    private Long id;
+
+}

+ 108 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/ContactController.java

@@ -0,0 +1,108 @@
+package cn.iocoder.yudao.module.crm.controller.admin.contact;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
+import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.*;
+import cn.iocoder.yudao.module.crm.convert.contact.ContactConvert;
+import cn.iocoder.yudao.module.crm.dal.dataobject.contact.ContactDO;
+import cn.iocoder.yudao.module.crm.service.contact.ContactService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+import java.io.IOException;
+import java.util.Collection;
+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;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+
+@Tag(name = "管理后台 - CRM 联系人")
+@RestController
+@RequestMapping("/crm/contact")
+@Validated
+public class ContactController {
+
+    @Resource
+    private ContactService contactService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建crm联系人")
+    @PreAuthorize("@ss.hasPermission('crm:contact:create')")
+    public CommonResult<Long> createContact(@Valid @RequestBody ContactCreateReqVO createReqVO) {
+        return success(contactService.createContact(createReqVO, getLoginUserId()));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新crm联系人")
+    @PreAuthorize("@ss.hasPermission('crm:contact:update')")
+    public CommonResult<Boolean> updateContact(@Valid @RequestBody ContactUpdateReqVO updateReqVO) {
+        contactService.updateContact(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除crm联系人")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('crm:contact:delete')")
+    public CommonResult<Boolean> deleteContact(@RequestParam("id") Long id) {
+        contactService.deleteContact(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得crm联系人")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('crm:contact:query')")
+    public CommonResult<ContactRespVO> getContact(@RequestParam("id") Long id) {
+        ContactDO contact = contactService.getContact(id);
+        return success(ContactConvert.INSTANCE.convert(contact));
+    }
+
+    @GetMapping("/list")
+    @Operation(summary = "获得crm联系人列表")
+    @Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
+    @PreAuthorize("@ss.hasPermission('crm:contact:query')")
+    public CommonResult<List<ContactRespVO>> getContactList(@RequestParam("ids") Collection<Long> ids) {
+        List<ContactDO> list = contactService.getContactList(ids);
+        return success(ContactConvert.INSTANCE.convertList(list));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得crm联系人分页")
+    @PreAuthorize("@ss.hasPermission('crm:contact:query')")
+    public CommonResult<PageResult<ContactRespVO>> getContactPage(@Valid ContactPageReqVO pageVO) {
+        PageResult<ContactDO> pageResult = contactService.getContactPage(pageVO);
+        return success(ContactConvert.INSTANCE.convertPage(pageResult));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出crm联系人 Excel")
+    @PreAuthorize("@ss.hasPermission('crm:contact:export')")
+    @OperateLog(type = EXPORT)
+    public void exportContactExcel(@Valid ContactExportReqVO exportReqVO,
+              HttpServletResponse response) throws IOException {
+        List<ContactDO> list = contactService.getContactList(exportReqVO);
+        // 导出 Excel
+        List<ContactExcelVO> datas = ContactConvert.INSTANCE.convertList02(list);
+        ExcelUtils.write(response, "crm联系人.xls", "数据", ContactExcelVO.class, datas);
+    }
+
+    @PutMapping("/transfer")
+    @Operation(summary = "联系人转移")
+    @PreAuthorize("@ss.hasPermission('crm:contact:update')")
+    public CommonResult<Boolean> transfer(@Valid @RequestBody CrmTransferContactReqVO reqVO) {
+        contactService.transferContact(reqVO, getLoginUserId());
+        return success(true);
+    }
+
+}

+ 61 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactBaseVO.java

@@ -0,0 +1,61 @@
+package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import javax.validation.constraints.NotNull;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+/**
+ * crm联系人 Base VO,提供给添加、修改、详细的子 VO 使用
+ * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
+ */
+@Data
+public class ContactBaseVO {
+
+    // TODO @zyna:部分字段,缺少 example,需要补充;
+
+    @Schema(description = "联系人名称", example = "张三")
+    @NotNull(message = "姓名不能为空")
+    private String name;
+
+    @Schema(description = "下次联系时间")
+    private LocalDateTime nextTime;
+
+    // TODO @zyna:缺少 validator 的校验
+    @Schema(description = "手机号")
+    private String mobile;
+
+    // TODO @zyna:缺少 validator 的校验
+    @Schema(description = "电话")
+    private String telephone;
+
+    // TODO @zyna:缺少 validator 的校验
+    @Schema(description = "电子邮箱")
+    private String email;
+
+    @Schema(description = "职务")
+    private String post;
+
+    // TODO @zyna:非空校验
+    @Schema(description = "客户编号", example = "10795")
+    private Long customerId;
+
+    @Schema(description = "地址")
+    private String address;
+
+    @Schema(description = "备注", example = "你说的对")
+    private String remark;
+
+    // TODO @zyna:这个新建的时候,应该不会传递;而是后端默认设置自己为负责人;
+    @Schema(description = "负责人用户编号", example = "7648")
+    private Long ownerUserId;
+
+    @Schema(description = "最后跟进时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime lastTime;
+
+}

+ 14 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactCreateReqVO.java

@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+@Schema(description = "管理后台 - CRM 联系人创建 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ContactCreateReqVO extends ContactBaseVO {
+
+}

+ 59 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactExcelVO.java

@@ -0,0 +1,59 @@
+package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+
+/**
+ * crm联系人 Excel VO
+ *
+ * @author 芋道源码
+ */
+@Data
+public class ContactExcelVO {
+
+    @ExcelProperty("主键")
+    private Long id;
+
+    @ExcelProperty("联系人名称")
+    private String name;
+
+    @ExcelProperty("下次联系时间")
+    private LocalDateTime nextTime;
+
+    @ExcelProperty("手机号")
+    private String mobile;
+
+    @ExcelProperty("电话")
+    private String telephone;
+
+    @ExcelProperty("电子邮箱")
+    private String email;
+
+    @ExcelProperty("职务")
+    private String post;
+
+    @ExcelProperty("客户编号")
+    private Long customerId;
+
+    @ExcelProperty("地址")
+    private String address;
+
+    @ExcelProperty("备注")
+    private String remark;
+
+    @ExcelProperty("负责人用户编号")
+    private Long ownerUserId;
+
+    @ExcelProperty("创建时间")
+    private LocalDateTime createTime;
+
+    @ExcelProperty("最后跟进时间")
+    private LocalDateTime lastTime;
+
+}

+ 55 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactExportReqVO.java

@@ -0,0 +1,55 @@
+package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import java.time.LocalDateTime;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - CRM 联系人 Excel 导出 Request VO,参数和 ContactPageReqVO 是一致的")
+@Data
+public class ContactExportReqVO {
+
+    @Schema(description = "联系人名称", example = "张三")
+    private String name;
+
+    @Schema(description = "下次联系时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] nextTime;
+
+    @Schema(description = "手机号")
+    private String mobile;
+
+    @Schema(description = "电话")
+    private String telephone;
+
+    @Schema(description = "电子邮箱")
+    private String email;
+
+    @Schema(description = "职务")
+    private String post;
+
+    @Schema(description = "客户编号", example = "10795")
+    private Long customerId;
+
+    @Schema(description = "地址")
+    private String address;
+
+    @Schema(description = "备注", example = "你说的对")
+    private String remark;
+
+    @Schema(description = "负责人用户编号", example = "7648")
+    private Long ownerUserId;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+    @Schema(description = "最后跟进时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] lastTime;
+
+}

+ 61 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactPageReqVO.java

@@ -0,0 +1,61 @@
+package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;
+
+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 = "管理后台 - CRM 联系人分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ContactPageReqVO extends PageParam {
+
+    // TODO @芋艿:需要查询的字段;
+
+    @Schema(description = "联系人名称", example = "张三")
+    private String name;
+
+    @Schema(description = "下次联系时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] nextTime;
+
+    @Schema(description = "手机号")
+    private String mobile;
+
+    @Schema(description = "电话")
+    private String telephone;
+
+    @Schema(description = "电子邮箱")
+    private String email;
+
+    @Schema(description = "职务")
+    private String post;
+
+    @Schema(description = "客户编号", example = "10795")
+    private Long customerId;
+
+    @Schema(description = "地址")
+    private String address;
+
+    @Schema(description = "备注", example = "你说的对")
+    private String remark;
+
+    @Schema(description = "负责人用户编号", example = "7648")
+    private Long ownerUserId;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+    @Schema(description = "最后跟进时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] lastTime;
+
+}

+ 20 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactRespVO.java

@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - CRM 联系人 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ContactRespVO extends ContactBaseVO {
+
+    @Schema(description = "主键", example = "23210")
+    private Long id;
+
+    @Schema(description = "创建时间")
+    private LocalDateTime createTime;
+    @Schema(description = "创建人")
+    private String creator;
+}

+ 17 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactUpdateReqVO.java

@@ -0,0 +1,17 @@
+package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+@Schema(description = "管理后台 - CRM 联系人更新 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ContactUpdateReqVO extends ContactBaseVO {
+
+    @Schema(description = "主键", example = "23210")
+    private Long id;
+
+}

+ 28 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmTransferContactReqVO.java

@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Schema(description = "管理后台 - CRM 联系人转移 Request VO")
+@Data
+public class CrmTransferContactReqVO {
+
+    @Schema(description = "联系人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+    @NotNull(message = "联系人编号不能为空")
+    private Long id;
+
+    @Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+    @NotNull(message = "新负责人的用户编号不能为空")
+    private Long ownerUserId;
+
+    @Schema(description = "原负责人移除方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+    @NotNull(message = "原负责人移除方式不能为空")
+    private Integer transferType;
+
+    @Schema(description = "权限类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+    @NotNull(message = "权限类型不能为空")
+    private Integer permissionType;
+
+}

+ 98 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/ContractController.java

@@ -0,0 +1,98 @@
+package cn.iocoder.yudao.module.crm.controller.admin.contract;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
+import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.*;
+import cn.iocoder.yudao.module.crm.convert.contract.ContractConvert;
+import cn.iocoder.yudao.module.crm.dal.dataobject.contract.ContractDO;
+import cn.iocoder.yudao.module.crm.service.contract.ContractService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+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;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+
+@Tag(name = "管理后台 - CRM 合同")
+@RestController
+@RequestMapping("/crm/contract")
+@Validated
+public class ContractController {
+
+    @Resource
+    private ContractService contractService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建合同")
+    @PreAuthorize("@ss.hasPermission('crm:contract:create')")
+    public CommonResult<Long> createContract(@Valid @RequestBody ContractCreateReqVO createReqVO) {
+        return success(contractService.createContract(createReqVO, getLoginUserId()));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新合同")
+    @PreAuthorize("@ss.hasPermission('crm:contract:update')")
+    public CommonResult<Boolean> updateContract(@Valid @RequestBody ContractUpdateReqVO updateReqVO) {
+        contractService.updateContract(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除合同")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('crm:contract:delete')")
+    public CommonResult<Boolean> deleteContract(@RequestParam("id") Long id) {
+        contractService.deleteContract(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得合同")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('crm:contract:query')")
+    public CommonResult<ContractRespVO> getContract(@RequestParam("id") Long id) {
+        ContractDO contract = contractService.getContract(id);
+        return success(ContractConvert.INSTANCE.convert(contract));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得合同分页")
+    @PreAuthorize("@ss.hasPermission('crm:contract:query')")
+    public CommonResult<PageResult<ContractRespVO>> getContractPage(@Valid ContractPageReqVO pageVO) {
+        PageResult<ContractDO> pageResult = contractService.getContractPage(pageVO);
+        return success(ContractConvert.INSTANCE.convertPage(pageResult));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出合同 Excel")
+    @PreAuthorize("@ss.hasPermission('crm:contract:export')")
+    @OperateLog(type = EXPORT)
+    public void exportContractExcel(@Valid ContractExportReqVO exportReqVO,
+                                    HttpServletResponse response) throws IOException {
+        List<ContractDO> list = contractService.getContractList(exportReqVO);
+        // 导出 Excel
+        List<ContractExcelVO> datas = ContractConvert.INSTANCE.convertList02(list);
+        ExcelUtils.write(response, "合同.xls", "数据", ContractExcelVO.class, datas);
+    }
+
+    @PutMapping("/transfer")
+    @Operation(summary = "合同转移")
+    @PreAuthorize("@ss.hasPermission('crm:contract:update')")
+    public CommonResult<Boolean> transfer(@Valid @RequestBody CrmTransferContractReqVO reqVO) {
+        contractService.transferContract(reqVO, getLoginUserId());
+        return success(true);
+    }
+
+}

+ 82 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractBaseVO.java

@@ -0,0 +1,82 @@
+package cn.iocoder.yudao.module.crm.controller.admin.contract.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import javax.validation.constraints.NotNull;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+// TODO @dhb52:所有类,带下 Crm 前缀,避免和别的模块重复
+/**
+ * 合同 Base VO,提供给添加、修改、详细的子 VO 使用
+ * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
+ */
+@Data
+public class ContractBaseVO {
+
+    // TODO @dhb52:类似 no 字段的 example 要写xia 哈;
+
+    @Schema(description = "合同名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
+    @NotNull(message = "合同名称不能为空")
+    private String name;
+
+    // TODO @dhb52:这个必须传递
+    @Schema(description = "客户编号", example = "18336")
+    private Long customerId;
+
+    @Schema(description = "商机编号", example = "10864")
+    private Long businessId;
+
+    @Schema(description = "工作流编号", example = "1043")
+    private Long processInstanceId;
+
+    // TODO @dhb52:这个必须传递
+    @Schema(description = "下单日期")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime orderDate;
+
+    // TODO @dhb52:这个必须传递
+    @Schema(description = "负责人的用户编号", example = "17144")
+    private Long ownerUserId;
+
+    // TODO @芋艿:未来应该支持自动生成;
+    // TODO @dhb52:这个必须传递;
+    @Schema(description = "合同编号")
+    private String no;
+
+    @Schema(description = "开始时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime startTime;
+
+    @Schema(description = "结束时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime endTime;
+
+    @Schema(description = "合同金额", example = "5617")
+    private Integer price;
+
+    @Schema(description = "整单折扣")
+    private Integer discountPercent;
+
+    @Schema(description = "产品总金额", example = "19510")
+    private Integer productPrice;
+
+    @Schema(description = "联系人编号", example = "18546")
+    private Long contactId;
+
+    @Schema(description = "公司签约人", example = "14036")
+    private Long signUserId;
+
+    @Schema(description = "最后跟进时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime contactLastTime;
+
+    @Schema(description = "备注", example = "你猜")
+    private String remark;
+
+    // TODO @dhb52:增加一个 status 字段:具体有哪些值,你来枚举下;主要页面上有个【草稿】【提交审核】的流程,可以看看。然后要对接工作流,这块也可以看看,不确定的地方问我。
+
+}

+ 14 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractCreateReqVO.java

@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.module.crm.controller.admin.contract.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+@Schema(description = "管理后台 - CRM 合同创建 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ContractCreateReqVO extends ContractBaseVO {
+
+}

+ 70 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractExcelVO.java

@@ -0,0 +1,70 @@
+package cn.iocoder.yudao.module.crm.controller.admin.contract.vo;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * CRM 合同 Excel VO
+ *
+ * @author dhb52
+ */
+@Data
+public class ContractExcelVO {
+
+    @ExcelProperty("合同编号")
+    private Long id;
+
+    @ExcelProperty("合同名称")
+    private String name;
+
+    @ExcelProperty("客户编号")
+    private Long customerId;
+
+    @ExcelProperty("商机编号")
+    private Long businessId;
+
+    @ExcelProperty("工作流编号")
+    private Long processInstanceId;
+
+    @ExcelProperty("下单日期")
+    private LocalDateTime orderDate;
+
+    @ExcelProperty("负责人的用户编号")
+    private Long ownerUserId;
+
+    @ExcelProperty("合同编号")
+    private String no;
+
+    @ExcelProperty("开始时间")
+    private LocalDateTime startTime;
+
+    @ExcelProperty("结束时间")
+    private LocalDateTime endTime;
+
+    @ExcelProperty("合同金额")
+    private Integer price;
+
+    @ExcelProperty("整单折扣")
+    private Integer discountPercent;
+
+    @ExcelProperty("产品总金额")
+    private Integer productPrice;
+
+    @ExcelProperty("联系人编号")
+    private Long contactId;
+
+    @ExcelProperty("公司签约人")
+    private Long signUserId;
+
+    @ExcelProperty("最后跟进时间")
+    private LocalDateTime contactLastTime;
+
+    @ExcelProperty("备注")
+    private String remark;
+
+    @ExcelProperty("创建时间")
+    private LocalDateTime createTime;
+
+}

+ 37 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractExportReqVO.java

@@ -0,0 +1,37 @@
+package cn.iocoder.yudao.module.crm.controller.admin.contract.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - CRM 合同 Excel 导出 Request VO,参数和 ContractPageReqVO 是一致的")
+@Data
+public class ContractExportReqVO {
+
+    @Schema(description = "合同名称", example = "王五")
+    private String name;
+
+    @Schema(description = "客户编号", example = "18336")
+    private Long customerId;
+
+    @Schema(description = "商机编号", example = "10864")
+    private Long businessId;
+
+    @Schema(description = "下单日期")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] orderDate;
+
+    @Schema(description = "合同编号")
+    private String no;
+
+    @Schema(description = "整单折扣")
+    private Integer discountPercent;
+
+    @Schema(description = "产品总金额", example = "19510")
+    private Integer productPrice;
+
+}

+ 42 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractPageReqVO.java

@@ -0,0 +1,42 @@
+package cn.iocoder.yudao.module.crm.controller.admin.contract.vo;
+
+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 = "管理后台 - CRM 合同分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ContractPageReqVO extends PageParam {
+
+    @Schema(description = "合同名称", example = "王五")
+    private String name;
+
+    @Schema(description = "客户编号", example = "18336")
+    private Long customerId;
+
+    @Schema(description = "商机编号", example = "10864")
+    private Long businessId;
+
+    @Schema(description = "下单日期")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] orderDate;
+
+    @Schema(description = "合同编号")
+    private String no;
+
+    @Schema(description = "整单折扣")
+    private Integer discountPercent;
+
+    @Schema(description = "产品总金额", example = "19510")
+    private Integer productPrice;
+
+}

+ 22 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractRespVO.java

@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.module.crm.controller.admin.contract.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - CRM 合同 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ContractRespVO extends ContractBaseVO {
+
+    @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+    private Long id;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDateTime createTime;
+
+}

+ 20 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractUpdateReqVO.java

@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.module.crm.controller.admin.contract.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import javax.validation.constraints.NotNull;
+
+@Schema(description = "管理后台 - CRM 合同更新 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ContractUpdateReqVO extends ContractBaseVO {
+
+    @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+    @NotNull(message = "合同编号不能为空")
+    private Long id;
+
+}

+ 29 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmTransferContractReqVO.java

@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.module.crm.controller.admin.contract.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+// TODO @puhui999:CrmContractTransferReqVO,模块名字要放前面;看看还有没其它类似的
+@Schema(description = "管理后台 - CRM 合同转移 Request VO")
+@Data
+public class CrmTransferContractReqVO {
+
+    @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+    @NotNull(message = "合同编号不能为空")
+    private Long id;
+
+    @Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+    @NotNull(message = "新负责人的用户编号不能为空")
+    private Long ownerUserId;
+
+    @Schema(description = "原负责人移除方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+    @NotNull(message = "原负责人移除方式不能为空")
+    private Integer transferType;
+
+    @Schema(description = "权限类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+    @NotNull(message = "权限类型不能为空")
+    private Integer permissionType;
+
+}

+ 4 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.http

@@ -0,0 +1,4 @@
+### 请求 /crm/customer/test 接口 => 成功
+GET {{baseUrl}}/crm/customer/test
+tenant-id: 1
+Authorization: Bearer {{token}}

+ 98 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java

@@ -0,0 +1,98 @@
+package cn.iocoder.yudao.module.crm.controller.admin.customer;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
+import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.*;
+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.service.customer.CrmCustomerService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+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;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+
+@Tag(name = "管理后台 - CRM 客户")
+@RestController
+@RequestMapping("/crm/customer")
+@Validated
+public class CrmCustomerController {
+
+    @Resource
+    private CrmCustomerService customerService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建客户")
+    @PreAuthorize("@ss.hasPermission('crm:customer:create')")
+    public CommonResult<Long> createCustomer(@Valid @RequestBody CrmCustomerCreateReqVO createReqVO) {
+        return success(customerService.createCustomer(createReqVO, getLoginUserId()));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新客户")
+    @PreAuthorize("@ss.hasPermission('crm:customer:update')")
+    public CommonResult<Boolean> updateCustomer(@Valid @RequestBody CrmCustomerUpdateReqVO updateReqVO) {
+        customerService.updateCustomer(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除客户")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('crm:customer:delete')")
+    public CommonResult<Boolean> deleteCustomer(@RequestParam("id") Long id) {
+        customerService.deleteCustomer(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得客户")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('crm:customer:query')")
+    public CommonResult<CrmCustomerRespVO> getCustomer(@RequestParam("id") Long id) {
+        CrmCustomerDO customer = customerService.getCustomer(id);
+        return success(CrmCustomerConvert.INSTANCE.convert(customer));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得客户分页")
+    @PreAuthorize("@ss.hasPermission('crm:customer:query')")
+    public CommonResult<PageResult<CrmCustomerRespVO>> getCustomerPage(@Valid CrmCustomerPageReqVO pageVO) {
+        PageResult<CrmCustomerDO> pageResult = customerService.getCustomerPage(pageVO);
+        return success(CrmCustomerConvert.INSTANCE.convertPage(pageResult));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出客户 Excel")
+    @PreAuthorize("@ss.hasPermission('crm:customer:export')")
+    @OperateLog(type = EXPORT)
+    public void exportCustomerExcel(@Valid CrmCustomerExportReqVO exportReqVO,
+                                    HttpServletResponse response) throws IOException {
+        List<CrmCustomerDO> list = customerService.getCustomerList(exportReqVO);
+        // 导出 Excel
+        List<CrmCustomerExcelVO> datas = CrmCustomerConvert.INSTANCE.convertList02(list);
+        ExcelUtils.write(response, "客户.xls", "数据", CrmCustomerExcelVO.class, datas);
+    }
+
+    @PutMapping("/transfer")
+    @Operation(summary = "客户转移")
+    @PreAuthorize("@ss.hasPermission('crm:customer:update')")
+    public CommonResult<Boolean> transfer(@Valid @RequestBody CrmTransferCustomerReqVO reqVO) {
+        customerService.transferCustomer(reqVO, getLoginUserId());
+        return success(true);
+    }
+
+}

+ 80 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerBaseVO.java

@@ -0,0 +1,80 @@
+package cn.iocoder.yudao.module.crm.controller.admin.customer.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.module.crm.enums.customer.CrmCustomerLevelEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import javax.validation.constraints.Email;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.Size;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+/**
+ * 客户 Base VO,提供给添加、修改、详细的子 VO 使用
+ * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
+ */
+@Data
+public class CrmCustomerBaseVO {
+
+    @Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
+    @NotEmpty(message = "客户名称不能为空")
+    private String name;
+
+    @Schema(description = "所属行业", example = "1")
+    private Integer industryId;
+
+    @Schema(description = "客户等级", example = "2")
+    @InEnum(CrmCustomerLevelEnum.class)
+    private Integer level;
+
+    @Schema(description = "客户来源", example = "3")
+    private Integer source;
+
+    @Schema(description = "手机", example = "18000000000")
+    @Mobile
+    private String mobile;
+
+    @Schema(description = "电话", example = "18000000000")
+    @Telephone
+    private String telephone;
+
+    @Schema(description = "网址", example = "https://www.baidu.com")
+    private String website;
+
+    @Schema(description = "QQ", example = "123456789")
+    @Size(max = 20, message = "QQ长度不能超过 20 个字符")
+    private String qq;
+
+    @Schema(description = "wechat", example = "123456789")
+    @Size(max = 255, message = "微信长度不能超过 255 个字符")
+    private String wechat;
+
+    @Schema(description = "email", example = "123456789@qq.com")
+    @Email(message = "邮箱格式不正确")
+    @Size(max = 255, message = "邮箱长度不能超过 255 个字符")
+    private String email;
+
+    @Schema(description = "客户描述", example = "任意文字")
+    @Size(max = 4096, message = "客户描述长度不能超过 4096 个字符")
+    private String description;
+
+    @Schema(description = "备注", example = "随便")
+    private String remark;
+
+    @Schema(description = "地区编号", example = "20158")
+    private Long areaId;
+
+    @Schema(description = "详细地址", example = "北京市海淀区")
+    private String detailAddress;
+
+    @Schema(description = "下次联系时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime contactNextTime;
+
+}

+ 16 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerCreateReqVO.java

@@ -0,0 +1,16 @@
+package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+@Schema(description = "管理后台 - CRM 客户创建 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class CrmCustomerCreateReqVO extends CrmCustomerBaseVO {
+
+    // TODO @wanwan:负责人
+
+}

+ 93 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerExcelVO.java

@@ -0,0 +1,93 @@
+package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
+
+import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
+import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
+import cn.iocoder.yudao.module.infra.enums.DictTypeConstants;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+// TODO 芋艿:导出最后做,等基本确认的差不多之后;
+/**
+ * CRM 客户 Excel VO
+ *
+ * @author Wanwan
+ */
+@Data
+public class CrmCustomerExcelVO {
+
+    @ExcelProperty("编号")
+    private Long id;
+
+    @ExcelProperty("客户名称")
+    private String name;
+
+    @ExcelProperty(value = "跟进状态", converter = DictConvert.class)
+    @DictFormat(DictTypeConstants.BOOLEAN_STRING)
+    private Boolean followUpStatus;
+
+    @ExcelProperty(value = "锁定状态", converter = DictConvert.class)
+    @DictFormat(DictTypeConstants.BOOLEAN_STRING)
+    private Boolean lockStatus;
+
+    @ExcelProperty(value = "成交状态", converter = DictConvert.class)
+    @DictFormat(DictTypeConstants.BOOLEAN_STRING)
+    private Boolean dealStatus;
+
+    @ExcelProperty(value = "所属行业", converter = DictConvert.class)
+    @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY)
+    private Integer industryId;
+
+    @ExcelProperty(value = "客户等级", converter = DictConvert.class)
+    @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_LEVEL)
+    private Integer level;
+
+    @ExcelProperty(value = "客户来源", converter = DictConvert.class)
+    @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_SOURCE)
+    private Integer source;
+
+    @ExcelProperty("手机")
+    private String mobile;
+
+    @ExcelProperty("电话")
+    private String telephone;
+
+    @ExcelProperty("网址")
+    private String website;
+
+    @ExcelProperty("QQ")
+    private String qq;
+
+    @ExcelProperty("wechat")
+    private String wechat;
+
+    @ExcelProperty("email")
+    private String email;
+
+    @ExcelProperty("客户描述")
+    private String description;
+
+    @ExcelProperty("备注")
+    private String remark;
+
+    @ExcelProperty("负责人的用户编号")
+    private Long ownerUserId;
+
+    @ExcelProperty("地区编号")
+    private Long areaId;
+
+    @ExcelProperty("详细地址")
+    private String detailAddress;
+
+    @ExcelProperty("最后跟进时间")
+    private LocalDateTime contactLastTime;
+
+    @ExcelProperty("下次联系时间")
+    private LocalDateTime contactNextTime;
+
+    @ExcelProperty("创建时间")
+    private LocalDateTime createTime;
+
+}

+ 17 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerExportReqVO.java

@@ -0,0 +1,17 @@
+package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+// TODO 芋艿:导出最后做,等基本确认的差不多之后;
+@Schema(description = "管理后台 - CRM 客户 Excel 导出 Request VO,参数和 CrmCustomerPageReqVO 是一致的")
+@Data
+public class CrmCustomerExportReqVO {
+
+    @Schema(description = "客户名称", example = "赵六")
+    private String name;
+
+    @Schema(description = "手机", example = "18000000000")
+    private String mobile;
+
+}

+ 22 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerPageReqVO.java

@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
+
+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;
+
+@Schema(description = "管理后台 - CRM 客户分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class CrmCustomerPageReqVO extends PageParam {
+
+    @Schema(description = "客户名称", example = "赵六")
+    private String name;
+
+    @Schema(description = "手机", example = "18000000000")
+    private String mobile;
+
+    // TODO @芋艿:场景;
+}

+ 41 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerRespVO.java

@@ -0,0 +1,41 @@
+package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
+
+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 = "管理后台 - CRM 客户 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class CrmCustomerRespVO extends CrmCustomerBaseVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")
+    private Long id;
+
+    @Schema(description = "跟进状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+    private Boolean followUpStatus;
+
+    @Schema(description = "锁定状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+    private Boolean lockStatus;
+
+    @Schema(description = "成交状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+    private Boolean dealStatus;
+
+    @Schema(description = "负责人的用户编号", example = "25682")
+    private Long ownerUserId;
+
+    @Schema(description = "最后跟进时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime contactLastTime;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDateTime createTime;
+
+}

+ 20 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerUpdateReqVO.java

@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import javax.validation.constraints.NotNull;
+
+@Schema(description = "管理后台 - CRM 客户更新 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class CrmCustomerUpdateReqVO extends CrmCustomerBaseVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")
+    @NotNull(message = "编号不能为空")
+    private Long id;
+
+}

+ 28 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmTransferCustomerReqVO.java

@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Schema(description = "管理后台 - CRM 客户转移 Request VO")
+@Data
+public class CrmTransferCustomerReqVO {
+
+    @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+    @NotNull(message = "客户编号不能为空")
+    private Long id;
+
+    @Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+    @NotNull(message = "新负责人的用户编号不能为空")
+    private Long ownerUserId;
+
+    @Schema(description = "原负责人移除方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+    @NotNull(message = "原负责人移除方式不能为空")
+    private Integer transferType;
+
+    @Schema(description = "权限类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+    @NotNull(message = "权限类型不能为空")
+    private Integer permissionType;
+
+}

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

@@ -0,0 +1,4 @@
+/**
+ * 产品表
+ */
+package cn.iocoder.yudao.module.crm.controller.admin.product;

+ 89 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/ReceivableController.java

@@ -0,0 +1,89 @@
+package cn.iocoder.yudao.module.crm.controller.admin.receivable;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
+import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.*;
+import cn.iocoder.yudao.module.crm.convert.receivable.ReceivableConvert;
+import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.ReceivableDO;
+import cn.iocoder.yudao.module.crm.service.receivable.ReceivableService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+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 = "管理后台 - CRM 回款管理")
+@RestController
+@RequestMapping("/crm/receivable")
+@Validated
+public class ReceivableController {
+
+    @Resource
+    private ReceivableService receivableService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建回款管理")
+    @PreAuthorize("@ss.hasPermission('crm:receivable:create')")
+    public CommonResult<Long> createReceivable(@Valid @RequestBody ReceivableCreateReqVO createReqVO) {
+        return success(receivableService.createReceivable(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新回款管理")
+    @PreAuthorize("@ss.hasPermission('crm:receivable:update')")
+    public CommonResult<Boolean> updateReceivable(@Valid @RequestBody ReceivableUpdateReqVO updateReqVO) {
+        receivableService.updateReceivable(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除回款管理")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('crm:receivable:delete')")
+    public CommonResult<Boolean> deleteReceivable(@RequestParam("id") Long id) {
+        receivableService.deleteReceivable(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得回款管理")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('crm:receivable:query')")
+    public CommonResult<ReceivableRespVO> getReceivable(@RequestParam("id") Long id) {
+        ReceivableDO receivable = receivableService.getReceivable(id);
+        return success(ReceivableConvert.INSTANCE.convert(receivable));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得回款管理分页")
+    @PreAuthorize("@ss.hasPermission('crm:receivable:query')")
+    public CommonResult<PageResult<ReceivableRespVO>> getReceivablePage(@Valid ReceivablePageReqVO pageVO) {
+        PageResult<ReceivableDO> pageResult = receivableService.getReceivablePage(pageVO);
+        return success(ReceivableConvert.INSTANCE.convertPage(pageResult));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出回款管理 Excel")
+    @PreAuthorize("@ss.hasPermission('crm:receivable:export')")
+    @OperateLog(type = EXPORT)
+    public void exportReceivableExcel(@Valid ReceivableExportReqVO exportReqVO,
+              HttpServletResponse response) throws IOException {
+        List<ReceivableDO> list = receivableService.getReceivableList(exportReqVO);
+        // 导出 Excel
+        List<ReceivableExcelVO> datas = ReceivableConvert.INSTANCE.convertList02(list);
+        ExcelUtils.write(response, "回款管理.xls", "数据", ReceivableExcelVO.class, datas);
+    }
+
+}

+ 89 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/ReceivablePlanController.java

@@ -0,0 +1,89 @@
+package cn.iocoder.yudao.module.crm.controller.admin.receivable;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
+import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.*;
+import cn.iocoder.yudao.module.crm.convert.receivable.ReceivablePlanConvert;
+import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.ReceivablePlanDO;
+import cn.iocoder.yudao.module.crm.service.receivable.ReceivablePlanService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+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 = "管理后台 - CRM 回款计划")
+@RestController
+@RequestMapping("/crm/receivable-plan")
+@Validated
+public class ReceivablePlanController {
+
+    @Resource
+    private ReceivablePlanService receivablePlanService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建回款计划")
+    @PreAuthorize("@ss.hasPermission('crm:receivable-plan:create')")
+    public CommonResult<Long> createReceivablePlan(@Valid @RequestBody ReceivablePlanCreateReqVO createReqVO) {
+        return success(receivablePlanService.createReceivablePlan(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新回款计划")
+    @PreAuthorize("@ss.hasPermission('crm:receivable-plan:update')")
+    public CommonResult<Boolean> updateReceivablePlan(@Valid @RequestBody ReceivablePlanUpdateReqVO updateReqVO) {
+        receivablePlanService.updateReceivablePlan(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除回款计划")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('crm:receivable-plan:delete')")
+    public CommonResult<Boolean> deleteReceivablePlan(@RequestParam("id") Long id) {
+        receivablePlanService.deleteReceivablePlan(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得回款计划")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('crm:receivable-plan:query')")
+    public CommonResult<ReceivablePlanRespVO> getReceivablePlan(@RequestParam("id") Long id) {
+        ReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(id);
+        return success(ReceivablePlanConvert.INSTANCE.convert(receivablePlan));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得回款计划分页")
+    @PreAuthorize("@ss.hasPermission('crm:receivable-plan:query')")
+    public CommonResult<PageResult<ReceivablePlanRespVO>> getReceivablePlanPage(@Valid ReceivablePlanPageReqVO pageVO) {
+        PageResult<ReceivablePlanDO> pageResult = receivablePlanService.getReceivablePlanPage(pageVO);
+        return success(ReceivablePlanConvert.INSTANCE.convertPage(pageResult));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出回款计划 Excel")
+    @PreAuthorize("@ss.hasPermission('crm:receivable-plan:export')")
+    @OperateLog(type = EXPORT)
+    public void exportReceivablePlanExcel(@Valid ReceivablePlanExportReqVO exportReqVO,
+              HttpServletResponse response) throws IOException {
+        List<ReceivablePlanDO> list = receivablePlanService.getReceivablePlanList(exportReqVO);
+        // 导出 Excel
+        List<ReceivablePlanExcelVO> datas = ReceivablePlanConvert.INSTANCE.convertList02(list);
+        ExcelUtils.write(response, "回款计划.xls", "数据", ReceivablePlanExcelVO.class, datas);
+    }
+
+}

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

@@ -0,0 +1,4 @@
+/**
+ * 回款
+ */
+package cn.iocoder.yudao.module.crm.controller.admin.receivable;

+ 80 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/ReceivableBaseVO.java

@@ -0,0 +1,80 @@
+package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import javax.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+/**
+ * 回款管理 Base VO,提供给添加、修改、详细的子 VO 使用
+ * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
+ */
+@Data
+public class ReceivableBaseVO {
+
+    // TODO @liuhongfeng:部分缺少 example 的字段,要补充下;
+    // TODO @liuhongfeng:部分字段,需要必传,要写 requiredMode = Schema.RequiredMode.REQUIRED,以及对应的 validator 非空校验
+
+    @Schema(description = "回款编号")
+    private String no;
+
+    // TODO @liuhongfeng:中英文之间,有个空格,这样更干净;
+    @Schema(description = "回款计划ID", example = "31177")
+    private Long planId;
+
+    @Schema(description = "客户ID", example = "4963")
+    private Long customerId;
+
+    @Schema(description = "合同ID", example = "30305")
+    private Long contractId;
+
+    // TODO @liuhongfeng:这个字段,可以写个枚举,然后 InEnum 去校验下;
+    // TODO @liuhongfeng:这个字段,应该不是前端传递的噢,而是后端自己生成的
+    @Schema(description = "审批状态", example = "1")
+    private Integer checkStatus;
+
+    // TODO @liuhongfeng:这个字段,应该不是前端传递的噢,而是后端自己生成的,所以不适合放在 base 里面;
+    @Schema(description = "工作流编号", example = "16568")
+    private Long processInstanceId;
+
+    @Schema(description = "回款日期")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime returnTime;
+
+    @Schema(description = "回款方式", example = "2")
+    private String returnType;
+
+    // TODO @liuhongfeng:使用 Int 哈,分;
+    @Schema(description = "回款金额", example = "31859")
+    private BigDecimal price;
+
+    @Schema(description = "负责人", example = "22202")
+    private Long ownerUserId;
+
+    @Schema(description = "批次", example = "2539")
+    private Long batchId;
+
+    @Schema(description = "显示顺序")
+    private Integer sort;
+
+    // TODO @芋艿:这个字段在看看;dataScope、dataScopeDeptIds
+    @Schema(description = "数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)")
+    private Integer dataScope;
+
+    @Schema(description = "数据范围(指定部门数组)")
+    private String dataScopeDeptIds;
+
+    // TODO @liuhongfeng:这个字段,这个字段,应该不是前端传递的噢,而是后端自己生成的,所以不适合放在 base 里面;
+    @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "状态不能为空")
+    private Integer status;
+
+    @Schema(description = "备注", example = "随便")
+    private String remark;
+
+}

+ 14 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/ReceivableCreateReqVO.java

@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import javax.validation.constraints.*;
+
+@Schema(description = "管理后台 - CRM 回款创建 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ReceivableCreateReqVO extends ReceivableBaseVO {
+
+}

+ 80 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/ReceivableExcelVO.java

@@ -0,0 +1,80 @@
+package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
+import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
+
+
+/**
+ * CRM 回款管理 Excel VO
+ *
+ * @author 赤焰
+ */
+@Data
+public class ReceivableExcelVO {
+
+    @ExcelProperty("ID")
+    private Long id;
+
+    @ExcelProperty("回款编号")
+    private String no;
+
+    @ExcelProperty("回款计划ID")
+    private Long planId;
+
+    @ExcelProperty("客户ID")
+    private Long customerId;
+
+    @ExcelProperty("合同ID")
+    private Long contractId;
+
+    @ExcelProperty(value = "审批状态", converter = DictConvert.class)
+    @DictFormat("crm_receivable_check_status") // TODO 代码优化:建议设置到对应的 XXXDictTypeConstants 枚举类中
+    private Integer checkStatus;
+
+    @ExcelProperty("工作流编号")
+    private Long processInstanceId;
+
+    @ExcelProperty("回款日期")
+    private LocalDateTime returnTime;
+
+    @ExcelProperty("回款方式")
+    private String returnType;
+
+    @ExcelProperty("回款金额")
+    private BigDecimal price;
+
+    @ExcelProperty("负责人")
+    private Long ownerUserId;
+
+    @ExcelProperty("批次")
+    private Long batchId;
+
+    //@ExcelProperty("显示顺序")
+    //private Integer sort;
+
+    //@ExcelProperty("数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)")
+    //private Integer dataScope;
+
+    //@ExcelProperty("数据范围(指定部门数组)")
+    //private String dataScopeDeptIds;
+
+    @ExcelProperty(value = "状态", converter = DictConvert.class)
+    @DictFormat("common_status") // TODO 代码优化:建议设置到对应的 XXXDictTypeConstants 枚举类中
+    private Integer status;
+
+    @ExcelProperty("备注")
+    private String remark;
+
+    @ExcelProperty("创建时间")
+    private LocalDateTime createTime;
+
+}

+ 72 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/ReceivableExportReqVO.java

@@ -0,0 +1,72 @@
+package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+/**
+ * @author 赤焰
+ */
+@Schema(description = "管理后台 - CRM 回款 Excel 导出 Request VO,参数和 ReceivablePageReqVO 是一致的")
+@Data
+public class ReceivableExportReqVO {
+
+    @Schema(description = "回款编号")
+    private String no;
+
+    @Schema(description = "回款计划ID", example = "31177")
+    private Long planId;
+
+    @Schema(description = "客户ID", example = "4963")
+    private Long customerId;
+
+    @Schema(description = "合同ID", example = "30305")
+    private Long contractId;
+
+    @Schema(description = "审批状态", example = "1")
+    private Integer checkStatus;
+
+    @Schema(description = "工作流编号", example = "16568")
+    private Long processInstanceId;
+
+    @Schema(description = "回款日期")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] returnTime;
+
+    @Schema(description = "回款方式", example = "2")
+    private String returnType;
+
+    @Schema(description = "回款金额", example = "31859")
+    private BigDecimal price;
+
+    @Schema(description = "负责人", example = "22202")
+    private Long ownerUserId;
+
+    @Schema(description = "批次", example = "2539")
+    private Long batchId;
+
+    @Schema(description = "显示顺序")
+    private Integer sort;
+
+    @Schema(description = "数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)")
+    private Integer dataScope;
+
+    @Schema(description = "数据范围(指定部门数组)")
+    private String dataScopeDeptIds;
+
+    @Schema(description = "状态", example = "1")
+    private Integer status;
+
+    @Schema(description = "备注", example = "随便")
+    private String remark;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}

+ 75 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/ReceivablePageReqVO.java

@@ -0,0 +1,75 @@
+package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo;
+
+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.math.BigDecimal;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - CRM 回款分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ReceivablePageReqVO extends PageParam {
+
+    // TODO @liuhongfeng:目前就使用 no 检索即可;
+    @Schema(description = "回款编号")
+    private String no;
+
+    @Schema(description = "回款计划ID", example = "31177")
+    private Long planId;
+
+    @Schema(description = "客户ID", example = "4963")
+    private Long customerId;
+
+    @Schema(description = "合同ID", example = "30305")
+    private Long contractId;
+
+    @Schema(description = "审批状态", example = "1")
+    private Integer checkStatus;
+
+    @Schema(description = "工作流编号", example = "16568")
+    private Long processInstanceId;
+
+    @Schema(description = "回款日期")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] returnTime;
+
+    @Schema(description = "回款方式", example = "2")
+    private String returnType;
+
+    @Schema(description = "回款金额", example = "31859")
+    private BigDecimal price;
+
+    @Schema(description = "负责人", example = "22202")
+    private Long ownerUserId;
+
+    @Schema(description = "批次", example = "2539")
+    private Long batchId;
+
+    @Schema(description = "显示顺序")
+    private Integer sort;
+
+    @Schema(description = "数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)")
+    private Integer dataScope;
+
+    @Schema(description = "数据范围(指定部门数组)")
+    private String dataScopeDeptIds;
+
+    @Schema(description = "状态", example = "1")
+    private Integer status;
+
+    @Schema(description = "备注", example = "随便")
+    private String remark;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}

+ 72 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/ReceivablePlanBaseVO.java

@@ -0,0 +1,72 @@
+package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+/**
+ * 回款计划 Base VO,提供给添加、修改、详细的子 VO 使用
+ * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
+ */
+@Data
+public class ReceivablePlanBaseVO {
+
+    // TODO 芋艿:这个字段,在想想命名;
+    @Schema(description = "期数")
+    private Long indexNo;
+
+    // TODO @liuhongfeng:中英文之间,有个空格,这样更干净;
+    @Schema(description = "回款ID", example = "19852")
+    private Long receivableId;
+
+    @Schema(description = "完成状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
+    //@NotNull(message = "完成状态不能为空")
+    private Integer status;
+
+    // TODO @liuhongfeng:这个字段,可以写个枚举,然后 InEnum 去校验下;
+    // TODO @liuhongfeng:这个字段,应该不是前端传递的噢,而是后端自己生成的
+    @Schema(description = "审批状态", example = "1")
+    private String checkStatus;
+
+    // TODO @liuhongfeng:这个字段,应该不是前端传递的噢,而是后端自己生成的,所以不适合放在 base 里面;
+    @Schema(description = "工作流编号", example = "8909")
+    private Long processInstanceId;
+
+    // TODO @liuhongfeng:使用 Int 哈,分;
+    @Schema(description = "计划回款金额", example = "29675")
+    private BigDecimal price;
+
+    @Schema(description = "计划回款日期")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime returnTime;
+
+    // TODO @liuhongfeng:这个字段,Integer
+    @Schema(description = "提前几天提醒")
+    private Long remindDays;
+
+    @Schema(description = "提醒日期")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime remindTime;
+
+    @Schema(description = "客户ID", example = "18026")
+    private Long customerId;
+
+    @Schema(description = "合同ID", example = "3473")
+    private Long contractId;
+
+    // TODO @liuhongfeng:这个字段,应该不是前端传递的噢,而是后端自己生成的,所以不适合放在 base 里面;
+    @Schema(description = "负责人", example = "17828")
+    private Long ownerUserId;
+
+    @Schema(description = "显示顺序")
+    private Integer sort;
+
+    @Schema(description = "备注", example = "随便")
+    private String remark;
+
+}

+ 14 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/ReceivablePlanCreateReqVO.java

@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import javax.validation.constraints.*;
+
+@Schema(description = "管理后台 - CRM 回款计划创建 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ReceivablePlanCreateReqVO extends ReceivablePlanBaseVO {
+
+}

+ 71 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/ReceivablePlanExcelVO.java

@@ -0,0 +1,71 @@
+package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo;
+
+import lombok.*;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
+import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
+
+
+/**
+ * CRM 回款计划 Excel VO
+ *
+ * @author 芋道源码
+ */
+@Data
+public class ReceivablePlanExcelVO {
+
+    @ExcelProperty("ID")
+    private Long id;
+
+    @ExcelProperty("期数")
+    private Long indexNo;
+
+    @ExcelProperty("回款ID")
+    private Long receivableId;
+
+    @ExcelProperty(value = "完成状态", converter = DictConvert.class)
+    @DictFormat("common_status") // TODO 代码优化:建议设置到对应的 XXXDictTypeConstants 枚举类中
+    private Integer status;
+
+    @ExcelProperty(value = "审批状态", converter = DictConvert.class)
+    @DictFormat("crm_receivable_check_status") // TODO 代码优化:建议设置到对应的 XXXDictTypeConstants 枚举类中
+    private String checkStatus;
+
+    //@ExcelProperty("工作流编号")
+    //private Long processInstanceId;
+
+    @ExcelProperty("计划回款金额")
+    private BigDecimal price;
+
+    @ExcelProperty("计划回款日期")
+    private LocalDateTime returnTime;
+
+    @ExcelProperty("提前几天提醒")
+    private Long remindDays;
+
+    @ExcelProperty("提醒日期")
+    private LocalDateTime remindTime;
+
+    @ExcelProperty("客户ID")
+    private Long customerId;
+
+    @ExcelProperty("合同ID")
+    private Long contractId;
+
+    @ExcelProperty("负责人")
+    private Long ownerUserId;
+
+    //@ExcelProperty("显示顺序")
+    //private Integer sort;
+
+    @ExcelProperty("备注")
+    private String remark;
+
+    @ExcelProperty("创建时间")
+    private LocalDateTime createTime;
+
+}

+ 52 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/ReceivablePlanExportReqVO.java

@@ -0,0 +1,52 @@
+package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import java.time.LocalDateTime;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - CRM 回款计划 Excel 导出 Request VO,参数和 ReceivablePlanPageReqVO 是一致的")
+@Data
+public class ReceivablePlanExportReqVO {
+
+    @Schema(description = "期数")
+    private Long indexNo;
+
+    @Schema(description = "完成状态", example = "2")
+    private Integer status;
+
+    @Schema(description = "审批状态", example = "1")
+    private String checkStatus;
+
+    @Schema(description = "计划回款日期")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] returnTime;
+
+    @Schema(description = "提前几天提醒")
+    private Long remindDays;
+
+    @Schema(description = "提醒日期")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] remindTime;
+
+    @Schema(description = "客户ID", example = "18026")
+    private Long customerId;
+
+    @Schema(description = "合同ID", example = "3473")
+    private Long contractId;
+
+    @Schema(description = "负责人", example = "17828")
+    private Long ownerUserId;
+
+    @Schema(description = "备注", example = "随便")
+    private String remark;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}

+ 58 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/ReceivablePlanPageReqVO.java

@@ -0,0 +1,58 @@
+package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo;
+
+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 = "管理后台 - CRM 回款计划分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ReceivablePlanPageReqVO extends PageParam {
+
+    // TODO 芋艿:筛选字段,需要去掉几个,在想想;
+
+    @Schema(description = "期数")
+    private Long indexNo;
+
+    @Schema(description = "完成状态", example = "2")
+    private Integer status;
+
+    @Schema(description = "审批状态", example = "1")
+    private String checkStatus;
+
+    @Schema(description = "计划回款日期")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] returnTime;
+
+    @Schema(description = "提前几天提醒")
+    private Long remindDays;
+
+    @Schema(description = "提醒日期")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] remindTime;
+
+    @Schema(description = "客户ID", example = "18026")
+    private Long customerId;
+
+    @Schema(description = "合同ID", example = "3473")
+    private Long contractId;
+
+    @Schema(description = "负责人", example = "17828")
+    private Long ownerUserId;
+
+    @Schema(description = "备注", example = "随便")
+    private String remark;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}

+ 19 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/ReceivablePlanRespVO.java

@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - CRM 回款计划 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ReceivablePlanRespVO extends ReceivablePlanBaseVO {
+
+    @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "25153")
+    private Long id;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDateTime createTime;
+
+}

+ 18 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/ReceivablePlanUpdateReqVO.java

@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+
+@Schema(description = "管理后台 - CRM 回款计划更新 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ReceivablePlanUpdateReqVO extends ReceivablePlanBaseVO {
+
+    @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "25153")
+    @NotNull(message = "ID不能为空")
+    private Long id;
+
+}

+ 19 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/ReceivableRespVO.java

@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - CRM 回款 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ReceivableRespVO extends ReceivableBaseVO {
+
+    @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "25787")
+    private Long id;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDateTime createTime;
+
+}

+ 18 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/ReceivableUpdateReqVO.java

@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+
+@Schema(description = "管理后台 - CRM 回款更新 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ReceivableUpdateReqVO extends ReceivableBaseVO {
+
+    @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "25787")
+    @NotNull(message = "ID不能为空")
+    private Long id;
+
+}

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff