From 0e55c4da6d4b8cb8490f41b1b42c781916e35664 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 17 Feb 2024 23:25:41 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20=E5=A2=9E=E5=8A=A0=20CRM=20?= =?UTF-8?q?=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 1 + .../src/main/resources/application.yaml | 10 + yudao-module-crm/pom.xml | 25 + yudao-module-crm/yudao-module-crm-api/pom.xml | 33 + .../yudao/module/crm/api/package-info.java | 4 + .../yudao/module/crm/enums/ApiConstants.java | 23 + .../module/crm/enums/DictTypeConstants.java | 18 + .../module/crm/enums/ErrorCodeConstants.java | 97 +++ .../module/crm/enums/LogRecordConstants.java | 139 +++++ .../crm/enums/business/CrmBizEndStatus.java | 55 ++ .../crm/enums/common/CrmAuditStatusEnum.java | 34 ++ .../crm/enums/common/CrmBizTypeEnum.java | 52 ++ .../crm/enums/common/CrmSceneTypeEnum.java | 51 ++ .../enums/customer/CrmCustomerLevelEnum.java | 38 ++ .../CrmCustomerLimitConfigTypeEnum.java | 52 ++ .../permission/CrmPermissionLevelEnum.java | 51 ++ .../permission/CrmPermissionRoleCodeEnum.java | 27 + .../enums/product/CrmProductStatusEnum.java | 38 ++ yudao-module-crm/yudao-module-crm-biz/pom.xml | 141 +++++ .../module/crm/CrmServerApplication.java | 30 + .../yudao/module/crm/api/package-info.java | 4 + .../admin/backlog/CrmBacklogController.java | 41 ++ .../backlog/vo/CrmTodayCustomerPageReqVO.java | 37 ++ .../admin/bi/CrmBiRankController.http | 9 + .../admin/bi/CrmBiRankController.java | 87 +++ .../admin/bi/vo/CrmBiRanKRespVO.java | 29 + .../admin/bi/vo/CrmBiRankReqVO.java | 35 ++ .../admin/business/CrmBusinessController.http | 32 + .../admin/business/CrmBusinessController.java | 177 ++++++ .../CrmBusinessStatusTypeController.java | 141 +++++ .../vo/business/CrmBusinessExcelVO.java | 75 +++ .../vo/business/CrmBusinessPageReqVO.java | 30 + .../vo/business/CrmBusinessRespVO.java | 69 +++ .../vo/business/CrmBusinessSaveReqVO.java | 103 ++++ .../vo/business/CrmBusinessTransferReqVO.java | 31 + .../vo/status/CrmBusinessStatusPageReqVO.java | 15 + .../vo/status/CrmBusinessStatusQueryVO.java | 19 + .../vo/status/CrmBusinessStatusRespVO.java | 33 + .../vo/status/CrmBusinessStatusSaveReqVO.java | 32 + .../type/CrmBusinessStatusTypePageReqVO.java | 15 + .../vo/type/CrmBusinessStatusTypeQueryVO.java | 19 + .../vo/type/CrmBusinessStatusTypeRespVO.java | 44 ++ .../type/CrmBusinessStatusTypeSaveReqVO.java | 29 + .../admin/clue/CrmClueController.java | 107 ++++ .../admin/clue/vo/CrmCluePageReqVO.java | 42 ++ .../admin/clue/vo/CrmClueRespVO.java | 115 ++++ .../admin/clue/vo/CrmClueSaveReqVO.java | 105 ++++ .../admin/clue/vo/CrmClueTransferReqVO.java | 25 + .../admin/clue/vo/CrmClueTranslateReqVO.java | 17 + .../admin/contact/CrmContactController.java | 204 +++++++ .../contact/vo/CrmContactBusinessReqVO.java | 22 + .../admin/contact/vo/CrmContactPageReqVO.java | 42 ++ .../admin/contact/vo/CrmContactRespVO.java | 112 ++++ .../admin/contact/vo/CrmContactSaveReqVO.java | 103 ++++ .../contact/vo/CrmContactTransferReqVO.java | 32 + .../admin/contract/CrmContractController.java | 187 ++++++ .../contract/vo/CrmContractPageReqVO.java | 50 ++ .../admin/contract/vo/CrmContractRespVO.java | 165 +++++ .../contract/vo/CrmContractSaveReqVO.java | 115 ++++ .../contract/vo/CrmContractTransferReqVO.java | 26 + .../admin/customer/CrmCustomerController.http | 16 + .../admin/customer/CrmCustomerController.java | 272 +++++++++ .../CrmCustomerLimitConfigController.java | 97 +++ .../CrmCustomerPoolConfigController.java | 44 ++ .../vo/CrmCustomerDistributeReqVO.java | 22 + .../customer/vo/CrmCustomerImportExcelVO.java | 71 +++ .../customer/vo/CrmCustomerImportReqVO.java | 25 + .../customer/vo/CrmCustomerImportRespVO.java | 24 + .../customer/vo/CrmCustomerLockReqVO.java | 16 + .../customer/vo/CrmCustomerPageReqVO.java | 39 ++ .../admin/customer/vo/CrmCustomerRespVO.java | 138 +++++ .../customer/vo/CrmCustomerSaveReqVO.java | 106 ++++ .../customer/vo/CrmCustomerTransferReqVO.java | 31 + .../CrmCustomerLimitConfigPageReqVO.java | 18 + .../CrmCustomerLimitConfigRespVO.java | 42 ++ .../CrmCustomerLimitConfigSaveReqVO.java | 41 ++ .../CrmCustomerPoolConfigRespVO.java | 27 + .../CrmCustomerPoolConfigSaveReqVO.java | 65 ++ .../followup/CrmFollowUpRecordController.java | 92 +++ .../vo/CrmFollowUpRecordPageReqVO.java | 21 + .../followup/vo/CrmFollowUpRecordRespVO.java | 55 ++ .../vo/CrmFollowUpRecordSaveReqVO.java | 48 ++ .../operatelog/CrmOperateLogController.java | 64 ++ .../operatelog/vo/CrmOperateLogPageReqVO.java | 27 + .../operatelog/vo/CrmOperateLogV2RespVO.java | 44 ++ .../permission/CrmPermissionController.http | 32 + .../permission/CrmPermissionController.java | 116 ++++ .../permission/vo/CrmPermissionBaseVO.java | 38 ++ .../vo/CrmPermissionCreateReqVO.java | 14 + .../permission/vo/CrmPermissionRespVO.java | 28 + .../vo/CrmPermissionUpdateReqVO.java | 34 ++ .../product/CrmProductCategoryController.java | 73 +++ .../admin/product/CrmProductController.java | 126 ++++ .../CrmProductCategoryCreateReqVO.java | 25 + .../category/CrmProductCategoryListReqVO.java | 22 + .../vo/category/CrmProductCategoryRespVO.java | 24 + .../vo/product/CrmProductPageReqVO.java | 21 + .../product/vo/product/CrmProductRespVO.java | 74 +++ .../vo/product/CrmProductSaveReqVO.java | 54 ++ .../receivable/CrmReceivableController.java | 147 +++++ .../CrmReceivablePlanController.java | 156 +++++ .../vo/plan/CrmReceivablePlanBaseVO.java | 54 ++ .../vo/plan/CrmReceivablePlanCreateReqVO.java | 12 + .../vo/plan/CrmReceivablePlanPageReqVO.java | 44 ++ .../vo/plan/CrmReceivablePlanRespVO.java | 40 ++ .../vo/plan/CrmReceivablePlanUpdateReqVO.java | 18 + .../vo/receivable/CrmReceivableBaseVO.java | 61 ++ .../receivable/CrmReceivableCreateReqVO.java | 12 + .../vo/receivable/CrmReceivablePageReqVO.java | 35 ++ .../vo/receivable/CrmReceivableRespVO.java | 38 ++ .../receivable/CrmReceivableUpdateReqVO.java | 18 + .../crm/controller/app/package-info.java | 4 + .../module/crm/controller/package-info.java | 6 + .../convert/business/CrmBusinessConvert.java | 57 ++ .../business/CrmBusinessStatusConvert.java | 25 + .../CrmBusinessStatusTypeConvert.java | 44 ++ .../crm/convert/clue/CrmClueConvert.java | 22 + .../convert/contact/CrmContactConvert.java | 77 +++ .../convert/contract/CrmContractConvert.java | 70 +++ .../convert/customer/CrmCustomerConvert.java | 66 ++ .../CrmCustomerLimitConfigConvert.java | 42 ++ .../module/crm/convert/package-info.java | 6 + .../permission/CrmPermissionConvert.java | 56 ++ .../convert/product/CrmProductConvert.java | 46 ++ .../receivable/CrmReceivableConvert.java | 52 ++ .../receivable/CrmReceivablePlanConvert.java | 56 ++ ...‹é“ Spring Boot å¯¹è±¡è½¬æ¢ MapStruct 入门》.md | 1 + .../dataobject/business/CrmBusinessDO.java | 108 ++++ .../business/CrmBusinessProductDO.java | 63 ++ .../business/CrmBusinessStatusDO.java | 46 ++ .../business/CrmBusinessStatusTypeDO.java | 51 ++ .../crm/dal/dataobject/clue/CrmClueDO.java | 116 ++++ .../crm/dal/dataobject/clue/package-info.java | 4 + .../contact/CrmContactBusinessDO.java | 43 ++ .../dal/dataobject/contact/CrmContactDO.java | 117 ++++ .../dal/dataobject/contact/package-info.java | 4 + .../dataobject/contract/CrmContractDO.java | 121 ++++ .../contract/CrmContractProductDO.java | 65 ++ .../dataobject/customer/CrmCustomerDO.java | 135 +++++ .../customer/CrmCustomerLimitConfigDO.java | 61 ++ .../customer/CrmCustomerPoolConfigDO.java | 52 ++ .../followup/CrmFollowUpRecordDO.java | 97 +++ .../permission/CrmPermissionDO.java | 59 ++ .../product/CrmProductCategoryDO.java | 49 ++ .../dal/dataobject/product/CrmProductDO.java | 72 +++ .../dal/dataobject/product/package-info.java | 4 + .../receivable/CrmReceivableDO.java | 96 +++ .../receivable/CrmReceivablePlanDO.java | 88 +++ .../dataobject/receivable/package-info.java | 4 + .../crm/dal/mysql/bi/CrmBiRankingMapper.java | 81 +++ .../dal/mysql/business/CrmBusinessMapper.java | 66 ++ .../business/CrmBusinessProductMapper.java | 33 + .../business/CrmBusinessStatusMapper.java | 37 ++ .../business/CrmBusinessStatusTypeMapper.java | 44 ++ .../crm/dal/mysql/business/package-info.java | 4 + .../crm/dal/mysql/clue/CrmClueMapper.java | 56 ++ .../crm/dal/mysql/clue/package-info.java | 4 + .../dal/mysql/contact/CrmContactMapper.java | 75 +++ .../crm/dal/mysql/contact/package-info.java | 4 + .../CrmContactBusinessMapper.java | 34 ++ .../dal/mysql/contract/CrmContractMapper.java | 90 +++ .../contract/CrmContractProductMapper.java | 32 + .../CrmCustomerLimitConfigMapper.java | 37 ++ .../dal/mysql/customer/CrmCustomerMapper.java | 150 +++++ .../customer/CrmCustomerPoolConfigMapper.java | 20 + .../followup/CrmFollowUpRecordMapper.java | 40 ++ .../mysql/permission/CrmPermissionMapper.java | 74 +++ .../dal/mysql/permission/package-info.java | 1 + .../product/CrmProductCategoryMapper.java | 34 ++ .../dal/mysql/product/CrmProductMapper.java | 37 ++ .../mysql/receivable/CrmReceivableMapper.java | 62 ++ .../receivable/CrmReceivablePlanMapper.java | 81 +++ .../core/CrmBusinessParseFunction.java | 44 ++ .../core/CrmContactParseFunction.java | 44 ++ .../core/CrmContractParseFunction.java | 44 ++ .../CrmCustomerIndustryParseFunction.java | 40 ++ .../core/CrmCustomerLevelParseFunction.java | 40 ++ .../core/CrmCustomerParseFunction.java | 44 ++ .../core/CrmCustomerSourceParseFunction.java | 40 ++ .../core/CrmProductStatusParseFunction.java | 39 ++ .../core/CrmProductUnitParseFunction.java | 39 ++ .../core/SysAdminUserParseFunction.java | 50 ++ .../operatelog/core/SysAreaParseFunction.java | 38 ++ .../core/SysBooleanParseFunction.java | 39 ++ .../operatelog/core/SysDeptParseFunction.java | 45 ++ .../operatelog/core/SysSexParseFunction.java | 39 ++ .../framework/operatelog/package-info.java | 1 + .../module/crm/framework/package-info.java | 6 + .../core/annotations/CrmPermission.java | 46 ++ .../core/aop/CrmPermissionAspect.java | 130 ++++ .../permission/core/package-info.java | 1 + .../core/util/CrmPermissionUtils.java | 40 ++ .../framework/permission/package-info.java | 1 + .../rpc/config/RpcConfiguration.java | 14 + .../crm/framework/rpc/package-info.java | 4 + .../config/SecurityConfiguration.java | 37 ++ .../framework/security/core/package-info.java | 4 + .../customer/CrmCustomerAutoPutPoolJob.java | 27 + .../yudao/module/crm/job/package-info.java | 4 + .../yudao/module/crm/package-info.java | 10 + .../crm/service/bi/CrmBiRankingService.java | 80 +++ .../service/bi/CrmBiRankingServiceImpl.java | 134 ++++ .../service/business/CrmBusinessService.java | 132 ++++ .../business/CrmBusinessServiceImpl.java | 298 +++++++++ .../business/CrmBusinessStatusService.java | 77 +++ .../CrmBusinessStatusServiceImpl.java | 86 +++ .../CrmBusinessStatusTypeService.java | 75 +++ .../CrmBusinessStatusTypeServiceImpl.java | 132 ++++ .../bo/CrmBusinessUpdateProductReqBO.java | 49 ++ .../crm/service/clue/CrmClueService.java | 93 +++ .../crm/service/clue/CrmClueServiceImpl.java | 279 +++++++++ .../contact/CrmContactBusinessService.java | 52 ++ .../CrmContactBusinessServiceImpl.java | 99 +++ .../service/contact/CrmContactService.java | 137 +++++ .../contact/CrmContactServiceImpl.java | 256 ++++++++ .../service/contract/CrmContractService.java | 146 +++++ .../contract/CrmContractServiceImpl.java | 363 +++++++++++ .../CrmCustomerLimitConfigService.java | 64 ++ .../CrmCustomerLimitConfigServiceImpl.java | 124 ++++ .../CrmCustomerPoolConfigService.java | 29 + .../CrmCustomerPoolConfigServiceImpl.java | 65 ++ .../service/customer/CrmCustomerService.java | 146 +++++ .../customer/CrmCustomerServiceImpl.java | 572 ++++++++++++++++++ .../customer/bo/CrmCustomerCreateReqBO.java | 125 ++++ .../followup/CrmFollowUpRecordService.java | 76 +++ .../CrmFollowUpRecordServiceImpl.java | 158 +++++ .../followup/bo/CrmFollowUpCreateReqBO.java | 78 +++ .../followup/bo/CrmUpdateFollowUpReqBO.java | 34 ++ .../service/message/CrmBacklogService.java | 23 + .../message/CrmBacklogServiceImpl.java | 28 + .../permission/CrmPermissionService.java | 122 ++++ .../permission/CrmPermissionServiceImpl.java | 222 +++++++ .../bo/CrmPermissionCreateReqBO.java | 43 ++ .../bo/CrmPermissionTransferReqBO.java | 49 ++ .../product/CrmProductCategoryService.java | 64 ++ .../CrmProductCategoryServiceImpl.java | 138 +++++ .../service/product/CrmProductService.java | 81 +++ .../product/CrmProductServiceImpl.java | 166 +++++ .../receivable/CrmReceivablePlanService.java | 80 +++ .../CrmReceivablePlanServiceImpl.java | 166 +++++ .../receivable/CrmReceivableService.java | 81 +++ .../receivable/CrmReceivableServiceImpl.java | 186 ++++++ .../module/crm/util/CrmQueryWrapperUtils.java | 113 ++++ .../src/main/resources/application-dev.yaml | 104 ++++ .../src/main/resources/application-local.yaml | 130 ++++ .../src/main/resources/application.yaml | 105 ++++ .../src/main/resources/bootstrap-local.yaml | 23 + .../src/main/resources/bootstrap.yaml | 14 + .../src/main/resources/logback-spring.xml | 76 +++ .../mapper/bi/CrmBiRankingMapper.xml | 126 ++++ .../BusinessStatusTypeServiceImplTest.java | 119 ++++ .../business/CrmBusinessServiceImplTest.java | 181 ++++++ .../service/clue/CrmClueServiceImplTest.java | 207 +++++++ .../contract/ContractServiceImplTest.java | 135 +++++ .../customer/CrmCustomerServiceImplTest.java | 141 +++++ ...CrmCustomerLimitConfigServiceImplTest.java | 119 ++++ .../CrmCrmReceivablePlanServiceImplTest.java | 137 +++++ .../CrmCrmReceivableServiceImplTest.java | 143 +++++ .../test/resources/application-unit-test.yaml | 51 ++ .../src/test/resources/logback.xml | 4 + .../src/test/resources/sql/clean.sql | 11 + .../src/test/resources/sql/create_tables.sql | 162 +++++ .../config/SecurityConfiguration.java | 6 +- .../yudao/module/system/api/dept/DeptApi.java | 7 +- .../yudao/module/system/api/dept/PostApi.java | 20 + .../system/api/dept/dto/PostRespDTO.java | 30 + .../module/system/api/user/AdminUserApi.java | 7 +- .../module/system/api/dept/DeptApiImpl.java | 7 + .../module/system/api/dept/PostApiImpl.java | 10 + .../system/api/user/AdminUserApiImpl.java | 42 +- 270 files changed, 17975 insertions(+), 9 deletions(-) create mode 100644 yudao-module-crm/pom.xml create mode 100644 yudao-module-crm/yudao-module-crm-api/pom.xml create mode 100644 yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/api/package-info.java create mode 100644 yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ApiConstants.java create mode 100644 yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/DictTypeConstants.java create mode 100644 yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java create mode 100644 yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java create mode 100644 yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/business/CrmBizEndStatus.java create mode 100644 yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmAuditStatusEnum.java create mode 100644 yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmBizTypeEnum.java create mode 100644 yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmSceneTypeEnum.java create mode 100644 yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/customer/CrmCustomerLevelEnum.java create mode 100644 yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/customer/CrmCustomerLimitConfigTypeEnum.java create mode 100644 yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/permission/CrmPermissionLevelEnum.java create mode 100644 yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/permission/CrmPermissionRoleCodeEnum.java create mode 100644 yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/product/CrmProductStatusEnum.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/pom.xml create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/CrmServerApplication.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/api/package-info.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/backlog/CrmBacklogController.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/backlog/vo/CrmTodayCustomerPageReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/CrmBiRankController.http create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/CrmBiRankController.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/vo/CrmBiRanKRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/vo/CrmBiRankReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.http create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessStatusTypeController.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessExcelVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessPageReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessSaveReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessTransferReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/status/CrmBusinessStatusPageReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/status/CrmBusinessStatusQueryVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/status/CrmBusinessStatusRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/status/CrmBusinessStatusSaveReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/type/CrmBusinessStatusTypePageReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/type/CrmBusinessStatusTypeQueryVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/type/CrmBusinessStatusTypeRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/type/CrmBusinessStatusTypeSaveReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmCluePageReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueSaveReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueTransferReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueTranslateReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/CrmContactController.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactBusinessReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactPageReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactSaveReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactTransferReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractPageReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractSaveReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractTransferReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.http create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerLimitConfigController.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerPoolConfigController.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerDistributeReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerImportExcelVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerImportReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerImportRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerLockReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerPageReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerSaveReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerTransferReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/limitconfig/CrmCustomerLimitConfigPageReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/limitconfig/CrmCustomerLimitConfigRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/limitconfig/CrmCustomerLimitConfigSaveReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/poolconfig/CrmCustomerPoolConfigRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/poolconfig/CrmCustomerPoolConfigSaveReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/CrmFollowUpRecordController.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordPageReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordSaveReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/CrmOperateLogController.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogPageReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogV2RespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.http create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionBaseVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionCreateReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionUpdateReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/CrmProductCategoryController.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/CrmProductController.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/category/CrmProductCategoryCreateReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/category/CrmProductCategoryListReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/category/CrmProductCategoryRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/product/CrmProductPageReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/product/CrmProductRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/product/CrmProductSaveReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanBaseVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanCreateReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanPageReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanUpdateReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableBaseVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableCreateReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivablePageReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableUpdateReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/app/package-info.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/package-info.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessConvert.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessStatusConvert.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessStatusTypeConvert.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/clue/CrmClueConvert.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contact/CrmContactConvert.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/CrmContractConvert.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/customer/CrmCustomerConvert.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/customer/CrmCustomerLimitConfigConvert.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/package-info.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/permission/CrmPermissionConvert.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/product/CrmProductConvert.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivableConvert.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivablePlanConvert.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/ã€ŠèŠ‹é“ Spring Boot å¯¹è±¡è½¬æ¢ MapStruct 入门》.md create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessDO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessProductDO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessStatusDO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessStatusTypeDO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/clue/CrmClueDO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/clue/package-info.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contact/CrmContactBusinessDO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contact/CrmContactDO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contact/package-info.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractDO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractProductDO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerDO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerLimitConfigDO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerPoolConfigDO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/followup/CrmFollowUpRecordDO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/permission/CrmPermissionDO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/product/CrmProductCategoryDO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/product/CrmProductDO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/product/package-info.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivableDO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivablePlanDO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/package-info.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/bi/CrmBiRankingMapper.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessProductMapper.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessStatusMapper.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessStatusTypeMapper.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/package-info.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/clue/CrmClueMapper.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/clue/package-info.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/package-info.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contactbusinesslink/CrmContactBusinessMapper.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractProductMapper.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerLimitConfigMapper.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerPoolConfigMapper.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/followup/CrmFollowUpRecordMapper.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/permission/CrmPermissionMapper.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/permission/package-info.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/product/CrmProductCategoryMapper.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/product/CrmProductMapper.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmBusinessParseFunction.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmContactParseFunction.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmContractParseFunction.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmCustomerIndustryParseFunction.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmCustomerLevelParseFunction.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmCustomerParseFunction.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmCustomerSourceParseFunction.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmProductStatusParseFunction.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmProductUnitParseFunction.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/SysAdminUserParseFunction.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/SysAreaParseFunction.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/SysBooleanParseFunction.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/SysDeptParseFunction.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/SysSexParseFunction.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/package-info.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/package-info.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/annotations/CrmPermission.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/aop/CrmPermissionAspect.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/package-info.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/util/CrmPermissionUtils.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/package-info.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/rpc/config/RpcConfiguration.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/rpc/package-info.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/security/config/SecurityConfiguration.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/security/core/package-info.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/job/customer/CrmCustomerAutoPutPoolJob.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/job/package-info.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/package-info.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/bi/CrmBiRankingService.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/bi/CrmBiRankingServiceImpl.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessService.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusService.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusServiceImpl.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusTypeService.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusTypeServiceImpl.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/bo/CrmBusinessUpdateProductReqBO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueService.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactBusinessService.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactBusinessServiceImpl.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactService.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerLimitConfigService.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerLimitConfigServiceImpl.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerPoolConfigService.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerPoolConfigServiceImpl.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/bo/CrmCustomerCreateReqBO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordService.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordServiceImpl.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/bo/CrmFollowUpCreateReqBO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/bo/CrmUpdateFollowUpReqBO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/message/CrmBacklogService.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/message/CrmBacklogServiceImpl.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionService.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImpl.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/bo/CrmPermissionCreateReqBO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/bo/CrmPermissionTransferReqBO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductCategoryService.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductCategoryServiceImpl.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductService.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductServiceImpl.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanService.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmQueryWrapperUtils.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/resources/application-dev.yaml create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/resources/application-local.yaml create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/resources/application.yaml create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/resources/bootstrap-local.yaml create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/resources/bootstrap.yaml create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/resources/logback-spring.xml create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/bi/CrmBiRankingMapper.xml create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/business/BusinessStatusTypeServiceImplTest.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImplTest.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImplTest.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/contract/ContractServiceImplTest.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImplTest.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/customerlimitconfig/CrmCustomerLimitConfigServiceImplTest.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/receivable/CrmCrmReceivablePlanServiceImplTest.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/receivable/CrmCrmReceivableServiceImplTest.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/test/resources/application-unit-test.yaml create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/test/resources/logback.xml create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/test/resources/sql/clean.sql create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/test/resources/sql/create_tables.sql create mode 100644 yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/dto/PostRespDTO.java diff --git a/pom.xml b/pom.xml index 3f234c29d..6106623ac 100644 --- a/pom.xml +++ b/pom.xml @@ -20,6 +20,7 @@ yudao-module-report yudao-module-mp yudao-module-mall + yudao-module-crm yudao-module-erp diff --git a/yudao-gateway/src/main/resources/application.yaml b/yudao-gateway/src/main/resources/application.yaml index 87d9a4a9b..d20d17176 100644 --- a/yudao-gateway/src/main/resources/application.yaml +++ b/yudao-gateway/src/main/resources/application.yaml @@ -145,6 +145,13 @@ spring: - Path=/admin-api/erp/** filters: - RewritePath=/admin-api/erp/v3/api-docs, /v3/api-docs # é…置,ä¿è¯è½¬å‘到 /v3/api-docs + ## crm-server æœåŠ¡ + - id: crm-admin-api # è·¯ç”±çš„ç¼–å· + uri: grayLb://crm-server + predicates: # 断言,作为路由的匹é…æ¡ä»¶ï¼Œå¯¹åº” RouteDefinition 数组 + - Path=/admin-api/crm/** + filters: + - RewritePath=/admin-api/crm/v3/api-docs, /v3/api-docs # é…置,ä¿è¯è½¬å‘到 /v3/api-docs x-forwarded: prefix-enabled: false # é¿å… Swagger é‡å¤å¸¦ä¸Šé¢å¤–çš„ /admin-api/system å‰ç¼€ @@ -186,6 +193,9 @@ knife4j: - name: erp-server service-name: erp-server url: /admin-api/erp/v3/api-docs + - name: crm-server + service-name: crm-server + url: /admin-api/crm/v3/api-docs --- #################### 芋é“相关é…ç½® #################### diff --git a/yudao-module-crm/pom.xml b/yudao-module-crm/pom.xml new file mode 100644 index 000000000..56921c6fb --- /dev/null +++ b/yudao-module-crm/pom.xml @@ -0,0 +1,25 @@ + + + + cn.iocoder.cloud + yudao + ${revision} + + + yudao-module-crm-api + yudao-module-crm-biz + + 4.0.0 + yudao-module-crm + pom + + ${project.artifactId} + + crm 包下,客户关系管ç†ï¼ˆCustomer Relationship Management)。 + 例如说:客户ã€è”系人ã€å•†æœºã€åˆåŒã€å›žæ¬¾ç­‰ç­‰ + 商业智能 BI 模å—,包括:报表ã€å›¾è¡¨ã€æ•°æ®å¤§å±ç­‰ç­‰ + + + diff --git a/yudao-module-crm/yudao-module-crm-api/pom.xml b/yudao-module-crm/yudao-module-crm-api/pom.xml new file mode 100644 index 000000000..fc97b6759 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-api/pom.xml @@ -0,0 +1,33 @@ + + + + cn.iocoder.cloud + yudao-module-crm + ${revision} + + 4.0.0 + yudao-module-crm-api + jar + + ${project.artifactId} + + crm æ¨¡å— API,暴露给其它模å—调用 + + + + + cn.iocoder.cloud + yudao-common + + + + + org.springframework.boot + spring-boot-starter-validation + true + + + + diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/api/package-info.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/api/package-info.java new file mode 100644 index 000000000..c38bde7f5 --- /dev/null +++ b/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; diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ApiConstants.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ApiConstants.java new file mode 100644 index 000000000..544ebf9f4 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ApiConstants.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.crm.enums; + +import cn.iocoder.yudao.framework.common.enums.RpcConstants; + +/** + * API 相关的枚举 + * + * @author 芋é“æºç  + */ +public class ApiConstants { + + /** + * æœåŠ¡å + * + * 注æ„,需è¦ä¿è¯å’Œ spring.application.name ä¿æŒä¸€è‡´ + */ + public static final String NAME = "crm-server"; + + public static final String PREFIX = RpcConstants.RPC_API_PREFIX + "/crm"; + + public static final String VERSION = "1.0.0"; + +} diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/DictTypeConstants.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/DictTypeConstants.java new file mode 100644 index 000000000..22bb0b426 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/DictTypeConstants.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.module.crm.enums; + +/** + * CRM 字典类型的枚举类 + * + * @author 芋é“æºç  + */ +public interface DictTypeConstants { + + String CRM_CUSTOMER_INDUSTRY = "crm_customer_industry"; // CRM 客户所属行业 + String CRM_CUSTOMER_LEVEL = "crm_customer_level"; // CRM 客户等级 + String CRM_CUSTOMER_SOURCE = "crm_customer_source"; // CRM 客户æ¥æº + String CRM_AUDIT_STATUS = "crm_audit_status"; // CRM å®¡æ‰¹çŠ¶æ€ + String CRM_PRODUCT_UNIT = "crm_product_unit"; // CRM 产å“å•ä½ + String CRM_PRODUCT_STATUS = "crm_product_status"; // CRM 产å“çŠ¶æ€ + String CRM_FOLLOW_UP_TYPE = "crm_follow_up_type"; // CRM è·Ÿè¿›æ–¹å¼ + +} diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java new file mode 100644 index 000000000..d536d8a40 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java @@ -0,0 +1,97 @@ +package cn.iocoder.yudao.module.crm.enums; + +import cn.iocoder.yudao.framework.common.exception.ErrorCode; + +/** + * CRM 错误ç æžšä¸¾ç±» + *

+ * crm 系统,使用 1-020-000-000 段 + */ +public interface ErrorCodeConstants { + + // ========== åˆåŒç®¡ç† 1-020-000-000 ========== + ErrorCode CONTRACT_NOT_EXISTS = new ErrorCode(1_020_000_000, "åˆåŒä¸å­˜åœ¨"); + ErrorCode CONTRACT_UPDATE_FAIL_EDITING_PROHIBITED = new ErrorCode(1_020_000_001, "æ›´æ–°åˆåŒå¤±è´¥ï¼ŒåŽŸå› ï¼šç¦æ­¢ç¼–辑"); + ErrorCode CONTRACT_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_020_000_002, "åˆåŒæ交审核失败,原因:åˆåŒæ²¡å¤„在未æ交状æ€"); + + // ========== çº¿ç´¢ç®¡ç† 1-020-001-000 ========== + ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "线索ä¸å­˜åœ¨"); + ErrorCode CLUE_ANY_CLUE_NOT_EXISTS = new ErrorCode(1_020_001_001, "线索ã€{}】ä¸å­˜åœ¨"); + ErrorCode CLUE_ANY_CLUE_ALREADY_TRANSLATED = new ErrorCode(1_020_001_002, "线索ã€{}】已ç»è½¬åŒ–过了,请勿é‡å¤è½¬åŒ–"); + + // ========== å•†æœºç®¡ç† 1-020-002-000 ========== + ErrorCode BUSINESS_NOT_EXISTS = new ErrorCode(1_020_002_000, "商机ä¸å­˜åœ¨"); + ErrorCode BUSINESS_CONTRACT_EXISTS = new ErrorCode(1_020_002_001, "商机已关è”åˆåŒï¼Œä¸èƒ½åˆ é™¤"); + + // TODO @lilleo:商机状æ€ã€å•†æœºç±»åž‹ï¼Œéƒ½å•ç‹¬é”™è¯¯ç æ®µ + + + // ========== è”ç³»äººç®¡ç† 1-020-003-000 ========== + ErrorCode CONTACT_NOT_EXISTS = new ErrorCode(1_020_003_000, "è”系人ä¸å­˜åœ¨"); + ErrorCode CONTACT_BUSINESS_LINK_NOT_EXISTS = new ErrorCode(1_020_003_001, "è”系人商机关è”ä¸å­˜åœ¨"); + ErrorCode CONTACT_DELETE_FAIL_CONTRACT_LINK_EXISTS = new ErrorCode(1_020_003_002, "è”系人已关è”åˆåŒï¼Œä¸èƒ½åˆ é™¤"); + + // ========== 回款 1-020-004-000 ========== + ErrorCode RECEIVABLE_NOT_EXISTS = new ErrorCode(1_020_004_000, "回款ä¸å­˜åœ¨"); + + // ========== åˆåŒç®¡ç† 1-020-005-000 ========== + ErrorCode RECEIVABLE_PLAN_NOT_EXISTS = new ErrorCode(1_020_005_000, "回款计划ä¸å­˜åœ¨"); + + // ========== å®¢æˆ·ç®¡ç† 1_020_006_000 ========== + ErrorCode CUSTOMER_NOT_EXISTS = new ErrorCode(1_020_006_000, "客户ä¸å­˜åœ¨"); + ErrorCode CUSTOMER_OWNER_EXISTS = new ErrorCode(1_020_006_001, "客户ã€{}】已存在所属负责人"); + ErrorCode CUSTOMER_LOCKED = new ErrorCode(1_020_006_002, "客户ã€{}】状æ€å·²é”定"); + ErrorCode CUSTOMER_ALREADY_DEAL = new ErrorCode(1_020_006_003, "客户已交易"); + ErrorCode CUSTOMER_IN_POOL = new ErrorCode(1_020_006_004, "客户ã€{}】放入公海失败,原因:已ç»æ˜¯å…¬æµ·å®¢æˆ·"); + ErrorCode CUSTOMER_LOCKED_PUT_POOL_FAIL = new ErrorCode(1_020_006_005, "客户ã€{}】放入公海失败,原因:客户已é”定"); + ErrorCode CUSTOMER_UPDATE_OWNER_USER_FAIL = new ErrorCode(1_020_006_006, "更新客户ã€{}】负责人失败, 原因:系统异常"); + ErrorCode CUSTOMER_LOCK_FAIL_IS_LOCK = new ErrorCode(1_020_006_007, "é”定客户失败,它已ç»å¤„于é”定状æ€"); + ErrorCode CUSTOMER_UNLOCK_FAIL_IS_UNLOCK = new ErrorCode(1_020_006_008, "解é”客户失败,它已ç»å¤„于未é”定状æ€"); + ErrorCode CUSTOMER_LOCK_EXCEED_LIMIT = new ErrorCode(1_020_006_009, "é”定客户失败,超出é”定规则上é™"); + ErrorCode CUSTOMER_OWNER_EXCEED_LIMIT = new ErrorCode(1_020_006_010, "æ“作失败,超出客户数拥有上é™"); + ErrorCode CUSTOMER_DELETE_FAIL_HAVE_REFERENCE = new ErrorCode(1_020_006_011, "删除客户失败,有关è”{}"); + ErrorCode CUSTOMER_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_020_006_012, "导入客户数æ®ä¸èƒ½ä¸ºç©ºï¼"); + ErrorCode CUSTOMER_CREATE_NAME_NOT_NULL = new ErrorCode(1_020_006_013, "客户å称ä¸èƒ½ä¸ºç©ºï¼"); + ErrorCode CUSTOMER_NAME_EXISTS = new ErrorCode(1_020_006_014, "已存在å为ã€{}】的客户ï¼"); + + // ========== æƒé™ç®¡ç† 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_EXISTS = new ErrorCode(1_020_007_003, "{}æ“作失败,原因:转移对象已ç»æ˜¯è¯¥è´Ÿè´£äºº"); + ErrorCode CRM_PERMISSION_DELETE_FAIL = new ErrorCode(1_020_007_004, "删除数æ®æƒé™å¤±è´¥ï¼ŒåŽŸå› ï¼šæ‰¹é‡åˆ é™¤æƒé™çš„时候,åªèƒ½å±žäºŽåŒä¸€ä¸ª bizId 下"); + ErrorCode CRM_PERMISSION_DELETE_FAIL_EXIST_OWNER = new ErrorCode(1_020_007_005, "删除数æ®æƒé™å¤±è´¥ï¼ŒåŽŸå› ï¼šå­˜åœ¨è´Ÿè´£äºº"); + ErrorCode CRM_PERMISSION_DELETE_DENIED = new ErrorCode(1_020_007_006, "删除数æ®æƒé™å¤±è´¥ï¼ŒåŽŸå› ï¼šæ²¡æœ‰æƒé™"); + ErrorCode CRM_PERMISSION_DELETE_SELF_PERMISSION_FAIL_EXIST_OWNER = new ErrorCode(1_020_007_007, "删除数æ®æƒé™å¤±è´¥ï¼ŒåŽŸå› ï¼šä¸èƒ½åˆ é™¤è´Ÿè´£äºº"); + ErrorCode CRM_PERMISSION_CREATE_FAIL = new ErrorCode(1_020_007_008, "创建数æ®æƒé™å¤±è´¥ï¼ŒåŽŸå› ï¼šæ‰€åŠ ç”¨æˆ·å·²æœ‰æƒé™"); + + // ========== äº§å“ 1_020_008_000 ========== + ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1_020_008_000, "产å“ä¸å­˜åœ¨"); + ErrorCode PRODUCT_NO_EXISTS = new ErrorCode(1_020_008_001, "产å“ç¼–å·å·²å­˜åœ¨"); + + // ========== 产å“分类 1_020_009_000 ========== + ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_020_009_000, "产å“分类ä¸å­˜åœ¨"); + ErrorCode PRODUCT_CATEGORY_EXISTS = new ErrorCode(1_020_009_001, "产å“分类已存在"); + ErrorCode PRODUCT_CATEGORY_USED = new ErrorCode(1_020_009_002, "产å“分类已关è”产å“"); + ErrorCode PRODUCT_CATEGORY_PARENT_NOT_EXISTS = new ErrorCode(1_020_009_003, "父分类ä¸å­˜åœ¨"); + ErrorCode PRODUCT_CATEGORY_PARENT_NOT_FIRST_LEVEL = new ErrorCode(1_020_009_004, "父分类ä¸èƒ½æ˜¯äºŒçº§åˆ†ç±»"); + ErrorCode product_CATEGORY_EXISTS_CHILDREN = new ErrorCode(1_020_009_005, "存在å­åˆ†ç±»ï¼Œæ— æ³•åˆ é™¤"); + + // ========== 商机状æ€ç±»åž‹ 1_020_010_000 ========== + ErrorCode BUSINESS_STATUS_TYPE_NOT_EXISTS = new ErrorCode(1_020_010_000, "商机状æ€ç±»åž‹ä¸å­˜åœ¨"); + ErrorCode BUSINESS_STATUS_TYPE_NAME_EXISTS = new ErrorCode(1_020_010_001, "商机状æ€ç±»åž‹å称已存在"); + + // ========== å•†æœºçŠ¶æ€ 1_020_011_000 ========== + ErrorCode BUSINESS_STATUS_NOT_EXISTS = new ErrorCode(1_020_011_000, "商机状æ€ä¸å­˜åœ¨"); + + // ========== 客户公海规则设置 1_020_012_000 ========== + ErrorCode CUSTOMER_POOL_CONFIG_NOT_EXISTS_OR_DISABLED = new ErrorCode(1_020_012_000, "客户公海é…ç½®ä¸å­˜åœ¨æˆ–未å¯ç”¨"); + ErrorCode CUSTOMER_LIMIT_CONFIG_NOT_EXISTS = new ErrorCode(1_020_012_001, "客户é™åˆ¶é…ç½®ä¸å­˜åœ¨"); + + // ========== 跟进记录 1_020_013_000 ========== + ErrorCode FOLLOW_UP_RECORD_NOT_EXISTS = new ErrorCode(1_020_013_000, "跟进记录ä¸å­˜åœ¨"); + ErrorCode FOLLOW_UP_RECORD_DELETE_DENIED = new ErrorCode(1_020_013_001, "删除跟进记录失败,原因:没有æƒé™"); + + // ========== å¾…åŠžæ¶ˆæ¯ 1_020_014_000 ========== + +} diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java new file mode 100644 index 000000000..98a66d2c9 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java @@ -0,0 +1,139 @@ +package cn.iocoder.yudao.module.crm.enums; + +/** + * CRM æ“作日志枚举 + * 目的:统一管ç†ï¼Œä¹Ÿå‡å°‘ Service 里å„ç§â€œå¤æ‚â€å­—符串 + * + * @author HUIHUI + */ +public interface LogRecordConstants { + + // ======================= CRM_LEADS 线索 ======================= + + String CRM_LEADS_TYPE = "CRM 线索"; + String CRM_LEADS_CREATE_SUB_TYPE = "创建线索"; + String CRM_LEADS_CREATE_SUCCESS = "创建了线索{{#clue.name}}"; + String CRM_LEADS_UPDATE_SUB_TYPE = "更新线索"; + String CRM_LEADS_UPDATE_SUCCESS = "更新了线索ã€{{#clueName}}】: {_DIFF{#updateReq}}"; + String CRM_LEADS_DELETE_SUB_TYPE = "删除线索"; + String CRM_LEADS_DELETE_SUCCESS = "删除了线索ã€{{#clueName}}】"; + String CRM_LEADS_TRANSFER_SUB_TYPE = "转移线索"; + String CRM_LEADS_TRANSFER_SUCCESS = "将线索ã€{{#clue.name}}】的负责人从ã€{getAdminUserById{#clue.ownerUserId}}】å˜æ›´ä¸ºäº†ã€{getAdminUserById{#reqVO.newOwnerUserId}}】"; + String CRM_LEADS_TRANSLATE_SUB_TYPE = "线索转化为客户"; + String CRM_LEADS_TRANSLATE_SUCCESS = "将线索ã€{{#clue.name}}】转化为客户"; + + // ======================= CRM_CUSTOMER 客户 ======================= + + String CRM_CUSTOMER_TYPE = "CRM 客户"; + String CRM_CUSTOMER_CREATE_SUB_TYPE = "创建客户"; + String CRM_CUSTOMER_CREATE_SUCCESS = "创建了客户{{#customer.name}}"; + String CRM_CUSTOMER_UPDATE_SUB_TYPE = "更新客户"; + String CRM_CUSTOMER_UPDATE_SUCCESS = "更新了客户ã€{{#customerName}}】: {_DIFF{#updateReqVO}}"; + String CRM_CUSTOMER_DELETE_SUB_TYPE = "删除客户"; + String CRM_CUSTOMER_DELETE_SUCCESS = "删除了客户ã€{{#customerName}}】"; + String CRM_CUSTOMER_TRANSFER_SUB_TYPE = "转移客户"; + String CRM_CUSTOMER_TRANSFER_SUCCESS = "将客户ã€{{#customer.name}}】的负责人从ã€{getAdminUserById{#customer.ownerUserId}}】å˜æ›´ä¸ºäº†ã€{getAdminUserById{#reqVO.newOwnerUserId}}】"; + String CRM_CUSTOMER_LOCK_SUB_TYPE = "{{#customer.lockStatus ? '解é”客户' : 'é”定客户'}}"; + String CRM_CUSTOMER_LOCK_SUCCESS = "{{#customer.lockStatus ? '将客户ã€' + #customer.name + '】解é”' : '将客户ã€' + #customer.name + '】é”定'}}"; + String CRM_CUSTOMER_POOL_SUB_TYPE = "客户放入公海"; + String CRM_CUSTOMER_POOL_SUCCESS = "将客户ã€{{#customerName}}】放入了公海"; + String CRM_CUSTOMER_RECEIVE_SUB_TYPE = "{{#ownerUserName != null ? '分é…客户' : '领å–客户'}}"; + String CRM_CUSTOMER_RECEIVE_SUCCESS = "{{#ownerUserName != null ? '将客户ã€' + #customer.name + '】分é…ç»™ã€' + #ownerUserName + '】' : '领å–客户ã€' + #customer.name + '】'}}"; + String CRM_CUSTOMER_IMPORT_SUB_TYPE = "{{#isUpdate ? '导入并更新客户' : '导入客户'}}"; + String CRM_CUSTOMER_IMPORT_SUCCESS = "{{#isUpdate ? '导入并更新了客户ã€'+ #customer.name +'】' : '导入了客户ã€'+ #customer.name +'】'}}"; + + // ======================= CRM_CUSTOMER_LIMIT_CONFIG 客户é™åˆ¶é…ç½® ======================= + + String CRM_CUSTOMER_LIMIT_CONFIG_TYPE = "CRM 客户é™åˆ¶é…ç½®"; + String CRM_CUSTOMER_LIMIT_CONFIG_CREATE_SUB_TYPE = "创建客户é™åˆ¶é…ç½®"; + String CRM_CUSTOMER_LIMIT_CONFIG_CREATE_SUCCESS = "创建了ã€{{#limitType}}】类型的客户é™åˆ¶é…ç½®"; + String CRM_CUSTOMER_LIMIT_CONFIG_UPDATE_SUB_TYPE = "更新客户é™åˆ¶é…ç½®"; + String CRM_CUSTOMER_LIMIT_CONFIG_UPDATE_SUCCESS = "更新了客户é™åˆ¶é…ç½®: {_DIFF{#updateReqVO}}"; + String CRM_CUSTOMER_LIMIT_CONFIG_DELETE_SUB_TYPE = "删除客户é™åˆ¶é…ç½®"; + String CRM_CUSTOMER_LIMIT_CONFIG_DELETE_SUCCESS = "删除了ã€{{#limitType}}】类型的客户é™åˆ¶é…ç½®"; + + // ======================= CRM_CUSTOMER_POOL_CONFIG 客户公海规则 ======================= + + String CRM_CUSTOMER_POOL_CONFIG_TYPE = "CRM 客户公海规则"; + String CRM_CUSTOMER_POOL_CONFIG_SUB_TYPE = "{{#isPoolConfigUpdate ? '更新客户公海规则' : '创建客户公海规则'}}"; + String CRM_CUSTOMER_POOL_CONFIG_SUCCESS = "{{#isPoolConfigUpdate ? '更新了客户公海规则' : '创建了客户公海规则'}}"; + + // ======================= CRM_CONTACT è”系人 ======================= + + String CRM_CONTACT_TYPE = "CRM è”系人"; + String CRM_CONTACT_CREATE_SUB_TYPE = "创建è”系人"; + String CRM_CONTACT_CREATE_SUCCESS = "创建了è”系人{{#contact.name}}"; + String CRM_CONTACT_UPDATE_SUB_TYPE = "æ›´æ–°è”系人"; + String CRM_CONTACT_UPDATE_SUCCESS = "更新了è”系人ã€{{#contactName}}】: {_DIFF{#updateReqVO}}"; + String CRM_CONTACT_DELETE_SUB_TYPE = "删除è”系人"; + String CRM_CONTACT_DELETE_SUCCESS = "删除了è”系人ã€{{#contactName}}】"; + String CRM_CONTACT_TRANSFER_SUB_TYPE = "转移è”系人"; + String CRM_CONTACT_TRANSFER_SUCCESS = "å°†è”系人ã€{{#contact.name}}】的负责人从ã€{getAdminUserById{#contact.ownerUserId}}】å˜æ›´ä¸ºäº†ã€{getAdminUserById{#reqVO.newOwnerUserId}}】"; + + // ======================= CRM_BUSINESS 商机 ======================= + + String CRM_BUSINESS_TYPE = "CRM 商机"; + String CRM_BUSINESS_CREATE_SUB_TYPE = "创建商机"; + String CRM_BUSINESS_CREATE_SUCCESS = "创建了商机{{#business.name}}"; + String CRM_BUSINESS_UPDATE_SUB_TYPE = "更新商机"; + String CRM_BUSINESS_UPDATE_SUCCESS = "更新了商机ã€{{#businessName}}】: {_DIFF{#updateReqVO}}"; + String CRM_BUSINESS_DELETE_SUB_TYPE = "删除商机"; + String CRM_BUSINESS_DELETE_SUCCESS = "删除了商机ã€{{#businessName}}】"; + String CRM_BUSINESS_TRANSFER_SUB_TYPE = "转移商机"; + String CRM_BUSINESS_TRANSFER_SUCCESS = "将商机ã€{{#business.name}}】的负责人从ã€{getAdminUserById{#business.ownerUserId}}】å˜æ›´ä¸ºäº†ã€{getAdminUserById{#reqVO.newOwnerUserId}}】"; + + // ======================= CRM_CONTRACT åˆåŒ ======================= + + String CRM_CONTRACT_TYPE = "CRM åˆåŒ"; + String CRM_CONTRACT_CREATE_SUB_TYPE = "创建åˆåŒ"; + String CRM_CONTRACT_CREATE_SUCCESS = "创建了åˆåŒ{{#contract.name}}"; + String CRM_CONTRACT_UPDATE_SUB_TYPE = "æ›´æ–°åˆåŒ"; + String CRM_CONTRACT_UPDATE_SUCCESS = "更新了åˆåŒã€{{#contractName}}】: {_DIFF{#updateReqVO}}"; + String CRM_CONTRACT_DELETE_SUB_TYPE = "删除åˆåŒ"; + String CRM_CONTRACT_DELETE_SUCCESS = "删除了åˆåŒã€{{#contractName}}】"; + String CRM_CONTRACT_TRANSFER_SUB_TYPE = "转移åˆåŒ"; + String CRM_CONTRACT_TRANSFER_SUCCESS = "å°†åˆåŒã€{{#contract.name}}】的负责人从ã€{getAdminUserById{#contract.ownerUserId}}】å˜æ›´ä¸ºäº†ã€{getAdminUserById{#reqVO.newOwnerUserId}}】"; + String CRM_CONTRACT_SUBMIT_SUB_TYPE = "æ交åˆåŒå®¡æ‰¹"; + String CRM_CONTRACT_SUBMIT_SUCCESS = "æ交åˆåŒã€{{#contractName}}】审批æˆåŠŸ"; + + // ======================= CRM_PRODUCT äº§å“ ======================= + + String CRM_PRODUCT_TYPE = "CRM 产å“"; + String CRM_PRODUCT_CREATE_SUB_TYPE = "创建产å“"; + String CRM_PRODUCT_CREATE_SUCCESS = "创建了产å“ã€{{#createReqVO.name}}】"; + String CRM_PRODUCT_UPDATE_SUB_TYPE = "更新产å“"; + String CRM_PRODUCT_UPDATE_SUCCESS = "更新了产å“ã€{{#updateReqVO.name}}】: {_DIFF{#updateReqVO}}"; + String CRM_PRODUCT_DELETE_SUB_TYPE = "删除产å“"; + String CRM_PRODUCT_DELETE_SUCCESS = "删除了产å“ã€{{#product.name}}】"; + + // ======================= CRM_PRODUCT_CATEGORY 产å“分类 ======================= + + String CRM_PRODUCT_CATEGORY_TYPE = "CRM 产å“分类"; + String CRM_PRODUCT_CATEGORY_CREATE_SUB_TYPE = "创建产å“分类"; + String CRM_PRODUCT_CATEGORY_CREATE_SUCCESS = "创建了产å“分类ã€{{#createReqVO.name}}】"; + String CRM_PRODUCT_CATEGORY_UPDATE_SUB_TYPE = "更新产å“分类"; + String CRM_PRODUCT_CATEGORY_UPDATE_SUCCESS = "更新了产å“分类ã€{{#updateReqVO.name}}】: {_DIFF{#updateReqVO}}"; + String CRM_PRODUCT_CATEGORY_DELETE_SUB_TYPE = "删除产å“分类"; + String CRM_PRODUCT_CATEGORY_DELETE_SUCCESS = "删除了产å“分类ã€{{#productCategory.name}}】"; + + // ======================= CRM_RECEIVABLE 回款 ======================= + + String CRM_RECEIVABLE_TYPE = "CRM 回款"; + String CRM_RECEIVABLE_CREATE_SUB_TYPE = "创建回款"; + String CRM_RECEIVABLE_CREATE_SUCCESS = "创建了åˆåŒã€{getContractById{#receivable.contractId}}】的第ã€{{#receivable.period}}】期回款"; + String CRM_RECEIVABLE_UPDATE_SUB_TYPE = "更新回款"; + String CRM_RECEIVABLE_UPDATE_SUCCESS = "更新了åˆåŒã€{getContractById{#receivable.contractId}}】的第ã€{{#receivable.period}}】期回款: {_DIFF{#updateReqVO}}"; + String CRM_RECEIVABLE_DELETE_SUB_TYPE = "删除回款"; + String CRM_RECEIVABLE_DELETE_SUCCESS = "删除了åˆåŒã€{getContractById{#receivable.contractId}}】的第ã€{{#receivable.period}}】期回款"; + + // ======================= CRM_RECEIVABLE_PLAN 回款计划 ======================= + + String CRM_RECEIVABLE_PLAN_TYPE = "CRM 回款计划"; + String CRM_RECEIVABLE_PLAN_CREATE_SUB_TYPE = "创建回款计划"; + String CRM_RECEIVABLE_PLAN_CREATE_SUCCESS = "创建了åˆåŒã€{getContractById{#receivablePlan.contractId}}】的第ã€{{#receivablePlan.period}}】期回款计划"; + String CRM_RECEIVABLE_PLAN_UPDATE_SUB_TYPE = "更新回款计划"; + String CRM_RECEIVABLE_PLAN_UPDATE_SUCCESS = "更新了åˆåŒã€{getContractById{#receivablePlan.contractId}}】的第ã€{{#receivablePlan.period}}】期回款计划: {_DIFF{#updateReqVO}}"; + String CRM_RECEIVABLE_PLAN_DELETE_SUB_TYPE = "删除回款计划"; + String CRM_RECEIVABLE_PLAN_DELETE_SUCCESS = "删除了åˆåŒã€{getContractById{#receivablePlan.contractId}}】的第ã€{{#receivablePlan.period}}】期回款计划"; + +} diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/business/CrmBizEndStatus.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/business/CrmBizEndStatus.java new file mode 100644 index 000000000..55548dbff --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/business/CrmBizEndStatus.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.crm.enums.business; + +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +// TODO @lzxhqs:1)titleã€descriptionã€create å¯ä»¥åˆ é™¤ï¼Œéžæ ‡å‡†çš„ javadoc 注释哈,然åŽå¯ä»¥åœ¨ç±»ä¸ŠåŠ ä¸‹è¿™ä¸ªç±»çš„注释;2)CrmBizEndStatus æ”¹æˆ CrmBusinessEndStatus,éžå¿…è¦ä¸ç¼©å†™å“ˆï¼Œå¯é˜…读比较é‡è¦ +/** + * @author lzxhqs + * @version 1.0 + * @title CrmBizEndStatus + * @description + * @create 2024/1/12 + */ +@RequiredArgsConstructor +@Getter +public enum CrmBizEndStatus implements IntArrayValuable { + + WIN(1, "èµ¢å•"), + LOSE(2, "输å•"), + INVALID(3, "无效"); + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmBizEndStatus::getStatus).toArray(); + + // TODO @lzxhqs:这里的方法,建议放到 49 行之åŽï¼›ä¸€èˆ¬ç±»é‡Œæ˜¯ï¼Œé™æ€å˜é‡ï¼Œæ™®é€šå˜é‡ï¼›é™æ€æ–¹æ³•ï¼›æ™®é€šæ–¹æ³• + public static boolean isWin(Integer status) { + return ObjectUtil.equal(WIN.getStatus(), status); + } + + public static boolean isLose(Integer status) { + return ObjectUtil.equal(LOSE.getStatus(), status); + } + + public static boolean isInvalid(Integer status) { + return ObjectUtil.equal(INVALID.getStatus(), status); + } + + /** + * 场景类型 + */ + private final Integer status; + /** + * 场景å称 + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmAuditStatusEnum.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmAuditStatusEnum.java new file mode 100644 index 000000000..67709e95b --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmAuditStatusEnum.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.crm.enums.common; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * CRM çš„å®¡æ‰¹çŠ¶æ€ + * + * @author 赤焰 + */ +@RequiredArgsConstructor +@Getter +public enum CrmAuditStatusEnum implements IntArrayValuable { + + DRAFT(0, "未æ交"), + PROCESS(10, "审批中"), + APPROVE(20, "审核通过"), + REJECT(30, "审核ä¸é€šè¿‡"), + CANCEL(40, "å·²å–消"); + + private final Integer status; + private final String name; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmAuditStatusEnum::getStatus).toArray(); + + @Override + public int[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmBizTypeEnum.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmBizTypeEnum.java new file mode 100644 index 000000000..f0784cab2 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmBizTypeEnum.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.crm.enums.common; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * CRM 业务类型枚举 + * + * @author HUIHUI + */ +@RequiredArgsConstructor +@Getter +public enum CrmBizTypeEnum implements IntArrayValuable { + + CRM_LEADS(1, "线索"), + CRM_CUSTOMER(2, "客户"), + CRM_CONTACT(3, "è”系人"), + CRM_BUSINESS(4, "商机"), + CRM_CONTRACT(5, "åˆåŒ"), + CRM_PRODUCT(6, "产å“"), + CRM_RECEIVABLE(7, "回款"), + CRM_RECEIVABLE_PLAN(8, "回款计划") + ; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmBizTypeEnum::getType).toArray(); + + /** + * 类型 + */ + private final Integer type; + /** + * å称 + */ + private final String name; + + public static String getNameByType(Integer type) { + CrmBizTypeEnum typeEnum = CollUtil.findOne(CollUtil.newArrayList(CrmBizTypeEnum.values()), + item -> ObjUtil.equal(item.type, type)); + return typeEnum == null ? null : typeEnum.getName(); + } + + @Override + public int[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmSceneTypeEnum.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmSceneTypeEnum.java new file mode 100644 index 000000000..945d7c6a3 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmSceneTypeEnum.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.module.crm.enums.common; + +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * CRM 列表检索场景 + * + * @author HUIHUI + */ +@Getter +@AllArgsConstructor +public enum CrmSceneTypeEnum implements IntArrayValuable { + + OWNER(1, "我负责的"), + INVOLVED(2, "我å‚与的"), + SUBORDINATE(3, "下属负责的"); + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmSceneTypeEnum::getType).toArray(); + + /** + * 场景类型 + */ + private final Integer type; + /** + * 场景å称 + */ + private final String name; + + public static boolean isOwner(Integer type) { + return ObjUtil.equal(OWNER.getType(), type); + } + + public static boolean isInvolved(Integer type) { + return ObjUtil.equal(INVOLVED.getType(), type); + } + + public static boolean isSubordinate(Integer type) { + return ObjUtil.equal(SUBORDINATE.getType(), type); + } + + @Override + public int[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/customer/CrmCustomerLevelEnum.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/customer/CrmCustomerLevelEnum.java new file mode 100644 index 000000000..aa06b05eb --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/customer/CrmCustomerLevelEnum.java @@ -0,0 +1,38 @@ +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::getLevel).toArray(); + + /** + * çŠ¶æ€ + */ + private final Integer level; + /** + * 状æ€å + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/customer/CrmCustomerLimitConfigTypeEnum.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/customer/CrmCustomerLimitConfigTypeEnum.java new file mode 100644 index 000000000..2cf8d7811 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/customer/CrmCustomerLimitConfigTypeEnum.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.crm.enums.customer; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjUtil; +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 CrmCustomerLimitConfigTypeEnum implements IntArrayValuable { + + /** + * 拥有客户数é™åˆ¶ + */ + CUSTOMER_OWNER_LIMIT(1, "拥有客户数é™åˆ¶"), + /** + * é”定客户数é™åˆ¶ + */ + CUSTOMER_LOCK_LIMIT(2, "é”定客户数é™åˆ¶"), + ; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmCustomerLimitConfigTypeEnum::getType).toArray(); + + /** + * çŠ¶æ€ + */ + private final Integer type; + /** + * 状æ€å + */ + private final String name; + + public static String getNameByType(Integer type) { + CrmCustomerLimitConfigTypeEnum typeEnum = CollUtil.findOne(CollUtil.newArrayList(CrmCustomerLimitConfigTypeEnum.values()), + item -> ObjUtil.equal(item.type, type)); + return typeEnum == null ? null : typeEnum.getName(); + } + + @Override + public int[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/permission/CrmPermissionLevelEnum.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/permission/CrmPermissionLevelEnum.java new file mode 100644 index 000000000..56b0366aa --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/permission/CrmPermissionLevelEnum.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.module.crm.enums.permission; + +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * CRM æ•°æ®æƒé™çº§åˆ«æžšä¸¾ + * + * @author HUIHUI + */ +@Getter +@AllArgsConstructor +public enum CrmPermissionLevelEnum implements IntArrayValuable { + + OWNER(1, "负责人"), + READ(2, "读"), + WRITE(3, "写"); + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmPermissionLevelEnum::getLevel).toArray(); + + /** + * 级别 + */ + private final Integer level; + /** + * 级别å称 + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } + + public static boolean isOwner(Integer level) { + return ObjUtil.equal(OWNER.level, level); + } + + public static boolean isRead(Integer level) { + return ObjUtil.equal(READ.level, level); + } + + public static boolean isWrite(Integer level) { + return ObjUtil.equal(WRITE.level, level); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/permission/CrmPermissionRoleCodeEnum.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/permission/CrmPermissionRoleCodeEnum.java new file mode 100644 index 000000000..c9a51057b --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/permission/CrmPermissionRoleCodeEnum.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.crm.enums.permission; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * Crm æ•°æ®æƒé™è§’色枚举 + * + * @author HUIHUI + */ +@Getter +@AllArgsConstructor +public enum CrmPermissionRoleCodeEnum { + + CRM_ADMIN("crm_admin", "CRM 管ç†å‘˜"); + + /** + * 角色标识 + */ + private String code; + /** + * 角色å称 + */ + private String name; + +} + diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/product/CrmProductStatusEnum.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/product/CrmProductStatusEnum.java new file mode 100644 index 000000000..e82d5b5b8 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/product/CrmProductStatusEnum.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.crm.enums.product; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * CRM 商å“çŠ¶æ€ + * + * @author ZanGe丶 + * @since 2023-11-30 21:53 + */ +@Getter +@AllArgsConstructor +public enum CrmProductStatusEnum implements IntArrayValuable { + + DISABLE(0, "下架"), + ENABLE(1, "上架"); + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmProductStatusEnum::getStatus).toArray(); + + /** + * çŠ¶æ€ + */ + private final Integer status; + /** + * 状æ€å + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/pom.xml b/yudao-module-crm/yudao-module-crm-biz/pom.xml new file mode 100644 index 000000000..323e873d9 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/pom.xml @@ -0,0 +1,141 @@ + + + + cn.iocoder.cloud + yudao-module-crm + ${revision} + + 4.0.0 + yudao-module-crm-biz + + ${project.artifactId} + + crm 包下,客户关系管ç†ï¼ˆCustomer Relationship Management)。 + 例如说:客户ã€è”系人ã€å•†æœºã€åˆåŒã€å›žæ¬¾ç­‰ç­‰ + + + + + + org.springframework.cloud + spring-cloud-starter-bootstrap + + + + cn.iocoder.cloud + yudao-spring-boot-starter-env + + + + + cn.iocoder.cloud + yudao-module-system-api + ${revision} + + + cn.iocoder.cloud + yudao-module-crm-api + ${revision} + + + cn.iocoder.cloud + yudao-module-bpm-api + ${revision} + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-biz-operatelog + + + cn.iocoder.cloud + yudao-spring-boot-starter-biz-ip + + + cn.iocoder.cloud + yudao-spring-boot-starter-biz-tenant + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-security + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-mybatis + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-rpc + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-job + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-excel + + + cn.iocoder.cloud + yudao-spring-boot-starter-biz-dict + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-monitor + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-test + + + + + + ${project.artifactId} + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + + repackage + + + + + + + + diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/CrmServerApplication.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/CrmServerApplication.java new file mode 100644 index 000000000..36bc7e8e3 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/CrmServerApplication.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.crm; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * 项目的å¯åŠ¨ç±» + *

+ * 如果你碰到å¯åŠ¨çš„问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章 + * 如果你碰到å¯åŠ¨çš„问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章 + * 如果你碰到å¯åŠ¨çš„问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章 + * + * @author 芋é“æºç  + */ +@SpringBootApplication +public class CrmServerApplication { + + public static void main(String[] args) { + // 如果你碰到å¯åŠ¨çš„问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章 + // 如果你碰到å¯åŠ¨çš„问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章 + // 如果你碰到å¯åŠ¨çš„问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章 + + SpringApplication.run(CrmServerApplication.class, args); + + // 如果你碰到å¯åŠ¨çš„问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章 + // 如果你碰到å¯åŠ¨çš„问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章 + // 如果你碰到å¯åŠ¨çš„问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章 + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/api/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/api/package-info.java new file mode 100644 index 000000000..5c4e2493e --- /dev/null +++ b/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; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/backlog/CrmBacklogController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/backlog/CrmBacklogController.java new file mode 100644 index 000000000..9b8841e2e --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/backlog/CrmBacklogController.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.crm.controller.admin.backlog; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.backlog.vo.CrmTodayCustomerPageReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.service.message.CrmBacklogService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "管ç†åŽå° - CRM待办消æ¯") +@RestController +@RequestMapping("/crm/backlog") +@Validated +public class CrmBacklogController { + + @Resource + private CrmBacklogService crmMessageService; + + // TODO 芋艿:未æ¥å¯èƒ½åˆå¹¶åˆ° CrmCustomerController + @GetMapping("/today-customer-page") + @Operation(summary = "今日需è”系客户") + @PreAuthorize("@ss.hasPermission('crm:customer:query')") + public CommonResult> getTodayCustomerPage(@Valid CrmTodayCustomerPageReqVO pageReqVO) { + PageResult pageResult = crmMessageService.getTodayCustomerPage(pageReqVO, getLoginUserId()); + return success(BeanUtils.toBean(pageResult, CrmCustomerRespVO.class)); + } + +} \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/backlog/vo/CrmTodayCustomerPageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/backlog/vo/CrmTodayCustomerPageReqVO.java new file mode 100644 index 000000000..21fd88c0b --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/backlog/vo/CrmTodayCustomerPageReqVO.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.crm.controller.admin.backlog.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum; +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 CrmTodayCustomerPageReqVO extends PageParam { + + /** + * è”ç³»çŠ¶æ€ - 今日需è”ç³» + */ + public static final int CONTACT_TODAY = 1; + /** + * è”ç³»çŠ¶æ€ - 已逾期 + */ + public static final int CONTACT_EXPIRED = 2; + /** + * è”ç³»çŠ¶æ€ - å·²è”ç³» + */ + public static final int CONTACT_ALREADY = 3; + + @Schema(description = "è”系状æ€", example = "1") + private Integer contactStatus; + + @Schema(description = "场景类型", example = "1") + @InEnum(CrmSceneTypeEnum.class) + private Integer sceneType; + +} \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/CrmBiRankController.http b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/CrmBiRankController.http new file mode 100644 index 000000000..b9e9a4edf --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/CrmBiRankController.http @@ -0,0 +1,9 @@ +### åˆåŒé‡‘é¢æŽ’行榜 +GET {{baseUrl}}/crm/bi-rank/get-contract-price-rank?deptId=100×[0]=2022-12-12 00:00:00×[1]=2024-12-12 23:59:59 +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} + +### 回款金é¢æŽ’行榜 +GET {{baseUrl}}/crm/bi-rank/get-receivable-price-rank?deptId=100×[0]=2022-12-12 00:00:00×[1]=2024-12-12 23:59:59 +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/CrmBiRankController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/CrmBiRankController.java new file mode 100644 index 000000000..21463aed0 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/CrmBiRankController.java @@ -0,0 +1,87 @@ +package cn.iocoder.yudao.module.crm.controller.admin.bi; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRanKRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRankReqVO; +import cn.iocoder.yudao.module.crm.service.bi.CrmBiRankingService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + + +@Tag(name = "管ç†åŽå° - CRM BI 排行榜") +@RestController +@RequestMapping("/crm/bi-rank") +@Validated +public class CrmBiRankController { + + @Resource + private CrmBiRankingService rankingService; + + @GetMapping("/get-contract-price-rank") + @Operation(summary = "获得åˆåŒé‡‘é¢æŽ’行榜") + @PreAuthorize("@ss.hasPermission('crm:bi-rank:query')") + public CommonResult> getContractPriceRank(@Valid CrmBiRankReqVO rankingReqVO) { + return success(rankingService.getContractPriceRank(rankingReqVO)); + } + + @GetMapping("/get-receivable-price-rank") + @Operation(summary = "获得回款金é¢æŽ’行榜") + @PreAuthorize("@ss.hasPermission('crm:bi-rank:query')") + public CommonResult> getReceivablePriceRank(@Valid CrmBiRankReqVO rankingReqVO) { + return success(rankingService.getReceivablePriceRank(rankingReqVO)); + } + + @GetMapping("/get-contract-count-rank") + @Operation(summary = "获得签约åˆåŒæ•°é‡æŽ’行榜") + @PreAuthorize("@ss.hasPermission('crm:bi-rank:query')") + public CommonResult> getContractCountRank(@Valid CrmBiRankReqVO rankingReqVO) { + return success(rankingService.getContractCountRank(rankingReqVO)); + } + + @GetMapping("/get-product-sales-rank") + @Operation(summary = "获得产å“销é‡æŽ’行榜") + @PreAuthorize("@ss.hasPermission('crm:bi-rank:query')") + public CommonResult> getProductSalesRank(@Valid CrmBiRankReqVO rankingReqVO) { + return success(rankingService.getProductSalesRank(rankingReqVO)); + } + + @GetMapping("/get-customer-count-rank") + @Operation(summary = "获得新增客户数排行榜") + @PreAuthorize("@ss.hasPermission('crm:bi-rank:query')") + public CommonResult> getCustomerCountRank(@Valid CrmBiRankReqVO rankingReqVO) { + return success(rankingService.getCustomerCountRank(rankingReqVO)); + } + + @GetMapping("/get-contacts-count-rank") + @Operation(summary = "获得新增è”系人数排行榜") + @PreAuthorize("@ss.hasPermission('crm:bi-rank:query')") + public CommonResult> getContactsCountRank(@Valid CrmBiRankReqVO rankingReqVO) { + return success(rankingService.getContactsCountRank(rankingReqVO)); + } + + @GetMapping("/get-follow-count-rank") + @Operation(summary = "获得跟进次数排行榜") + @PreAuthorize("@ss.hasPermission('crm:bi-rank:query')") + public CommonResult> getFollowCountRank(@Valid CrmBiRankReqVO rankingReqVO) { + return success(rankingService.getFollowCountRank(rankingReqVO)); + } + + @GetMapping("/get-follow-customer-count-rank") + @Operation(summary = "获得跟进客户数排行榜") + @PreAuthorize("@ss.hasPermission('crm:bi-rank:query')") + public CommonResult> getFollowCustomerCountRank(@Valid CrmBiRankReqVO rankingReqVO) { + return success(rankingService.getFollowCustomerCountRank(rankingReqVO)); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/vo/CrmBiRanKRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/vo/CrmBiRanKRespVO.java new file mode 100644 index 000000000..404ee3352 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/vo/CrmBiRanKRespVO.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.crm.controller.admin.bi.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + + +@Schema(description = "管ç†åŽå° - CRM BI 排行榜 Response VO") +@Data +public class CrmBiRanKRespVO { + + @Schema(description = "负责人编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long ownerUserId; + + @Schema(description = "姓å", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private String nickname; + + @Schema(description = "部门å称", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private String deptName; + + /** + * æ•°é‡æ˜¯ä¸ªç‰¹åˆ«â€œæŠ½è±¡â€çš„概念,在ä¸åŒæŽ’行下,代表ä¸åŒå«ä¹‰ + * + * 1. 金é¢ï¼šåˆåŒé‡‘é¢æŽ’è¡Œã€å›žæ¬¾é‡‘é¢æŽ’è¡Œ + * 2. 个数:签约åˆåŒæŽ’è¡Œã€äº§å“销é‡æŽ’è¡Œã€äº§å“销é‡æŽ’è¡Œã€æ–°å¢žå®¢æˆ·æ•°æŽ’è¡Œã€æ–°å¢žè”系人排行ã€è·Ÿè¿›æ¬¡æ•°æŽ’è¡Œã€è·Ÿè¿›å®¢æˆ·æ•°æŽ’è¡Œ + */ + @Schema(description = "æ•°é‡", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer count; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/vo/CrmBiRankReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/vo/CrmBiRankReqVO.java new file mode 100644 index 000000000..6d36f6d6f --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/bi/vo/CrmBiRankReqVO.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.crm.controller.admin.bi.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管ç†åŽå° - CRM BI 排行榜 Request VO") +@Data +public class CrmBiRankReqVO { + + @Schema(description = "部门 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "部门 id ä¸èƒ½ä¸ºç©º") + private Long deptId; + + /** + * userIds ç›®å‰ä¸ç”¨å‰ç«¯ä¼ é€’,目å‰æ˜¯æ–¹ä¾¿åŽç«¯é€šè¿‡ deptId 读å–ç¼–å·åŽï¼Œè®¾ç½®å›žæ¥ + * + * åŽç»­ï¼Œå¯èƒ½ä¼šæ”¯æŒé€‰æ‹©éƒ¨åˆ†ç”¨æˆ·è¿›è¡ŒæŸ¥è¯¢ + */ + @Schema(description = "负责人用户 id 集åˆ", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2") + private List userIds; + + @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.REQUIRED) + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @NotEmpty(message = "时间范围ä¸èƒ½ä¸ºç©º") + private LocalDateTime[] times; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.http b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.http new file mode 100644 index 000000000..55adb4bd5 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.http @@ -0,0 +1,32 @@ +### 请求 /transfer +PUT {{baseUrl}}/crm/business/transfer +Content-Type: application/json +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} + +{ + "id": 1, + "ownerUserId": 2, + "transferType": 2, + "permissionType": 2 +} + +### 请求 /update +PUT {{baseUrl}}/crm/business/update +Content-Type: application/json +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} + +{ + "id": 1, + "name": "2", + "statusTypeId": 2, + "statusId": 2, + "customerId": 1 +} + +### 请求 /get +GET {{baseUrl}}/crm/business/get?id=1024 +Content-Type: application/json +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java new file mode 100644 index 000000000..c85c151f5 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java @@ -0,0 +1,177 @@ +package cn.iocoder.yudao.module.crm.controller.admin.business; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessSaveReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO; +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.dal.dataobject.business.CrmBusinessStatusDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessStatusService; +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessStatusTypeService; +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 jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CUSTOMER_NOT_EXISTS; + +@Tag(name = "管ç†åŽå° - CRM 商机") +@RestController +@RequestMapping("/crm/business") +@Validated +public class CrmBusinessController { + + @Resource + private CrmBusinessService businessService; + @Resource + private CrmCustomerService customerService; + @Resource + private CrmBusinessStatusTypeService businessStatusTypeService; + @Resource + private CrmBusinessStatusService businessStatusService; + + @PostMapping("/create") + @Operation(summary = "创建商机") + @PreAuthorize("@ss.hasPermission('crm:business:create')") + public CommonResult createBusiness(@Valid @RequestBody CrmBusinessSaveReqVO createReqVO) { + return success(businessService.createBusiness(createReqVO, getLoginUserId())); + } + + @PutMapping("/update") + @Operation(summary = "更新商机") + @PreAuthorize("@ss.hasPermission('crm:business:update')") + public CommonResult updateBusiness(@Valid @RequestBody CrmBusinessSaveReqVO 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 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 getBusiness(@RequestParam("id") Long id) { + CrmBusinessDO business = businessService.getBusiness(id); + return success(BeanUtils.toBean(business, CrmBusinessRespVO.class)); + } + + @GetMapping("/list-by-ids") + @Operation(summary = "获得商机列表") + @Parameter(name = "ids", description = "ç¼–å·", required = true, example = "[1024]") + @PreAuthorize("@ss.hasPermission('crm:business:query')") + public CommonResult> getContactListByIds(@RequestParam("ids") List ids) { + return success(BeanUtils.toBean(businessService.getBusinessList(ids, getLoginUserId()), CrmBusinessRespVO.class)); + } + + @GetMapping("/simple-all-list") + @Operation(summary = "获得è”系人的精简列表") + @PreAuthorize("@ss.hasPermission('crm:contact:query')") + public CommonResult> getSimpleContactList() { + CrmBusinessPageReqVO reqVO = new CrmBusinessPageReqVO(); + reqVO.setPageSize(PAGE_SIZE_NONE); // ä¸åˆ†é¡µ + PageResult pageResult = businessService.getBusinessPage(reqVO, getLoginUserId()); + return success(convertList(pageResult.getList(), business -> // åªè¿”回 idã€name 字段 + new CrmBusinessRespVO().setId(business.getId()).setName(business.getName()))); + } + + @GetMapping("/page") + @Operation(summary = "获得商机分页") + @PreAuthorize("@ss.hasPermission('crm:business:query')") + public CommonResult> getBusinessPage(@Valid CrmBusinessPageReqVO pageVO) { + PageResult pageResult = businessService.getBusinessPage(pageVO, getLoginUserId()); + return success(buildBusinessDetailPageResult(pageResult)); + } + + @GetMapping("/page-by-customer") + @Operation(summary = "获得商机分页,基于指定客户") + public CommonResult> getBusinessPageByCustomer(@Valid CrmBusinessPageReqVO pageReqVO) { + if (pageReqVO.getCustomerId() == null) { + throw exception(CUSTOMER_NOT_EXISTS); + } + PageResult pageResult = businessService.getBusinessPageByCustomerId(pageReqVO); + return success(buildBusinessDetailPageResult(pageResult)); + } + + @GetMapping("/page-by-contact") + @Operation(summary = "获得è”系人的商机分页") + @PreAuthorize("@ss.hasPermission('crm:business:query')") + public CommonResult> getBusinessContactPage(@Valid CrmBusinessPageReqVO pageReqVO) { + PageResult pageResult = businessService.getBusinessPageByContact(pageReqVO); + return success(buildBusinessDetailPageResult(pageResult)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出商机 Excel") + @PreAuthorize("@ss.hasPermission('crm:business:export')") + @OperateLog(type = EXPORT) + public void exportBusinessExcel(@Valid CrmBusinessPageReqVO exportReqVO, + HttpServletResponse response) throws IOException { + exportReqVO.setPageSize(PAGE_SIZE_NONE); + PageResult pageResult = businessService.getBusinessPage(exportReqVO, getLoginUserId()); + // 导出 Excel + ExcelUtils.write(response, "商机.xls", "æ•°æ®", CrmBusinessRespVO.class, + buildBusinessDetailPageResult(pageResult).getList()); + } + + /** + * 构建详细的商机分页结果 + * + * @param pageResult 简å•çš„商机分页结果 + * @return 详细的商机分页结果 + */ + private PageResult buildBusinessDetailPageResult(PageResult pageResult) { + if (CollUtil.isEmpty(pageResult.getList())) { + return PageResult.empty(pageResult.getTotal()); + } + List statusTypeList = businessStatusTypeService.getBusinessStatusTypeList( + convertSet(pageResult.getList(), CrmBusinessDO::getStatusTypeId)); + List statusList = businessStatusService.getBusinessStatusList( + convertSet(pageResult.getList(), CrmBusinessDO::getStatusId)); + List customerList = customerService.getCustomerList( + convertSet(pageResult.getList(), CrmBusinessDO::getCustomerId)); + return CrmBusinessConvert.INSTANCE.convertPage(pageResult, customerList, statusTypeList, statusList); + } + + @PutMapping("/transfer") + @Operation(summary = "商机转移") + @PreAuthorize("@ss.hasPermission('crm:business:update')") + public CommonResult transferBusiness(@Valid @RequestBody CrmBusinessTransferReqVO reqVO) { + businessService.transferBusiness(reqVO, getLoginUserId()); + return success(true); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessStatusTypeController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessStatusTypeController.java new file mode 100644 index 000000000..86a15dcaf --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessStatusTypeController.java @@ -0,0 +1,141 @@ +package cn.iocoder.yudao.module.crm.controller.admin.business; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusQueryVO; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypePageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeQueryVO; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeSaveReqVO; +import cn.iocoder.yudao.module.crm.convert.business.CrmBusinessStatusConvert; +import cn.iocoder.yudao.module.crm.convert.business.CrmBusinessStatusTypeConvert; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO; +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessStatusService; +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessStatusTypeService; +import cn.iocoder.yudao.module.system.api.dept.DeptApi; +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; + +@Tag(name = "管ç†åŽå° - CRM 商机状æ€ç±»åž‹") +@RestController +@RequestMapping("/crm/business-status-type") +@Validated +public class CrmBusinessStatusTypeController { + + @Resource + private CrmBusinessStatusTypeService businessStatusTypeService; + + @Resource + private CrmBusinessStatusService businessStatusService; + + @Resource + private DeptApi deptApi; + + @PostMapping("/create") + @Operation(summary = "创建商机状æ€ç±»åž‹") + @PreAuthorize("@ss.hasPermission('crm:business-status-type:create')") + public CommonResult createBusinessStatusType(@Valid @RequestBody CrmBusinessStatusTypeSaveReqVO createReqVO) { + return success(businessStatusTypeService.createBusinessStatusType(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新商机状æ€ç±»åž‹") + @PreAuthorize("@ss.hasPermission('crm:business-status-type:update')") + public CommonResult updateBusinessStatusType(@Valid @RequestBody CrmBusinessStatusTypeSaveReqVO 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 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 getBusinessStatusType(@RequestParam("id") Long id) { + CrmBusinessStatusTypeDO statusType = businessStatusTypeService.getBusinessStatusType(id); + // 处ç†çŠ¶æ€å›žæ˜¾ + // TODO @lzxhqs:å¯ä»¥åœ¨ businessStatusService 加个 getBusinessStatusListByTypeId 方法,直接返回 List 哈,常用的,尽é‡å°è£…个简å•æ˜“懂的方法,ä¸ç”¨è¿½æ±‚ç»å¯¹é€šç”¨å“ˆï¼› + CrmBusinessStatusQueryVO queryVO = new CrmBusinessStatusQueryVO(); + queryVO.setTypeId(id); + List statusList = businessStatusService.selectList(queryVO); + return success(CrmBusinessStatusTypeConvert.INSTANCE.convert(statusType, statusList)); + } + + @GetMapping("/page") + @Operation(summary = "获得商机状æ€ç±»åž‹åˆ†é¡µ") + @PreAuthorize("@ss.hasPermission('crm:business-status-type:query')") + public CommonResult> getBusinessStatusTypePage(@Valid CrmBusinessStatusTypePageReqVO pageReqVO) { + PageResult pageResult = businessStatusTypeService.getBusinessStatusTypePage(pageReqVO); + // 处ç†éƒ¨é—¨å›žæ˜¾ + Set deptIds = CollectionUtils.convertSetByFlatMap(pageResult.getList(), CrmBusinessStatusTypeDO::getDeptIds,Collection::stream); + List deptList = deptApi.getDeptList(deptIds).getCheckedData(); + return success(CrmBusinessStatusTypeConvert.INSTANCE.convertPage(pageResult, deptList)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出商机状æ€ç±»åž‹ Excel") + @PreAuthorize("@ss.hasPermission('crm:business-status-type:export')") + @OperateLog(type = EXPORT) + public void exportBusinessStatusTypeExcel(@Valid CrmBusinessStatusTypePageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = businessStatusTypeService.getBusinessStatusTypePage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "商机状æ€ç±»åž‹.xls", "æ•°æ®", CrmBusinessStatusTypeRespVO.class, + BeanUtils.toBean(list, CrmBusinessStatusTypeRespVO.class)); + } + + @GetMapping("/get-simple-list") + @Operation(summary = "获得商机状æ€ç±»åž‹åˆ—表") + @PreAuthorize("@ss.hasPermission('crm:business-status-type:query')") + public CommonResult> getBusinessStatusTypeList() { + CrmBusinessStatusTypeQueryVO queryVO = new CrmBusinessStatusTypeQueryVO(); + queryVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + List list = businessStatusTypeService.selectList(queryVO); + return success(BeanUtils.toBean(list, CrmBusinessStatusTypeRespVO.class)); + } + + // TODO @ljlleo 这个接å£ï¼Œæ˜¯ä¸æ˜¯å¯ä»¥å’Œ getBusinessStatusTypeList åˆå¹¶æˆä¸€ä¸ªï¼Ÿ + @GetMapping("/get-status-list") + @Operation(summary = "获得商机状æ€åˆ—表") + @PreAuthorize("@ss.hasPermission('crm:business-status:query')") + public CommonResult> getBusinessStatusListByTypeId(@RequestParam("typeId") Long typeId) { + CrmBusinessStatusQueryVO queryVO = new CrmBusinessStatusQueryVO(); + queryVO.setTypeId(typeId); + List list = businessStatusService.selectList(queryVO); + return success(CrmBusinessStatusConvert.INSTANCE.convertList(list)); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessExcelVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessExcelVO.java new file mode 100644 index 000000000..a11949ecd --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessExcelVO.java @@ -0,0 +1,75 @@ +package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business; + +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 roUserIds; + + @ExcelProperty("读写æƒé™çš„用户编å·æ•°ç»„") + private Set rwUserIds; + + @ExcelProperty("1èµ¢å•2输å•3无效") + private Integer endStatus; + + @ExcelProperty("结æŸæ—¶çš„备注") + private String endRemark; + + @ExcelProperty("最åŽè·Ÿè¿›æ—¶é—´") + private LocalDateTime contactLastTime; + + @ExcelProperty("跟进状æ€") + private Integer followUpStatus; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessPageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessPageReqVO.java new file mode 100644 index 000000000..0e47bf5be --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessPageReqVO.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum; +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; + + @Schema(description = "客户编å·", example = "10795") + private Long customerId; + + @Schema(description = "è”系人编å·", example = "10795") + private Long contactId; + + @Schema(description = "场景类型", example = "1") + @InEnum(CrmSceneTypeEnum.class) + private Integer sceneType; // 场景类型,为 null 时则表示全部 + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessRespVO.java new file mode 100644 index 000000000..d3b6ab2fb --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessRespVO.java @@ -0,0 +1,69 @@ +package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +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 = "管ç†åŽå° - 商机 Response VO") +@Data +public class CrmBusinessRespVO { + + @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "32129") + private Long id; + + @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; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "客户å称", requiredMode = Schema.RequiredMode.REQUIRED, example = "æŽå››") + private String customerName; + + @Schema(description = "状æ€ç±»åž‹å称", requiredMode = Schema.RequiredMode.REQUIRED, example = "进行中") + private String statusTypeName; + + @Schema(description = "状æ€å称", requiredMode = Schema.RequiredMode.REQUIRED, example = "跟进中") + private String statusName; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessSaveReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessSaveReqVO.java new file mode 100644 index 000000000..0be6264eb --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessSaveReqVO.java @@ -0,0 +1,103 @@ +package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.crm.enums.business.CrmBizEndStatus; +import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerParseFunction; +import com.mzt.logapi.starter.annotation.DiffLogField; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.format.annotation.DateTimeFormat; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管ç†åŽå° - CRM 商机创建/æ›´æ–° Request VO") +@Data +public class CrmBusinessSaveReqVO { + + @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "32129") + private Long id; + + @Schema(description = "商机å称", requiredMode = Schema.RequiredMode.REQUIRED, example = "æŽå››") + @DiffLogField(name = "商机å称") + @NotNull(message = "商机å称ä¸èƒ½ä¸ºç©º") + private String name; + + @Schema(description = "商机状æ€ç±»åž‹ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "25714") + @DiffLogField(name = "商机状æ€") + @NotNull(message = "商机状æ€ç±»åž‹ä¸èƒ½ä¸ºç©º") + private Long statusTypeId; + + @Schema(description = "商机状æ€ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320") + @DiffLogField(name = "商机状æ€") + @NotNull(message = "商机状æ€ä¸èƒ½ä¸ºç©º") + private Long statusId; + + @Schema(description = "下次è”系时间") + @DiffLogField(name = "下次è”系时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime contactNextTime; + + @Schema(description = "客户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "10299") + @DiffLogField(name = "客户", function = CrmCustomerParseFunction.NAME) + @NotNull(message = "客户ä¸èƒ½ä¸ºç©º") + private Long customerId; + + @Schema(description = "预计æˆäº¤æ—¥æœŸ") + @DiffLogField(name = "预计æˆäº¤æ—¥æœŸ") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime dealTime; + + @Schema(description = "商机金é¢", example = "12371") + @DiffLogField(name = "商机金é¢") + private Integer price; + + @Schema(description = "æ•´å•æŠ˜æ‰£") + @DiffLogField(name = "æ•´å•æŠ˜æ‰£") + private Integer discountPercent; + + @Schema(description = "产å“总金é¢", example = "12025") + @DiffLogField(name = "产å“总金é¢") + private BigDecimal productPrice; + + @Schema(description = "备注", example = "éšä¾¿") + @DiffLogField(name = "备注") + private String remark; + + @Schema(description = "结æŸçŠ¶æ€", example = "1") + @InEnum(CrmBizEndStatus.class) + private Integer endStatus; + + @Schema(description = "è”系人编å·", example = "110") + private Long contactId; // 使用场景,在ã€è”系人详情】添加商机时,如果需è¦å…³è”两者,需è¦ä¼ é€’ contactId 字段 + + // TODO @puhui999:传递 items 就行啦; + @Schema(description = "产å“列表") + private List productItems; + + @Schema(description = "产å“列表") + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class CrmBusinessProductItem { + + @Schema(description = "产å“ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "20529") + @NotNull(message = "产å“ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long id; + + @Schema(description = "产å“æ•°é‡", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911") + @NotNull(message = "产å“æ•°é‡ä¸èƒ½ä¸ºç©º") + private Integer count; + + @Schema(description = "产å“折扣") + private Integer discountPercent; + + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessTransferReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessTransferReqVO.java new file mode 100644 index 000000000..a76c48cae --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessTransferReqVO.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business; + +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管ç†åŽå° - 商机转移 Request VO") +@Data +public class CrmBusinessTransferReqVO { + + @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 newOwnerUserId; + + /** + * è€è´Ÿè´£äººåŠ å…¥å›¢é˜ŸåŽçš„æƒé™çº§åˆ«ã€‚如果 null 说明移除 + * + * å…³è” {@link CrmPermissionLevelEnum} + */ + @Schema(description = "è€è´Ÿè´£äººåŠ å…¥å›¢é˜ŸåŽçš„æƒé™çº§åˆ«", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Integer oldOwnerPermissionLevel; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/status/CrmBusinessStatusPageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/status/CrmBusinessStatusPageReqVO.java new file mode 100644 index 000000000..b91a954e0 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/status/CrmBusinessStatusPageReqVO.java @@ -0,0 +1,15 @@ +package cn.iocoder.yudao.module.crm.controller.admin.business.vo.status; + +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 { + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/status/CrmBusinessStatusQueryVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/status/CrmBusinessStatusQueryVO.java new file mode 100644 index 000000000..fbf4d06e1 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/status/CrmBusinessStatusQueryVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.crm.controller.admin.business.vo.status; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.ToString; + +import java.util.Collection; + +@Schema(description = "管ç†åŽå° - å•†æœºçŠ¶æ€ Query VO") +@Data +@ToString(callSuper = true) +public class CrmBusinessStatusQueryVO { + + @Schema(description = "主键集åˆ") + private Collection idList; + + @Schema(description = "状æ€ç±»åž‹ç¼–å·") + private Long typeId; +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/status/CrmBusinessStatusRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/status/CrmBusinessStatusRespVO.java new file mode 100644 index 000000000..405a832a5 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/status/CrmBusinessStatusRespVO.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.crm.controller.admin.business.vo.status; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管ç†åŽå° - å•†æœºçŠ¶æ€ Response VO") +@Data +@ExcelIgnoreUnannotated +public class CrmBusinessStatusRespVO { + + @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "23899") + @ExcelProperty("主键") + private Long id; + + @Schema(description = "状æ€ç±»åž‹ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "7139") + @ExcelProperty("状æ€ç±»åž‹ç¼–å·") + private Long typeId; + + @Schema(description = "状æ€å", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") + @ExcelProperty("状æ€å") + private String name; + + @Schema(description = "èµ¢å•çŽ‡") + @ExcelProperty("èµ¢å•çŽ‡") + private String percent; + + @Schema(description = "排åº", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty("排åº") + private Integer sort; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/status/CrmBusinessStatusSaveReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/status/CrmBusinessStatusSaveReqVO.java new file mode 100644 index 000000000..3327b09f7 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/status/CrmBusinessStatusSaveReqVO.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.crm.controller.admin.business.vo.status; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +@Schema(description = "管ç†åŽå° - 商机状æ€æ–°å¢ž/修改 Request VO") +@Data +public class CrmBusinessStatusSaveReqVO { + + @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "23899") + private Long id; + + @Schema(description = "状æ€ç±»åž‹ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "7139") + @NotNull(message = "状æ€ç±»åž‹ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long typeId; + + @Schema(description = "状æ€å", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") + @NotEmpty(message = "状æ€åä¸èƒ½ä¸ºç©º") + private String name; + + // TODO @lzxhqs::percent 应该是 Integerï¼› + @Schema(description = "èµ¢å•çŽ‡") + private String percent; + + // TODO @lzxhqs:这个是ä¸æ˜¯ä¸ç”¨å‰ç«¯æ–°å¢žå’Œä¿®æ”¹çš„时候传递,交给顺åºè®¡ç®—出æ¥ï¼Œå­˜å‚¨èµ·æ¥å°±å¥½äº†ï¼› + @Schema(description = "排åº") + private Integer sort; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/type/CrmBusinessStatusTypePageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/type/CrmBusinessStatusTypePageReqVO.java new file mode 100644 index 000000000..03b113cc7 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/type/CrmBusinessStatusTypePageReqVO.java @@ -0,0 +1,15 @@ +package cn.iocoder.yudao.module.crm.controller.admin.business.vo.type; + +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 { + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/type/CrmBusinessStatusTypeQueryVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/type/CrmBusinessStatusTypeQueryVO.java new file mode 100644 index 000000000..9c78f1afc --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/type/CrmBusinessStatusTypeQueryVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.crm.controller.admin.business.vo.type; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.ToString; + +import java.util.Collection; + +@Schema(description = "管ç†åŽå° - 商机状æ€ç±»åž‹ Query VO") +@Data +@ToString(callSuper = true) +public class CrmBusinessStatusTypeQueryVO { + + @Schema(description = "主键集åˆ") + private Collection idList; + + @Schema(description = "状æ€") + private Integer status; +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/type/CrmBusinessStatusTypeRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/type/CrmBusinessStatusTypeRespVO.java new file mode 100644 index 000000000..9d13d5dc3 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/type/CrmBusinessStatusTypeRespVO.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.crm.controller.admin.business.vo.type; + +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管ç†åŽå° - 商机状æ€ç±»åž‹ Response VO") +@Data +@ExcelIgnoreUnannotated +public class CrmBusinessStatusTypeRespVO { + + @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "2934") + @ExcelProperty("主键") + private Long id; + + @Schema(description = "状æ€ç±»åž‹å", requiredMode = Schema.RequiredMode.REQUIRED, example = "æŽå››") + @ExcelProperty("状æ€ç±»åž‹å") + private String name; + + @Schema(description = "使用的部门编å·", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("使用的部门编å·") + private List deptIds; + @Schema(description = "使用的部门å称", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("使用的部门å称") + private List deptNames; + + @Schema(description = "创建人", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建人") + private String creator; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + // TODO @ljlleo 字段åŽç¼€æ”¹æˆ statuses,ä¿æŒå’Œ deptIds 风格一致;CrmBusinessStatusDO æ”¹æˆ VO 哈;一般ä¸ä½¿ç”¨ do 直接返回 + @Schema(description = "状æ€é›†åˆ", requiredMode = Schema.RequiredMode.REQUIRED) + private List statusList; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/type/CrmBusinessStatusTypeSaveReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/type/CrmBusinessStatusTypeSaveReqVO.java new file mode 100644 index 000000000..23dc7742d --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/type/CrmBusinessStatusTypeSaveReqVO.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.crm.controller.admin.business.vo.type; + +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusSaveReqVO; +import com.google.common.collect.Lists; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotEmpty; +import java.util.List; + +@Schema(description = "管ç†åŽå° - 商机状æ€ç±»åž‹æ–°å¢ž/修改 Request VO") +@Data +public class CrmBusinessStatusTypeSaveReqVO { + + @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "2934") + private Long id; + + @Schema(description = "状æ€ç±»åž‹å", requiredMode = Schema.RequiredMode.REQUIRED, example = "æŽå››") + @NotEmpty(message = "状æ€ç±»åž‹åä¸èƒ½ä¸ºç©º") + private String name; + + // TODO @lzxhqs: VO 里é¢ï¼Œæˆ‘们ä¸ä½¿ç”¨é»˜è®¤å€¼å“ˆã€‚这里 Lists.newArrayList() çœ‹çœ‹æ€Žä¹ˆåŽ»æŽ‰ã€‚ä¸Šé¢ deptIds 也是类似噢 + @Schema(description = "使用的部门编å·", requiredMode = Schema.RequiredMode.REQUIRED) + private List deptIds = Lists.newArrayList(); + + @Schema(description = "商机状æ€é›†åˆ", requiredMode = Schema.RequiredMode.REQUIRED) + private List statusList; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java new file mode 100644 index 000000000..8be62ae26 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java @@ -0,0 +1,107 @@ +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.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.*; +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 jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; +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/clue") +@Validated +public class CrmClueController { + + @Resource + private CrmClueService clueService; + + @PostMapping("/create") + @Operation(summary = "创建线索") + @PreAuthorize("@ss.hasPermission('crm:clue:create')") + public CommonResult createClue(@Valid @RequestBody CrmClueSaveReqVO createReqVO) { + return success(clueService.createClue(createReqVO, getLoginUserId())); + } + + @PutMapping("/update") + @Operation(summary = "更新线索") + @PreAuthorize("@ss.hasPermission('crm:clue:update')") + public CommonResult updateClue(@Valid @RequestBody CrmClueSaveReqVO 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 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 getClue(@RequestParam("id") Long id) { + CrmClueDO clue = clueService.getClue(id); + return success(BeanUtils.toBean(clue, CrmClueRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得线索分页") + @PreAuthorize("@ss.hasPermission('crm:clue:query')") + public CommonResult> getCluePage(@Valid CrmCluePageReqVO pageVO) { + PageResult pageResult = clueService.getCluePage(pageVO, getLoginUserId()); + return success(BeanUtils.toBean(pageResult, CrmClueRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出线索 Excel") + @PreAuthorize("@ss.hasPermission('crm:clue:export')") + @OperateLog(type = EXPORT) + public void exportClueExcel(@Valid CrmCluePageReqVO pageReqVO, HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PAGE_SIZE_NONE); + List list = clueService.getCluePage(pageReqVO, getLoginUserId()).getList(); + // 导出 Excel + List datas = BeanUtils.toBean(list, CrmClueRespVO.class); + ExcelUtils.write(response, "线索.xls", "æ•°æ®", CrmClueRespVO.class, datas); + } + + @PutMapping("/transfer") + @Operation(summary = "线索转移") + @PreAuthorize("@ss.hasPermission('crm:clue:update')") + public CommonResult transferClue(@Valid @RequestBody CrmClueTransferReqVO reqVO) { + clueService.transferClue(reqVO, getLoginUserId()); + return success(true); + } + + @PostMapping("/transform") + @Operation(summary = "线索转化为客户") + @PreAuthorize("@ss.hasPermission('crm:clue:update')") + public CommonResult translateCustomer(@Valid @RequestBody CrmClueTranslateReqVO reqVO) { + clueService.translateCustomer(reqVO, getLoginUserId()); + return success(Boolean.TRUE); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmCluePageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmCluePageReqVO.java new file mode 100644 index 000000000..3ba823d54 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmCluePageReqVO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.crm.controller.admin.clue.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum; +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; + + @Schema(description = "场景类型", example = "1") + @InEnum(CrmSceneTypeEnum.class) + private Integer sceneType; // 场景类型,为 null 时则表示全部 + + @Schema(description = "是å¦ä¸ºå…¬æµ·æ•°æ®", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + private Boolean pool; // null 则表示为ä¸æ˜¯å…¬æµ·æ•°æ® + + @Schema(description = "所属行业", example = "1") + private Integer industryId; + + @Schema(description = "客户等级", example = "1") + private Integer level; + + @Schema(description = "客户æ¥æº", example = "1") + private Integer source; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueRespVO.java new file mode 100644 index 000000000..35d30956e --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueRespVO.java @@ -0,0 +1,115 @@ +package cn.iocoder.yudao.module.crm.controller.admin.clue.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.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +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 = "管ç†åŽå° - 线索 Response VO") +@Data +@ToString(callSuper = true) +@ExcelIgnoreUnannotated +public class CrmClueRespVO { + + @Schema(description = "ç¼–å·ï¼Œä¸»é”®è‡ªå¢ž", requiredMode = Schema.RequiredMode.REQUIRED, example = "10969") + @ExcelProperty("ç¼–å·") + private Long id; + + @Schema(description = "转化状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @ExcelProperty(value = "转化状æ€", converter = DictConvert.class) + @DictFormat(DictTypeConstants.BOOLEAN_STRING) + private Boolean transformStatus; + + @Schema(description = "跟进状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @ExcelProperty(value = "跟进状æ€", converter = DictConvert.class) + @DictFormat(DictTypeConstants.BOOLEAN_STRING) + private Boolean followUpStatus; + + @Schema(description = "线索å称", requiredMode = Schema.RequiredMode.REQUIRED, example = "线索xxx") + @ExcelProperty("线索å称") + private String name; + + @Schema(description = "客户 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "520") + // TODO 这里需è¦å¯¼å‡ºæˆå®¢æˆ·å称 + @ExcelProperty("客户id") + private Long customerId; + + @Schema(description = "下次è”系时间", example = "2023-10-18 01:00:00") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ExcelProperty("下次è”系时间") + private LocalDateTime contactNextTime; + + @Schema(description = "电è¯", example = "18000000000") + @ExcelProperty("电è¯") + private String telephone; + + @Schema(description = "手机å·", example = "18000000000") + @ExcelProperty("手机å·") + private String mobile; + + @Schema(description = "地å€", example = "北京市海淀区") + @ExcelProperty("地å€") + private String address; + + @Schema(description = "负责人编å·") + @ExcelProperty("负责人的用户编å·") + private Long ownerUserId; + + @Schema(description = "最åŽè·Ÿè¿›æ—¶é—´") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @ExcelProperty("最åŽè·Ÿè¿›æ—¶é—´") + private LocalDateTime contactLastTime; + + @Schema(description = "备注", example = "éšä¾¿") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "所属行业", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") + @ExcelProperty(value = "所属行业", converter = DictConvert.class) + @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY) + private Integer industryId; + + @Schema(description = "客户等级", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") + @ExcelProperty(value = "客户等级", converter = DictConvert.class) + @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_LEVEL) + private Integer level; + + @Schema(description = "客户æ¥æº", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") + @ExcelProperty(value = "客户æ¥æº", converter = DictConvert.class) + @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_SOURCE) + private Integer source; + + @Schema(description = "网å€", example = "25682") + @ExcelProperty("网å€") + private String website; + + @Schema(description = "QQ", example = "25682") + @ExcelProperty("QQ") + private String qq; + + @Schema(description = "wechat", example = "25682") + @ExcelProperty("wechat") + private String wechat; + + @Schema(description = "email", example = "25682") + @ExcelProperty("email") + private String email; + + @Schema(description = "客户æè¿°", example = "25682") + @ExcelProperty("客户æè¿°") + private String description; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueSaveReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueSaveReqVO.java new file mode 100644 index 000000000..4ca004a59 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueSaveReqVO.java @@ -0,0 +1,105 @@ +package cn.iocoder.yudao.module.crm.controller.admin.clue.vo; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.framework.common.validation.Mobile; +import cn.iocoder.yudao.framework.common.validation.Telephone; +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLevelEnum; +import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerIndustryParseFunction; +import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerLevelParseFunction; +import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerSourceParseFunction; +import com.mzt.logapi.starter.annotation.DiffLogField; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Size; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; +import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY; + +@Schema(description = "管ç†åŽå° - CRM 线索 创建/æ›´æ–° Request VO") +@Data +public class CrmClueSaveReqVO { + + @Schema(description = "ç¼–å·", example = "10969") + private Long id; + + @Schema(description = "线索å称", requiredMode = Schema.RequiredMode.REQUIRED, example = "线索xxx") + @DiffLogField(name = "线索å称") + @NotEmpty(message = "线索å称ä¸èƒ½ä¸ºç©º") + private String name; + + @Schema(description = "下次è”系时间", example = "2023-10-18 01:00:00") + @DiffLogField(name = "下次è”系时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime contactNextTime; + + @Schema(description = "电è¯", example = "18000000000") + @DiffLogField(name = "电è¯") + @Telephone + private String telephone; + + @Schema(description = "手机å·", example = "18000000000") + @DiffLogField(name = "手机å·") + @Mobile + private String mobile; + + @Schema(description = "地å€", example = "北京市海淀区") + @DiffLogField(name = "地å€") + private String address; + + @Schema(description = "最åŽè·Ÿè¿›æ—¶é—´") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @DiffLogField(name = "最åŽè·Ÿè¿›æ—¶é—´") + private LocalDateTime contactLastTime; + + @Schema(description = "负责人编å·", example = "2048") + private Long ownerUserId; + + @Schema(description = "备注", example = "éšä¾¿") + @DiffLogField(name = "备注") + private String remark; + + @Schema(description = "所属行业", example = "1") + @DiffLogField(name = "所属行业", function = CrmCustomerIndustryParseFunction.NAME) + @DictFormat(CRM_CUSTOMER_INDUSTRY) + private Integer industryId; + + @Schema(description = "客户等级", example = "2") + @DiffLogField(name = "客户等级", function = CrmCustomerLevelParseFunction.NAME) + @InEnum(CrmCustomerLevelEnum.class) + private Integer level; + + @Schema(description = "客户æ¥æº", example = "3") + @DiffLogField(name = "客户æ¥æº", function = CrmCustomerSourceParseFunction.NAME) + private Integer source; + + @Schema(description = "网å€", example = "https://www.baidu.com") + @DiffLogField(name = "网å€") + private String website; + + @Schema(description = "QQ", example = "123456789") + @DiffLogField(name = "QQ") + @Size(max = 20, message = "QQ长度ä¸èƒ½è¶…过 20 个字符") + private String qq; + + @Schema(description = "微信", example = "123456789") + @DiffLogField(name = "微信") + @Size(max = 255, message = "微信长度ä¸èƒ½è¶…过 255 个字符") + private String wechat; + + @Schema(description = "邮箱", example = "123456789@qq.com") + @DiffLogField(name = "邮箱") + @Email(message = "邮箱格å¼ä¸æ­£ç¡®") + @Size(max = 255, message = "邮箱长度ä¸èƒ½è¶…过 255 个字符") + private String email; + + @Schema(description = "客户æè¿°", example = "ä»»æ„文字") + @DiffLogField(name = "客户æè¿°") + @Size(max = 4096, message = "客户æ述长度ä¸èƒ½è¶…过 4096 个字符") + private String description; +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueTransferReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueTransferReqVO.java new file mode 100644 index 000000000..63bdc1838 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueTransferReqVO.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.crm.controller.admin.clue.vo; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管ç†åŽå° - 线索转移 Request VO") +@Data +public class CrmClueTransferReqVO { + + @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 newOwnerUserId; + + @Schema(description = "è€è´Ÿè´£äººåŠ å…¥å›¢é˜ŸåŽçš„æƒé™çº§åˆ«", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @InEnum(value = CrmPermissionLevelEnum.class) + private Integer oldOwnerPermissionLevel; // è€è´Ÿè´£äººåŠ å…¥å›¢é˜ŸåŽçš„æƒé™çº§åˆ«ã€‚如果 null 说明移除 + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueTranslateReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueTranslateReqVO.java new file mode 100644 index 000000000..03a4d78f1 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueTranslateReqVO.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.crm.controller.admin.clue.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.util.Set; + +@Schema(description = "管ç†åŽå° - 线索转化为客户 Request VO") +@Data +public class CrmClueTranslateReqVO { + + @Schema(description = "线索编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1024, 1025]") + @NotEmpty(message = "线索编å·ä¸èƒ½ä¸ºç©º") + private Set ids; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/CrmContactController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/CrmContactController.java new file mode 100644 index 000000000..766adb02d --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/CrmContactController.java @@ -0,0 +1,204 @@ +package cn.iocoder.yudao.module.crm.controller.admin.contact; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.NumberUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.*; +import cn.iocoder.yudao.module.crm.convert.contact.CrmContactConvert; +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants; +import cn.iocoder.yudao.module.crm.service.contact.CrmContactBusinessService; +import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; +import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import com.google.common.collect.Lists; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; +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 +@Slf4j +public class CrmContactController { + + @Resource + private CrmContactService contactService; + @Resource + private CrmCustomerService customerService; + @Resource + private CrmContactBusinessService contactBusinessLinkService; + + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建è”系人") + @PreAuthorize("@ss.hasPermission('crm:contact:create')") + public CommonResult createContact(@Valid @RequestBody CrmContactSaveReqVO createReqVO) { + return success(contactService.createContact(createReqVO, getLoginUserId())); + } + + @PutMapping("/update") + @Operation(summary = "æ›´æ–°è”系人") + @OperateLog(enable = false) + @PreAuthorize("@ss.hasPermission('crm:contact:update')") + public CommonResult updateContact(@Valid @RequestBody CrmContactSaveReqVO updateReqVO) { + contactService.updateContact(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除è”系人") + @Parameter(name = "id", description = "ç¼–å·", required = true) + @PreAuthorize("@ss.hasPermission('crm:contact:delete')") + public CommonResult deleteContact(@RequestParam("id") Long id) { + contactService.deleteContact(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得è”系人") + @Parameter(name = "id", description = "ç¼–å·", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('crm:contact:query')") + public CommonResult getContact(@RequestParam("id") Long id) { + CrmContactDO contact = contactService.getContact(id); + if (contact == null) { + throw exception(ErrorCodeConstants.CONTACT_NOT_EXISTS); + } + // 1. 获å–用户å + Map userMap = adminUserApi.getUserMap(CollUtil.removeNull(Lists.newArrayList( + NumberUtil.parseLong(contact.getCreator()), contact.getOwnerUserId()))); + // 2. 获å–å®¢æˆ·ä¿¡æ¯ + List customerList = customerService.getCustomerList( + Collections.singletonList(contact.getCustomerId())); + // 3. 直属上级 + List parentContactList = contactService.getContactListByIds( + Collections.singletonList(contact.getParentId()), getLoginUserId()); + return success(CrmContactConvert.INSTANCE.convert(contact, userMap, customerList, parentContactList)); + } + + @GetMapping("/list-by-ids") + @Operation(summary = "获得è”系人列表") + @Parameter(name = "ids", description = "ç¼–å·", required = true, example = "[1024]") + @PreAuthorize("@ss.hasPermission('crm:contact:query')") + public CommonResult> getContactListByIds(@RequestParam("ids") List ids) { + return success(BeanUtils.toBean(contactService.getContactListByIds(ids, getLoginUserId()), CrmContactRespVO.class)); + } + + @GetMapping("/simple-all-list") + @Operation(summary = "获得è”系人的精简列表") + @PreAuthorize("@ss.hasPermission('crm:contact:query')") + public CommonResult> getSimpleContactList() { + List list = contactService.getSimpleContactList(getLoginUserId()); + return success(convertList(list, contact -> // åªè¿”回 idã€name 字段 + new CrmContactRespVO().setId(contact.getId()).setName(contact.getName()))); + } + + @GetMapping("/page") + @Operation(summary = "获得è”系人分页") + @PreAuthorize("@ss.hasPermission('crm:contact:query')") + public CommonResult> getContactPage(@Valid CrmContactPageReqVO pageVO) { + PageResult pageResult = contactService.getContactPage(pageVO, getLoginUserId()); + return success(buildContactDetailPage(pageResult)); + } + + @GetMapping("/page-by-customer") + @Operation(summary = "获得è”系人分页,基于指定客户") + public CommonResult> getContactPageByCustomer(@Valid CrmContactPageReqVO pageVO) { + Assert.notNull(pageVO.getCustomerId(), "客户编å·ä¸èƒ½ä¸ºç©º"); + PageResult pageResult = contactService.getContactPageByCustomerId(pageVO); + return success(buildContactDetailPage(pageResult)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出è”系人 Excel") + @PreAuthorize("@ss.hasPermission('crm:contact:export')") + @OperateLog(type = EXPORT) + public void exportContactExcel(@Valid CrmContactPageReqVO exportReqVO, + HttpServletResponse response) throws IOException { + exportReqVO.setPageNo(PAGE_SIZE_NONE); + PageResult pageResult = contactService.getContactPage(exportReqVO, getLoginUserId()); + ExcelUtils.write(response, "è”系人.xls", "æ•°æ®", CrmContactRespVO.class, + buildContactDetailPage(pageResult).getList()); + } + + /** + * 构建详细的è”系人分页结果 + * + * @param pageResult 简å•çš„è”系人分页结果 + * @return 详细的è”系人分页结果 + */ + private PageResult buildContactDetailPage(PageResult pageResult) { + List contactList = pageResult.getList(); + if (CollUtil.isEmpty(contactList)) { + return PageResult.empty(pageResult.getTotal()); + } + // 1. 获å–客户列表 + List crmCustomerDOList = customerService.getCustomerList( + convertSet(contactList, CrmContactDO::getCustomerId)); + // 2. 获å–创建人ã€è´Ÿè´£äººåˆ—表 + Map userMap = adminUserApi.getUserMap(convertListByFlatMap(contactList, + contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId()))); + // 3. 直属上级 + List parentContactList = contactService.getContactListByIds( + convertSet(contactList, CrmContactDO::getParentId), getLoginUserId()); + return CrmContactConvert.INSTANCE.convertPage(pageResult, userMap, crmCustomerDOList, parentContactList); + } + + @PutMapping("/transfer") + @Operation(summary = "è”系人转移") + @PreAuthorize("@ss.hasPermission('crm:contact:update')") + public CommonResult transferContact(@Valid @RequestBody CrmContactTransferReqVO reqVO) { + contactService.transferContact(reqVO, getLoginUserId()); + return success(true); + } + + // ================== å…³è”/å–å…³è”系人 =================== + + @PostMapping("/create-business-list") + @Operation(summary = "创建è”系人与商机的关è”") + @PreAuthorize("@ss.hasPermission('crm:contact:create-business')") + public CommonResult createContactBusinessList(@Valid @RequestBody CrmContactBusinessReqVO createReqVO) { + contactBusinessLinkService.createContactBusinessList(createReqVO); + return success(true); + } + + @DeleteMapping("/delete-business-list") + @Operation(summary = "删除è”系人与è”系人的关è”") + @PreAuthorize("@ss.hasPermission('crm:contact:delete-business')") + public CommonResult deleteContactBusinessList(@Valid @RequestBody CrmContactBusinessReqVO deleteReqVO) { + contactBusinessLinkService.deleteContactBusinessList(deleteReqVO); + return success(true); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactBusinessReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactBusinessReqVO.java new file mode 100644 index 000000000..9b360f84b --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactBusinessReqVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.crm.controller.admin.contact.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管ç†åŽå° - CRM è”系人商机 Request VO") // 用于关è”,å–消关è”çš„æ“作 +@Data +public class CrmContactBusinessReqVO { + + @Schema(description = "è”系人编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "20878") + @NotNull(message="è”系人ä¸èƒ½ä¸ºç©º") + private Long contactId; + + @Schema(description = "商机编å·æ•°ç»„", requiredMode = Schema.RequiredMode.REQUIRED, example = "7638") + @NotEmpty(message="商机ä¸èƒ½ä¸ºç©º") + private List businessIds; + +} \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactPageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactPageReqVO.java new file mode 100644 index 000000000..75294a1bd --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactPageReqVO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.crm.controller.admin.contact.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum; +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 CrmContactPageReqVO extends PageParam { + + @Schema(description = "姓å", example = "芋艿") + private String name; + + @Schema(description = "客户编å·", example = "10795") + private Long customerId; + + @Schema(description = "手机å·", example = "13898273941") + private String mobile; + + @Schema(description = "电è¯", example = "021-383773") + private String telephone; + + @Schema(description = "电å­é‚®ç®±", example = "111@22.com") + private String email; + + @Schema(description = "QQ", example = "3882872") + private Long qq; + + @Schema(description = "微信", example = "zzZ98373") + private String wechat; + + @Schema(description = "场景类型", example = "1") + @InEnum(CrmSceneTypeEnum.class) + private Integer sceneType; // 场景类型,为 null 时则表示全部 + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactRespVO.java new file mode 100644 index 000000000..d99ea703c --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactRespVO.java @@ -0,0 +1,112 @@ +package cn.iocoder.yudao.module.crm.controller.admin.contact.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.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.ToString; + +import java.time.LocalDateTime; + +@Schema(description = "管ç†åŽå° - CRM è”系人 Response VO") +@Data +@ToString(callSuper = true) +@ExcelIgnoreUnannotated +public class CrmContactRespVO { + + @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "3167") + private Long id; + + @Schema(description = "姓å", example = "芋艿") + @ExcelProperty(value = "姓å", order = 1) + private String name; + + @Schema(description = "客户编å·", example = "10795") + private Long customerId; + + @Schema(description = "性别") + @ExcelProperty(value = "性别", converter = DictConvert.class, order = 3) + @DictFormat(cn.iocoder.yudao.module.system.enums.DictTypeConstants.USER_SEX) + private Integer sex; + + @Schema(description = "èŒä½") + @ExcelProperty(value = "èŒä½", order = 3) + private String post; + + @Schema(description = "是å¦å…³é”®å†³ç­–人") + @ExcelProperty(value = "是å¦å…³é”®å†³ç­–人", converter = DictConvert.class, order = 3) + @DictFormat(DictTypeConstants.BOOLEAN_STRING) + private Boolean master; + + @Schema(description = "直属上级", example = "23457") + private Long parentId; + + @Schema(description = "手机å·", example = "1387171766") + @ExcelProperty(value = "手机å·", order = 4) + private String mobile; + + @Schema(description = "电è¯", example = "021-0029922") + @ExcelProperty(value = "电è¯", order = 4) + private String telephone; + + @Schema(description = "QQ", example = "197272662") + @ExcelProperty(value = "QQ", order = 4) + private Long qq; + + @Schema(description = "微信", example = "zzz3883") + @ExcelProperty(value = "微信", order = 4) + private String wechat; + + @Schema(description = "电å­é‚®ç®±", example = "1111@22.com") + @ExcelProperty(value = "邮箱", order = 4) + private String email; + + @Schema(description = "地区编å·", example = "20158") + private Integer areaId; + + @Schema(description = "地å€") + @ExcelProperty(value = "地å€", order = 5) + private String detailAddress; + + @Schema(description = "备注", example = "你说的对") + @ExcelProperty(value = "备注", order = 6) + private String remark; + + @Schema(description = "负责人用户编å·", example = "14334") + private Long ownerUserId; + + @Schema(description = "最åŽè·Ÿè¿›æ—¶é—´") + @ExcelProperty(value = "最åŽè·Ÿè¿›æ—¶é—´", order = 6) + private LocalDateTime contactLastTime; + + @Schema(description = "下次è”系时间") + @ExcelProperty(value = "下次è”系时间", order = 6) + private LocalDateTime contactNextTime; + + @Schema(description = "创建人", example = "25682") + private String creator; + + @Schema(description = "创建人åå­—", example = "test") + @ExcelProperty(value = "创建人", order = 8) + private String creatorName; + + @ExcelProperty(value = "客户å称", order = 2) + @Schema(description = "客户åå­—", example = "test") + private String customerName; + + @Schema(description = "负责人", example = "test") + @ExcelProperty(value = "负责人", order = 7) + private String ownerUserName; + + @Schema(description = "直属上级å", example = "芋头") + @ExcelProperty(value = "直属上级", order = 4) + private String parentName; + + @Schema(description = "地区å", example = "上海上海市浦东新区") + @ExcelProperty(value = "地区", order = 5) + private String areaName; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactSaveReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactSaveReqVO.java new file mode 100644 index 000000000..299b1fbbb --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactSaveReqVO.java @@ -0,0 +1,103 @@ +package cn.iocoder.yudao.module.crm.controller.admin.contact.vo; + +import cn.iocoder.yudao.framework.common.validation.Mobile; +import cn.iocoder.yudao.framework.common.validation.Telephone; +import cn.iocoder.yudao.module.crm.framework.operatelog.core.*; +import com.mzt.logapi.starter.annotation.DiffLogField; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotNull; +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; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管ç†åŽå° - CRM è”系人创建/æ›´æ–° Request VO") +@Data +public class CrmContactSaveReqVO { + + @Schema(description = "主键", example = "3167") + private Long id; + + @Schema(description = "姓å", example = "芋艿") + @NotNull(message = "姓åä¸èƒ½ä¸ºç©º") + @DiffLogField(name = "姓å") + private String name; + + @Schema(description = "客户编å·", example = "10795") + @DiffLogField(name = "客户", function = CrmCustomerParseFunction.NAME) + private Long customerId; + + @Schema(description = "性别") + @DiffLogField(name = "性别", function = SysSexParseFunction.NAME) + private Integer sex; + + @Schema(description = "èŒä½") + @DiffLogField(name = "èŒä½") + private String post; + + @Schema(description = "是å¦å…³é”®å†³ç­–人") + @DiffLogField(name = "关键决策人", function = SysBooleanParseFunction.NAME) + private Boolean master; + + @Schema(description = "直属上级", example = "23457") + @DiffLogField(name = "直属上级", function = CrmContactParseFunction.NAME) + private Long parentId; + + @Schema(description = "手机å·", example = "1387171766") + @Mobile + @DiffLogField(name = "手机å·") + private String mobile; + + @Schema(description = "电è¯", example = "021-0029922") + @Telephone + @DiffLogField(name = "电è¯") + private String telephone; + + @Schema(description = "QQ", example = "197272662") + @DiffLogField(name = "QQ") + private Long qq; + + @Schema(description = "微信", example = "zzz3883") + @DiffLogField(name = "微信") + private String wechat; + + @Schema(description = "电å­é‚®ç®±", example = "1111@22.com") + @DiffLogField(name = "邮箱") + @Email + private String email; + + @Schema(description = "地区编å·", example = "20158") + @DiffLogField(name = "所在地", function = SysAreaParseFunction.NAME) + private Integer areaId; + + @Schema(description = "地å€") + @DiffLogField(name = "地å€") + private String detailAddress; + + @Schema(description = "备注", example = "你说的对") + @DiffLogField(name = "备注") + private String remark; + + @Schema(description = "负责人用户编å·", example = "14334") + @NotNull(message = "负责人ä¸èƒ½ä¸ºç©º") + @DiffLogField(name = "负责人", function = SysAdminUserParseFunction.NAME) + private Long ownerUserId; + + @Schema(description = "最åŽè·Ÿè¿›æ—¶é—´") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @DiffLogField(name = "最åŽè·Ÿè¿›æ—¶é—´") + private LocalDateTime contactLastTime; + + @Schema(description = "下次è”系时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY) + @DiffLogField(name = "下次è”系时间") + private LocalDateTime contactNextTime; + + @Schema(description = "å…³è”商机 ID", example = "122233") + private Long businessId; // 注æ„:该字段用于在ã€å•†æœºã€‘详情界é¢ã€Œæ–°å»ºè”系人ã€æ—¶ï¼Œè‡ªåŠ¨è¿›è¡Œå…³è” + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactTransferReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactTransferReqVO.java new file mode 100644 index 000000000..c65b205c8 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactTransferReqVO.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.crm.controller.admin.contact.vo; + +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotNull; + +@Schema(description = "管ç†åŽå° - CRM è”系人转移 Request VO") +@Data +public class CrmContactTransferReqVO { + + @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 newOwnerUserId; + + /** + * è€è´Ÿè´£äººåŠ å…¥å›¢é˜ŸåŽçš„æƒé™çº§åˆ«ã€‚如果 null 说明移除 + * + * å…³è” {@link CrmPermissionLevelEnum} + */ + @Schema(description = "è€è´Ÿè´£äººåŠ å…¥å›¢é˜ŸåŽçš„æƒé™çº§åˆ«", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Integer oldOwnerPermissionLevel; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java new file mode 100644 index 000000000..ace5d1817 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java @@ -0,0 +1,187 @@ +package cn.iocoder.yudao.module.crm.controller.admin.contract; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO; +import cn.iocoder.yudao.module.crm.convert.contract.CrmContractConvert; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractProductDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; +import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; +import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; +import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; +import cn.iocoder.yudao.module.crm.service.product.CrmProductService; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; +import static java.util.Collections.singletonList; + +@Tag(name = "管ç†åŽå° - CRM åˆåŒ") +@RestController +@RequestMapping("/crm/contract") +@Validated +public class CrmContractController { + + @Resource + private CrmContractService contractService; + @Resource + private CrmCustomerService customerService; + @Resource + private CrmContactService contactService; + @Resource + private CrmBusinessService businessService; + @Resource + private CrmProductService productService; + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建åˆåŒ") + @PreAuthorize("@ss.hasPermission('crm:contract:create')") + public CommonResult createContract(@Valid @RequestBody CrmContractSaveReqVO createReqVO) { + return success(contractService.createContract(createReqVO, getLoginUserId())); + } + + @PutMapping("/update") + @Operation(summary = "æ›´æ–°åˆåŒ") + @PreAuthorize("@ss.hasPermission('crm:contract:update')") + public CommonResult updateContract(@Valid @RequestBody CrmContractSaveReqVO 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 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 getContract(@RequestParam("id") Long id) { + // 1. 查询åˆåŒ + CrmContractDO contract = contractService.getContract(id); + if (contract == null) { + return success(null); + } + + // 2. 拼接åˆåŒä¿¡æ¯ + List respVOList = buildContractDetailList(singletonList(contract)); + return success(respVOList.get(0)); + } + + @GetMapping("/page") + @Operation(summary = "获得åˆåŒåˆ†é¡µ") + @PreAuthorize("@ss.hasPermission('crm:contract:query')") + public CommonResult> getContractPage(@Valid CrmContractPageReqVO pageVO) { + PageResult pageResult = contractService.getContractPage(pageVO, getLoginUserId()); + return success(BeanUtils.toBean(pageResult, CrmContractRespVO.class).setList(buildContractDetailList(pageResult.getList()))); + } + + @GetMapping("/page-by-customer") + @Operation(summary = "获得åˆåŒåˆ†é¡µï¼ŒåŸºäºŽæŒ‡å®šå®¢æˆ·") + public CommonResult> getContractPageByCustomer(@Valid CrmContractPageReqVO pageVO) { + Assert.notNull(pageVO.getCustomerId(), "客户编å·ä¸èƒ½ä¸ºç©º"); + PageResult pageResult = contractService.getContractPageByCustomerId(pageVO); + return success(BeanUtils.toBean(pageResult, CrmContractRespVO.class).setList(buildContractDetailList(pageResult.getList()))); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出åˆåŒ Excel") + @PreAuthorize("@ss.hasPermission('crm:contract:export')") + @OperateLog(type = EXPORT) + public void exportContractExcel(@Valid CrmContractPageReqVO exportReqVO, + HttpServletResponse response) throws IOException { + PageResult pageResult = contractService.getContractPage(exportReqVO, getLoginUserId()); + // 导出 Excel + ExcelUtils.write(response, "åˆåŒ.xls", "æ•°æ®", CrmContractRespVO.class, + BeanUtils.toBean(pageResult.getList(), CrmContractRespVO.class)); + } + + @PutMapping("/transfer") + @Operation(summary = "åˆåŒè½¬ç§»") + @PreAuthorize("@ss.hasPermission('crm:contract:update')") + public CommonResult transferContract(@Valid @RequestBody CrmContractTransferReqVO reqVO) { + contractService.transferContract(reqVO, getLoginUserId()); + return success(true); + } + + @PutMapping("/submit") + @Operation(summary = "æ交åˆåŒå®¡æ‰¹") + @PreAuthorize("@ss.hasPermission('crm:contract:update')") + public CommonResult submitContract(@RequestParam("id") Long id) { + contractService.submitContract(id, getLoginUserId()); + return success(true); + } + + /** + * 构建详细的åˆåŒç»“æžœ + * + * @param contractList 原始åˆåŒä¿¡æ¯ + * @return 细的åˆåŒç»“æžœ + */ + private List buildContractDetailList(List contractList) { + if (CollUtil.isEmpty(contractList)) { + return Collections.emptyList(); + } + // 1. 获å–客户列表 + List customerList = customerService.getCustomerList( + convertSet(contractList, CrmContractDO::getCustomerId)); + // 2. 获å–创建人ã€è´Ÿè´£äººåˆ—表 + Map userMap = adminUserApi.getUserMap(convertListByFlatMap(contractList, + contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId()))); + // 3. 获å–è”系人 + Map contactMap = convertMap(contactService.getContactListByIds(convertSet(contractList, + CrmContractDO::getContactId)), CrmContactDO::getId); + // 4. 获å–商机 + Map businessMap = convertMap(businessService.getBusinessList(convertSet(contractList, + CrmContractDO::getBusinessId)), CrmBusinessDO::getId); + // 5. 获å–åˆåŒå…³è”çš„å•†å“ + Map contractProductMap = null; + List productList = null; + if (contractList.size() == 1) { + List contractProductList = contractService.getContractProductListByContractId(contractList.get(0).getId()); + contractProductMap = convertMap(contractProductList, CrmContractProductDO::getProductId); + productList = productService.getProductListByIds(convertSet(contractProductList, CrmContractProductDO::getProductId)); + } + return CrmContractConvert.INSTANCE.convertList(contractList, userMap, customerList, contactMap, businessMap, contractProductMap, productList); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractPageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractPageReqVO.java new file mode 100644 index 000000000..c61a64ccf --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractPageReqVO.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.crm.controller.admin.contract.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum; +import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum; +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 CrmContractPageReqVO extends PageParam { + + /** + * 过期类型 - å³å°†è¿‡æœŸ + */ + public static final Integer EXPIRY_TYPE_ABOUT_TO_EXPIRE = 1; + /** + * 过期类型 - 已过期 + */ + public static final Integer EXPIRY_TYPE_EXPIRED = 2; + + @Schema(description = "åˆåŒç¼–å·", example = "XYZ008") + private String no; + + @Schema(description = "åˆåŒå称", example = "王五") + private String name; + + @Schema(description = "客户编å·", example = "18336") + private Long customerId; + + @Schema(description = "商机编å·", example = "10864") + private Long businessId; + + @Schema(description = "场景类型", example = "1") + @InEnum(CrmSceneTypeEnum.class) + private Integer sceneType; // 场景类型,为 null 时则表示全部 + + @Schema(description = "审批状æ€", example = "20") + @InEnum(CrmAuditStatusEnum.class) + private Integer auditStatus; + + @Schema(description = "过期类型", example = "1") + private Integer expiryType; // 过期类型,为 null 时则表示全部 + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractRespVO.java new file mode 100644 index 000000000..da6239414 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractRespVO.java @@ -0,0 +1,165 @@ +package cn.iocoder.yudao.module.crm.controller.admin.contract.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管ç†åŽå° - CRM åˆåŒ Response VO") +@Data +@ExcelIgnoreUnannotated +public class CrmContractRespVO { + + @Schema(description = "åˆåŒç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") + @ExcelProperty("åˆåŒç¼–å·") + private Long id; + + @Schema(description = "åˆåŒå称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") + @ExcelProperty("åˆåŒå称") + private String name; + + @Schema(description = "客户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "18336") + @ExcelProperty("客户编å·") + private Long customerId; + @Schema(description = "客户å称", requiredMode = Schema.RequiredMode.REQUIRED, example = "18336") + @ExcelProperty("客户å称") + private String customerName; + + @Schema(description = "商机编å·", example = "10864") + @ExcelProperty("商机编å·") + private Long businessId; + @Schema(description = "商机å称", example = "10864") + @ExcelProperty("商机å称") + private String businessName; + + @Schema(description = "工作æµç¼–å·", example = "1043") + @ExcelProperty("工作æµç¼–å·") + private Long processInstanceId; + + @Schema(description = "下å•æ—¥æœŸ", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("下å•æ—¥æœŸ") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime orderDate; + + @Schema(description = "负责人的用户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "17144") + @ExcelProperty("负责人的用户编å·") + private Long ownerUserId; + + // TODO @芋艿:未æ¥åº”该支æŒè‡ªåŠ¨ç”Ÿæˆï¼› + @Schema(description = "åˆåŒç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "20230101") + @ExcelProperty("åˆåŒç¼–å·") + private String no; + + @Schema(description = "开始时间") + @ExcelProperty("开始时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime startTime; + + @Schema(description = "结æŸæ—¶é—´") + @ExcelProperty("结æŸæ—¶é—´") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime endTime; + + @Schema(description = "åˆåŒé‡‘é¢", example = "5617") + @ExcelProperty("åˆåŒé‡‘é¢") + private Integer price; + + @Schema(description = "æ•´å•æŠ˜æ‰£") + @ExcelProperty("æ•´å•æŠ˜æ‰£") + private Integer discountPercent; + + @Schema(description = "产å“总金é¢", example = "19510") + @ExcelProperty("产å“总金é¢") + private Integer productPrice; + + @Schema(description = "è”系人编å·", example = "18546") + @ExcelProperty("è”系人编å·") + private Long contactId; + @Schema(description = "è”系人编å·", example = "18546") + @ExcelProperty("è”系人编å·") + private String contactName; + + @Schema(description = "å…¬å¸ç­¾çº¦äºº", example = "14036") + @ExcelProperty("å…¬å¸ç­¾çº¦äºº") + private Long signUserId; + @Schema(description = "å…¬å¸ç­¾çº¦äºº", example = "14036") + @ExcelProperty("å…¬å¸ç­¾çº¦äºº") + private String signUserName; + + @Schema(description = "最åŽè·Ÿè¿›æ—¶é—´") + @ExcelProperty("最åŽè·Ÿè¿›æ—¶é—´") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime contactLastTime; + + @Schema(description = "备注", example = "你猜") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime createTime; + + @Schema(description = "创建人", example = "25682") + @ExcelProperty("创建人") + private String creator; + + @Schema(description = "创建人åå­—", example = "test") + @ExcelProperty("创建人åå­—") + private String creatorName; + + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("更新时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime updateTime; + + @Schema(description = "负责人", example = "test") + @ExcelProperty("负责人") + private String ownerUserName; + + @Schema(description = "审批状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + @ExcelProperty("审批状æ€") + private Integer auditStatus; + + @Schema(description = "产å“列表") + private List productItems; + + // TODO @puhui999:å¯ä»¥ç›´æŽ¥å« Item + @Schema(description = "产å“列表") + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class CrmContractProductItemRespVO { + + @Schema(description = "产å“ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "20529") + private Long id; + + @Schema(description = "产å“å称", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是产å“") + private String name; + + @Schema(description = "产å“ç¼–ç ", requiredMode = Schema.RequiredMode.REQUIRED, example = "N881") + private String no; + + @Schema(description = "å•ä½", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer unit; + + @Schema(description = "价格,å•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer price; + + @Schema(description = "产å“æ•°é‡", requiredMode = Schema.RequiredMode.REQUIRED, example = "20") + private Integer count; + + @Schema(description = "产å“折扣", example = "99") + private Integer discountPercent; + + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractSaveReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractSaveReqVO.java new file mode 100644 index 000000000..20b20580e --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractSaveReqVO.java @@ -0,0 +1,115 @@ +package cn.iocoder.yudao.module.crm.controller.admin.contract.vo; + +import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmBusinessParseFunction; +import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmContactParseFunction; +import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerParseFunction; +import cn.iocoder.yudao.module.crm.framework.operatelog.core.SysAdminUserParseFunction; +import com.mzt.logapi.starter.annotation.DiffLogField; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管ç†åŽå° - CRM åˆåŒåˆ›å»º/æ›´æ–° Request VO") +@Data +public class CrmContractSaveReqVO { + + @Schema(description = "åˆåŒç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") + private Long id; + + @Schema(description = "åˆåŒå称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") + @DiffLogField(name = "åˆåŒå称") + @NotNull(message = "åˆåŒå称ä¸èƒ½ä¸ºç©º") + private String name; + + @Schema(description = "客户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "18336") + @DiffLogField(name = "客户", function = CrmCustomerParseFunction.NAME) + @NotNull(message = "客户编å·ä¸èƒ½ä¸ºç©º") + private Long customerId; + + @Schema(description = "商机编å·", example = "10864") + @DiffLogField(name = "商机", function = CrmBusinessParseFunction.NAME) + private Long businessId; + + @Schema(description = "下å•æ—¥æœŸ", requiredMode = Schema.RequiredMode.REQUIRED) + @DiffLogField(name = "下å•æ—¥æœŸ") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @NotNull(message = "下å•æ—¥æœŸä¸èƒ½ä¸ºç©º") + private LocalDateTime orderDate; + + @Schema(description = "负责人的用户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "17144") + @DiffLogField(name = "负责人", function = SysAdminUserParseFunction.NAME) + @NotNull(message = "负责人ä¸èƒ½ä¸ºç©º") + private Long ownerUserId; + + // TODO @芋艿:未æ¥åº”该支æŒè‡ªåŠ¨ç”Ÿæˆï¼› + @Schema(description = "åˆåŒç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "20230101") + @DiffLogField(name = "åˆåŒç¼–å·") + @NotNull(message = "åˆåŒç¼–å·ä¸èƒ½ä¸ºç©º") + private String no; + + @Schema(description = "开始时间") + @DiffLogField(name = "开始时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime startTime; + + @Schema(description = "结æŸæ—¶é—´") + @DiffLogField(name = "结æŸæ—¶é—´") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime endTime; + + @Schema(description = "åˆåŒé‡‘é¢", example = "5617") + @DiffLogField(name = "åˆåŒé‡‘é¢") + private Integer price; + + @Schema(description = "æ•´å•æŠ˜æ‰£") + @DiffLogField(name = "æ•´å•æŠ˜æ‰£") + private Integer discountPercent; + + @Schema(description = "产å“总金é¢", example = "19510") + @DiffLogField(name = "产å“总金é¢") + private Integer productPrice; + + @Schema(description = "è”系人编å·", example = "18546") + @DiffLogField(name = "è”系人", function = CrmContactParseFunction.NAME) + private Long contactId; + + @Schema(description = "å…¬å¸ç­¾çº¦äºº", example = "14036") + @DiffLogField(name = "å…¬å¸ç­¾çº¦äºº", function = SysAdminUserParseFunction.NAME) + private Long signUserId; + + @Schema(description = "备注", example = "你猜") + @DiffLogField(name = "备注") + private String remark; + + + @Schema(description = "产å“列表") + private List productItems; + + @Schema(description = "产å“列表") + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class CrmContractProductItem { + + @Schema(description = "产å“ç¼–å·", example = "20529") + @NotNull(message = "产å“ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long id; + + @Schema(description = "产å“æ•°é‡", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911") + @NotNull(message = "产å“æ•°é‡ä¸èƒ½ä¸ºç©º") + private Integer count; + + @Schema(description = "产å“折扣") + private Integer discountPercent; + + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractTransferReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractTransferReqVO.java new file mode 100644 index 000000000..88227c560 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractTransferReqVO.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.crm.controller.admin.contract.vo; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotNull; + +@Schema(description = "管ç†åŽå° - CRM åˆåŒè½¬ç§» Request VO") +@Data +public class CrmContractTransferReqVO { + + @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 newOwnerUserId; + + @Schema(description = "è€è´Ÿè´£äººåŠ å…¥å›¢é˜ŸåŽçš„æƒé™çº§åˆ«", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @InEnum(value = CrmPermissionLevelEnum.class) + private Integer oldOwnerPermissionLevel; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.http b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.http new file mode 100644 index 000000000..9a6cb93a8 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.http @@ -0,0 +1,16 @@ +### 请求 /transfer +PUT {{baseUrl}}/crm/customer/transfer +Content-Type: application/-id: {{adminTenentId}}json +Authorization: Bearer {{token}} +tenant + +{ + "id": 10, + "newOwnerUserId": 127 +} + +### 自定义日志记录结果 +### æ“作日志 ===> OperateLogV2CreateReqBO(traceId=, userId=1, userType=2, module=CRM-客户, name=客户转移, bizId=10, content=把客户ã€å¼ ä¸‰ã€‘的负责人从ã€èŠ‹é“æºç (15612345678)】å˜æ›´ä¸ºäº†ã€tttt】, requestMethod=PUT, requestUrl=/admin-api/crm/customer/transfer, userIp=127.0.0.1, userAgent=Apache-HttpClient/4.5.14 (Java/17.0.9)) + +### diff 日志 +### | æ“作日志 ===> OperateLogV2CreateReqBO(traceId=, userId=1, userType=2, module=CRM-客户, name=更新客户, bizId=11, content=更新了客户ã€æ‰€å±žè¡Œä¸šã€‘从ã€H ä½å®¿å’Œé¤é¥®ä¸šã€‘修改为ã€D 电力ã€çƒ­åŠ›ã€ç‡ƒæ°”åŠæ°´ç”Ÿäº§å’Œä¾›åº”业】;ã€å®¢æˆ·ç­‰çº§ã€‘从ã€C (éžä¼˜å…ˆå®¢æˆ·ï¼‰ã€‘修改为ã€A (é‡ç‚¹å®¢æˆ·ï¼‰ã€‘ï¼›ã€å®¢æˆ·æ¥æºã€‘从ã€çº¿ä¸Šå’¨è¯¢ã€‘修改为ã€é¢„约上门】, requestMethod=PUT, requestUrl=/admin-api/crm/customer/update, userIp=0:0:0:0:0:0:0:1, userAgent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36) diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java new file mode 100644 index 000000000..da23f2b47 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java @@ -0,0 +1,272 @@ +package cn.iocoder.yudao.module.crm.controller.admin.customer; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.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.dal.dataobject.customer.CrmCustomerPoolConfigDO; +import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerPoolConfigService; +import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; +import cn.iocoder.yudao.module.system.api.dept.DeptApi; +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.mapstruct.ap.internal.util.Collections; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CUSTOMER_POOL_CONFIG_NOT_EXISTS_OR_DISABLED; + +@Tag(name = "管ç†åŽå° - CRM 客户") +@RestController +@RequestMapping("/crm/customer") +@Validated +public class CrmCustomerController { + + @Resource + private CrmCustomerService customerService; + @Resource + private CrmCustomerPoolConfigService customerPoolConfigService; + @Resource + private DeptApi deptApi; + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建客户") + @PreAuthorize("@ss.hasPermission('crm:customer:create')") + public CommonResult createCustomer(@Valid @RequestBody CrmCustomerSaveReqVO createReqVO) { + return success(customerService.createCustomer(createReqVO, getLoginUserId())); + } + + @PutMapping("/update") + @Operation(summary = "更新客户") + @PreAuthorize("@ss.hasPermission('crm:customer:update')") + public CommonResult updateCustomer(@Valid @RequestBody CrmCustomerSaveReqVO 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 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 getCustomer(@RequestParam("id") Long id) { + // 1. 获å–客户 + CrmCustomerDO customer = customerService.getCustomer(id); + if (customer == null) { + return success(null); + } + // 2. æ‹¼æŽ¥æ•°æ® + Map userMap = adminUserApi.getUserMap( + Collections.asSet(Long.valueOf(customer.getCreator()), customer.getOwnerUserId())); + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); + return success(CrmCustomerConvert.INSTANCE.convert(customer, userMap, deptMap)); + } + + @GetMapping("/page") + @Operation(summary = "获得客户分页") + @PreAuthorize("@ss.hasPermission('crm:customer:query')") + public CommonResult> getCustomerPage(@Valid CrmCustomerPageReqVO pageVO) { + // 1. 查询客户分页 + PageResult pageResult = customerService.getCustomerPage(pageVO, getLoginUserId()); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty(pageResult.getTotal())); + } + + // 2. æ‹¼æŽ¥æ•°æ® + Map poolDayMap = Boolean.TRUE.equals(pageVO.getPool()) ? null : + getPoolDayMap(pageResult.getList()); // 客户界é¢ï¼Œéœ€è¦æŸ¥çœ‹è·ç¦»è¿›å…¥å…¬æµ·çš„时间 + Map userMap = adminUserApi.getUserMap( + convertSetByFlatMap(pageResult.getList(), user -> Stream.of(Long.parseLong(user.getCreator()), user.getOwnerUserId()))); + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); + return success(CrmCustomerConvert.INSTANCE.convertPage(pageResult, userMap, deptMap, poolDayMap)); + } + + @GetMapping("/put-in-pool-remind-page") + @Operation(summary = "获得待进入公海客户分页") + @PreAuthorize("@ss.hasPermission('crm:customer:query')") + public CommonResult> getPutInPoolRemindCustomerPage(@Valid CrmCustomerPageReqVO pageVO) { + // 获å–公海é…ç½® TODO @dbh52:åˆå¹¶åˆ° getPutInPoolRemindCustomerPage 会更åˆé€‚哈; + CrmCustomerPoolConfigDO poolConfigDO = customerPoolConfigService.getCustomerPoolConfig(); + if (ObjUtil.isNull(poolConfigDO) + || Boolean.FALSE.equals(poolConfigDO.getEnabled()) + || Boolean.FALSE.equals(poolConfigDO.getNotifyEnabled()) + ) { // TODO @dbh52:这个括å·ï¼Œä¸€èˆ¬ä¸æ¢è¡Œï¼Œåœ¨ java 这里; + throw exception(CUSTOMER_POOL_CONFIG_NOT_EXISTS_OR_DISABLED); + } + + // 1. 查询客户分页 + PageResult pageResult = customerService.getPutInPoolRemindCustomerPage(pageVO, poolConfigDO, getLoginUserId()); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty(pageResult.getTotal())); + } + + // 2. æ‹¼æŽ¥æ•°æ® + // TODO @芋艿:åˆå¹¶ getCustomerPage å’Œ getPutInPoolRemindCustomerPage çš„åŽç½®å¤„ç†ï¼› + Map poolDayMap = getPoolDayMap(pageResult.getList()); // 客户界é¢ï¼Œéœ€è¦æŸ¥çœ‹è·ç¦»è¿›å…¥å…¬æµ·çš„时间 + Map userMap = adminUserApi.getUserMap( + convertSetByFlatMap(pageResult.getList(), user -> Stream.of(Long.parseLong(user.getCreator()), user.getOwnerUserId()))); + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); + return success(CrmCustomerConvert.INSTANCE.convertPage(pageResult, userMap, deptMap, poolDayMap)); + } + + /** + * 获å–è·ç¦»è¿›å…¥å…¬æµ·çš„时间 + * + * @param customerList 客户列表 + * @return Map + */ + private Map getPoolDayMap(List customerList) { + CrmCustomerPoolConfigDO poolConfig = customerPoolConfigService.getCustomerPoolConfig(); + if (poolConfig == null || !poolConfig.getEnabled()) { + return MapUtil.empty(); + } + return convertMap(customerList, CrmCustomerDO::getId, customer -> { + // 1.1 未æˆäº¤æ”¾å…¥å…¬æµ·å¤©æ•° + long dealExpireDay = 0; + if (!customer.getDealStatus()) { + dealExpireDay = poolConfig.getDealExpireDays() - LocalDateTimeUtils.between(customer.getCreateTime()); + } + // 1.2 未跟进放入公海天数 + LocalDateTime lastTime = ObjUtil.defaultIfNull(customer.getContactLastTime(), customer.getCreateTime()); + long contactExpireDay = poolConfig.getContactExpireDays() - LocalDateTimeUtils.between(lastTime); + if (contactExpireDay < 0) { + contactExpireDay = 0; + } + // 2. 返回最å°çš„天数 + return Math.min(dealExpireDay, contactExpireDay); + }); + } + + @GetMapping(value = "/list-all-simple") + @Operation(summary = "获å–客户精简信æ¯åˆ—表", description = "åªåŒ…å«æœ‰è¯»æƒé™çš„客户,主è¦ç”¨äºŽå‰ç«¯çš„下拉选项") + public CommonResult> getSimpleDeptList() { + CrmCustomerPageReqVO reqVO = new CrmCustomerPageReqVO(); + reqVO.setPageSize(PAGE_SIZE_NONE); // ä¸åˆ†é¡µ + List list = customerService.getCustomerPage(reqVO, getLoginUserId()).getList(); + return success(convertList(list, customer -> // åªè¿”回 idã€name 精简字段 + new CrmCustomerRespVO().setId(customer.getId()).setName(customer.getName()))); + } + + // TODO @puhui999:公海的导出,å‰ç«¯å¯ä»¥æŽ¥ä¸‹ + @GetMapping("/export-excel") + @Operation(summary = "导出客户 Excel") + @PreAuthorize("@ss.hasPermission('crm:customer:export')") + @OperateLog(type = EXPORT) + public void exportCustomerExcel(@Valid CrmCustomerPageReqVO pageVO, + HttpServletResponse response) throws IOException { + pageVO.setPageSize(PAGE_SIZE_NONE); // ä¸åˆ†é¡µ + List list = customerService.getCustomerPage(pageVO, getLoginUserId()).getList(); + // 导出 Excel + ExcelUtils.write(response, "客户.xls", "æ•°æ®", CrmCustomerRespVO.class, + BeanUtils.toBean(list, CrmCustomerRespVO.class)); + } + + @GetMapping("/get-import-template") + @Operation(summary = "获得导入客户模æ¿") + public void importTemplate(HttpServletResponse response) throws IOException { + // 手动创建导出 demo + List list = Arrays.asList( + CrmCustomerImportExcelVO.builder().name("芋é“").industryId(1).level(1).source(1).mobile("15601691300").telephone("") + .website("https://doc.iocoder.cn/").qq("").wechat("").email("yunai@iocoder.cn").description("").remark("") + .areaId(null).detailAddress("").build(), + CrmCustomerImportExcelVO.builder().name("æºç ").industryId(1).level(1).source(1).mobile("15601691300").telephone("") + .website("https://doc.iocoder.cn/").qq("").wechat("").email("yunai@iocoder.cn").description("").remark("") + .areaId(null).detailAddress("").build() + ); + // 输出 + ExcelUtils.write(response, "客户导入模æ¿.xls", "客户列表", CrmCustomerImportExcelVO.class, list); + } + + @PostMapping("/import") + @Operation(summary = "导入客户") + @PreAuthorize("@ss.hasPermission('system:customer:import')") + public CommonResult importExcel(@Valid @RequestBody CrmCustomerImportReqVO importReqVO) + throws Exception { + List list = ExcelUtils.read(importReqVO.getFile(), CrmCustomerImportExcelVO.class); + return success(customerService.importCustomerList(list, importReqVO)); + } + + @PutMapping("/transfer") + @Operation(summary = "转移客户") + @PreAuthorize("@ss.hasPermission('crm:customer:update')") + public CommonResult transferCustomer(@Valid @RequestBody CrmCustomerTransferReqVO reqVO) { + customerService.transferCustomer(reqVO, getLoginUserId()); + return success(true); + } + + @PutMapping("/lock") + @Operation(summary = "é”定/解é”客户") + @PreAuthorize("@ss.hasPermission('crm:customer:update')") + public CommonResult lockCustomer(@Valid @RequestBody CrmCustomerLockReqVO lockReqVO) { + customerService.lockCustomer(lockReqVO, getLoginUserId()); + return success(true); + } + + // ==================== 公海相关æ“作 ==================== + + @PutMapping("/put-pool") + @Operation(summary = "æ•°æ®æ”¾å…¥å…¬æµ·") + @Parameter(name = "id", description = "客户编å·", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('crm:customer:update')") + public CommonResult putCustomerPool(@RequestParam("id") Long id) { + customerService.putCustomerPool(id); + return success(true); + } + + @PutMapping("/receive") + @Operation(summary = "领å–公海客户") + @Parameter(name = "ids", description = "ç¼–å·æ•°ç»„", required = true, example = "1,2,3") + @PreAuthorize("@ss.hasPermission('crm:customer:receive')") + public CommonResult receiveCustomer(@RequestParam(value = "ids") List ids) { + customerService.receiveCustomer(ids, getLoginUserId(), Boolean.TRUE); + return success(true); + } + + @PutMapping("/distribute") + @Operation(summary = "分é…公海给对应负责人") + @PreAuthorize("@ss.hasPermission('crm:customer:distribute')") + public CommonResult distributeCustomer(@Valid @RequestBody CrmCustomerDistributeReqVO distributeReqVO) { + customerService.receiveCustomer(distributeReqVO.getIds(), distributeReqVO.getOwnerUserId(), Boolean.FALSE); + return success(true); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerLimitConfigController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerLimitConfigController.java new file mode 100644 index 000000000..95f4ccd8f --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerLimitConfigController.java @@ -0,0 +1,97 @@ +package cn.iocoder.yudao.module.crm.controller.admin.customer; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigSaveReqVO; +import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerLimitConfigConvert; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO; +import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerLimitConfigService; +import cn.iocoder.yudao.module.system.api.dept.DeptApi; +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Collection; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; + +@Tag(name = "管ç†åŽå° - CRM 客户é™åˆ¶é…ç½®") +@RestController +@RequestMapping("/crm/customer-limit-config") +@Validated +public class CrmCustomerLimitConfigController { + + @Resource + private CrmCustomerLimitConfigService customerLimitConfigService; + + @Resource + private DeptApi deptApi; + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建客户é™åˆ¶é…ç½®") + @PreAuthorize("@ss.hasPermission('crm:customer-limit-config:create')") + public CommonResult createCustomerLimitConfig(@Valid @RequestBody CrmCustomerLimitConfigSaveReqVO createReqVO) { + return success(customerLimitConfigService.createCustomerLimitConfig(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新客户é™åˆ¶é…ç½®") + @PreAuthorize("@ss.hasPermission('crm:customer-limit-config:update')") + public CommonResult updateCustomerLimitConfig(@Valid @RequestBody CrmCustomerLimitConfigSaveReqVO updateReqVO) { + customerLimitConfigService.updateCustomerLimitConfig(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除客户é™åˆ¶é…ç½®") + @Parameter(name = "id", description = "ç¼–å·", required = true) + @PreAuthorize("@ss.hasPermission('crm:customer-limit-config:delete')") + public CommonResult deleteCustomerLimitConfig(@RequestParam("id") Long id) { + customerLimitConfigService.deleteCustomerLimitConfig(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得客户é™åˆ¶é…ç½®") + @Parameter(name = "id", description = "ç¼–å·", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('crm:customer-limit-config:query')") + public CommonResult getCustomerLimitConfig(@RequestParam("id") Long id) { + CrmCustomerLimitConfigDO customerLimitConfig = customerLimitConfigService.getCustomerLimitConfig(id); + // æ‹¼æŽ¥æ•°æ® + Map userMap = adminUserApi.getUserMap(customerLimitConfig.getUserIds()); + Map deptMap = deptApi.getDeptMap(customerLimitConfig.getDeptIds()); + return success(CrmCustomerLimitConfigConvert.INSTANCE.convert(customerLimitConfig, userMap, deptMap)); + } + + @GetMapping("/page") + @Operation(summary = "获得客户é™åˆ¶é…置分页") + @PreAuthorize("@ss.hasPermission('crm:customer-limit-config:query')") + public CommonResult> getCustomerLimitConfigPage(@Valid CrmCustomerLimitConfigPageReqVO pageVO) { + PageResult pageResult = customerLimitConfigService.getCustomerLimitConfigPage(pageVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty(pageResult.getTotal())); + } + // æ‹¼æŽ¥æ•°æ® + Map userMap = adminUserApi.getUserMap( + convertSetByFlatMap(pageResult.getList(), CrmCustomerLimitConfigDO::getUserIds, Collection::stream)); + Map deptMap = deptApi.getDeptMap( + convertSetByFlatMap(pageResult.getList(), CrmCustomerLimitConfigDO::getDeptIds, Collection::stream)); + return success(CrmCustomerLimitConfigConvert.INSTANCE.convertPage(pageResult, userMap, deptMap)); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerPoolConfigController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerPoolConfigController.java new file mode 100644 index 000000000..ca3b1ac62 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerPoolConfigController.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.crm.controller.admin.customer; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.poolconfig.CrmCustomerPoolConfigRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.poolconfig.CrmCustomerPoolConfigSaveReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO; +import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerPoolConfigService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管ç†åŽå° - CRM 客户公海é…ç½®") +@RestController +@RequestMapping("/crm/customer-pool-config") +@Validated +public class CrmCustomerPoolConfigController { + + @Resource + private CrmCustomerPoolConfigService customerPoolConfigService; + + @GetMapping("/get") + @Operation(summary = "获å–客户公海规则设置") + @PreAuthorize("@ss.hasPermission('crm:customer-pool-config:query')") + public CommonResult getCustomerPoolConfig() { + CrmCustomerPoolConfigDO poolConfig = customerPoolConfigService.getCustomerPoolConfig(); + return success(BeanUtils.toBean(poolConfig, CrmCustomerPoolConfigRespVO.class)); + } + + @PutMapping("/save") + @Operation(summary = "更新客户公海规则设置") + @PreAuthorize("@ss.hasPermission('crm:customer-pool-config:update')") + public CommonResult saveCustomerPoolConfig(@Valid @RequestBody CrmCustomerPoolConfigSaveReqVO updateReqVO) { + customerPoolConfigService.saveCustomerPoolConfig(updateReqVO); + return success(true); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerDistributeReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerDistributeReqVO.java new file mode 100644 index 000000000..24113ed12 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerDistributeReqVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管ç†åŽå° - CRM 客户分é…公海给对应负责人 Request VO") +@Data +public class CrmCustomerDistributeReqVO { + + @Schema(description = "客户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1024]") + @NotEmpty(message = "客户编å·ä¸èƒ½ä¸ºç©º") + private List ids; + + @Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "负责人ä¸èƒ½ä¸ºç©º") + private Long ownerUserId; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerImportExcelVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerImportExcelVO.java new file mode 100644 index 000000000..4f57564dd --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerImportExcelVO.java @@ -0,0 +1,71 @@ +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 com.alibaba.excel.annotation.ExcelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.*; + +/** + * 客户 Excel 导入 VO + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Accessors(chain = false) // 设置 chain = false,é¿å…用户导入有问题 +public class CrmCustomerImportExcelVO { + + @ExcelProperty("客户å称") + private String name; + + // TODO @puhui999:industryIdã€levelã€source 字段,å¯ä»¥ç ”究下怎么æžä¸‹æ‹‰æ¡† + @ExcelProperty(value = "所属行业", converter = DictConvert.class) + @DictFormat(CRM_CUSTOMER_INDUSTRY) + private Integer industryId; + + @ExcelProperty(value = "客户等级", converter = DictConvert.class) + @DictFormat(CRM_CUSTOMER_LEVEL) + private Integer level; + + @ExcelProperty(value = "客户æ¥æº", converter = DictConvert.class) + @DictFormat(CRM_CUSTOMER_SOURCE) + private Integer source; + + @ExcelProperty("手机") + private String mobile; + + @ExcelProperty("电è¯") + private String telephone; + + @ExcelProperty("网å€") + private String website; + + @ExcelProperty("QQ") + private String qq; + + @ExcelProperty("微信") + private String wechat; + + @ExcelProperty("邮箱") + private String email; + + @ExcelProperty("客户æè¿°") + private String description; + + @ExcelProperty("备注") + private String remark; + + // TODO @puhui999:需è¦é€‰æ‹©çœå¸‚区,需è¦ç ”究下,怎么æžåˆç†ç‚¹ï¼› + @ExcelProperty("地区编å·") + private Integer areaId; + + @ExcelProperty("详细地å€") + private String detailAddress; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerImportReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerImportReqVO.java new file mode 100644 index 000000000..a396dc50b --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerImportReqVO.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Builder; +import lombok.Data; +import org.springframework.web.multipart.MultipartFile; + +@Schema(description = "管ç†åŽå° - 客户导入 Request VO") +@Data +@Builder +public class CrmCustomerImportReqVO { + + @Schema(description = "Excel 文件", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "Excel 文件ä¸èƒ½ä¸ºç©º") + private MultipartFile file; + + @Schema(description = "是å¦æ”¯æŒæ›´æ–°", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是å¦æ”¯æŒæ›´æ–°ä¸èƒ½ä¸ºç©º") + private Boolean updateSupport; + + @Schema(description = "负责人", example = "1") + private Long ownerUserId; // 为 null 则客户进入公海 + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerImportRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerImportRespVO.java new file mode 100644 index 000000000..de35b7b92 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerImportRespVO.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Schema(description = "管ç†åŽå° - 客户导入 Response VO") +@Data +@Builder +public class CrmCustomerImportRespVO { + + @Schema(description = "创建æˆåŠŸçš„客户å数组", requiredMode = Schema.RequiredMode.REQUIRED) + private List createCustomerNames; + + @Schema(description = "æ›´æ–°æˆåŠŸçš„客户å数组", requiredMode = Schema.RequiredMode.REQUIRED) + private List updateCustomerNames; + + @Schema(description = "导入失败的客户集åˆï¼Œkey 为客户å,value 为失败原因", requiredMode = Schema.RequiredMode.REQUIRED) + private Map failureCustomerNames; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerLockReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerLockReqVO.java new file mode 100644 index 000000000..1cf9ff382 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerLockReqVO.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; + +@Schema(description = "管ç†åŽå° - CRM 客户é”定/è§£é” Request VO") +@Data +public class CrmCustomerLockReqVO { + + @Schema(description = "客户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") + private Long id; + + @Schema(description = "客户é”定状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + private Boolean lockStatus; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerPageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerPageReqVO.java new file mode 100644 index 000000000..bded50473 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerPageReqVO.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum; +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; + + @Schema(description = "所属行业", example = "1") + private Integer industryId; + + @Schema(description = "客户等级", example = "1") + private Integer level; + + @Schema(description = "客户æ¥æº", example = "1") + private Integer source; + + @Schema(description = "场景类型", example = "1") + @InEnum(CrmSceneTypeEnum.class) + private Integer sceneType; // 场景类型,为 null 时则表示全部 + + @Schema(description = "是å¦ä¸ºå…¬æµ·æ•°æ®", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + private Boolean pool; // null 则表示为ä¸æ˜¯å…¬æµ·æ•°æ® + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerRespVO.java new file mode 100644 index 000000000..69c75856f --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerRespVO.java @@ -0,0 +1,138 @@ +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.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +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 客户 Response VO") +@Data +@ExcelIgnoreUnannotated +public class CrmCustomerRespVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") + @ExcelProperty("ç¼–å·") + private Long id; + + @Schema(description = "客户å称", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") + @ExcelProperty("客户å称") + private String name; + + @Schema(description = "跟进状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") + @ExcelProperty(value = "跟进状æ€", converter = DictConvert.class) + @DictFormat(DictTypeConstants.BOOLEAN_STRING) + private Boolean followUpStatus; + + @Schema(description = "é”定状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") + @ExcelProperty(value = "é”定状æ€", converter = DictConvert.class) + @DictFormat(DictTypeConstants.BOOLEAN_STRING) + private Boolean lockStatus; + + @Schema(description = "æˆäº¤çŠ¶æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") + @ExcelProperty(value = "æˆäº¤çŠ¶æ€", converter = DictConvert.class) + @DictFormat(DictTypeConstants.BOOLEAN_STRING) + private Boolean dealStatus; + + @Schema(description = "所属行业", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") + @ExcelProperty(value = "所属行业", converter = DictConvert.class) + @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY) + private Integer industryId; + + @Schema(description = "客户等级", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") + @ExcelProperty(value = "客户等级", converter = DictConvert.class) + @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_LEVEL) + private Integer level; + + @Schema(description = "客户æ¥æº", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") + @ExcelProperty(value = "客户æ¥æº", converter = DictConvert.class) + @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_SOURCE) + private Integer source; + + @Schema(description = "负责人的用户编å·", example = "25682") + @ExcelProperty("手机") + private String mobile; + + @Schema(description = "负责人的用户编å·", example = "25682") + @ExcelProperty("电è¯") + private String telephone; + + @Schema(description = "负责人的用户编å·", example = "25682") + @ExcelProperty("网å€") + private String website; + + @Schema(description = "负责人的用户编å·", example = "25682") + @ExcelProperty("QQ") + private String qq; + + @Schema(description = "负责人的用户编å·", example = "25682") + @ExcelProperty("wechat") + private String wechat; + + @Schema(description = "负责人的用户编å·", example = "25682") + @ExcelProperty("email") + private String email; + + @Schema(description = "负责人的用户编å·", example = "25682") + @ExcelProperty("客户æè¿°") + private String description; + + @Schema(description = "负责人的用户编å·", example = "25682") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "负责人的用户编å·", example = "25682") + @ExcelProperty("负责人的用户编å·") + private Long ownerUserId; + @Schema(description = "负责人åå­—", example = "25682") + @ExcelProperty("负责人åå­—") + private String ownerUserName; + @Schema(description = "负责人部门") + @ExcelProperty("负责人部门") + private String ownerUserDeptName; + + @Schema(description = "地区编å·", example = "1024") + @ExcelProperty("地区编å·") + private Integer areaId; + @Schema(description = "地区å称", example = "北京市") + @ExcelProperty("地区å称") + private String areaName; + @Schema(description = "详细地å€", example = "北京市æˆåŽå¤§é“") + @ExcelProperty("详细地å€") + private String detailAddress; + + @Schema(description = "最åŽè·Ÿè¿›æ—¶é—´") + @ExcelProperty("最åŽè·Ÿè¿›æ—¶é—´") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime contactLastTime; + + @Schema(description = "下次è”系时间") + @ExcelProperty("下次è”系时间") + private LocalDateTime contactNextTime; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("更新时间") + private LocalDateTime updateTime; + + @Schema(description = "创建人", example = "1024") + @ExcelProperty("创建人") + private String creator; + @Schema(description = "创建人åå­—", example = "芋é“æºç ") + @ExcelProperty("创建人åå­—") + private String creatorName; + + @Schema(description = "è·ç¦»åŠ å…¥å…¬æµ·æ—¶é—´", example = "1") + private Long poolDay; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerSaveReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerSaveReqVO.java new file mode 100644 index 000000000..d6d73b142 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerSaveReqVO.java @@ -0,0 +1,106 @@ +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.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLevelEnum; +import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerIndustryParseFunction; +import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerLevelParseFunction; +import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerSourceParseFunction; +import cn.iocoder.yudao.module.crm.framework.operatelog.core.SysAreaParseFunction; +import com.mzt.logapi.starter.annotation.DiffLogField; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Size; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; +import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY; + +@Schema(description = "管ç†åŽå° - CRM 客户新增/修改 Request VO") +@Data +public class CrmCustomerSaveReqVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") + private Long id; + + @Schema(description = "客户å称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") + @DiffLogField(name = "客户å称") + @NotEmpty(message = "客户å称ä¸èƒ½ä¸ºç©º") + private String name; + + @Schema(description = "所属行业", example = "1") + @DiffLogField(name = "所属行业", function = CrmCustomerIndustryParseFunction.NAME) + @DictFormat(CRM_CUSTOMER_INDUSTRY) + private Integer industryId; + + @Schema(description = "客户等级", example = "2") + @DiffLogField(name = "客户等级", function = CrmCustomerLevelParseFunction.NAME) + @InEnum(CrmCustomerLevelEnum.class) + private Integer level; + + @Schema(description = "客户æ¥æº", example = "3") + @DiffLogField(name = "客户æ¥æº", function = CrmCustomerSourceParseFunction.NAME) + private Integer source; + + @Schema(description = "手机", example = "18000000000") + @DiffLogField(name = "手机") + @Mobile + private String mobile; + + @Schema(description = "电è¯", example = "18000000000") + @DiffLogField(name = "电è¯") + @Telephone + private String telephone; + + @Schema(description = "网å€", example = "https://www.baidu.com") + @DiffLogField(name = "网å€") + private String website; + + @Schema(description = "QQ", example = "123456789") + @DiffLogField(name = "QQ") + @Size(max = 20, message = "QQ长度ä¸èƒ½è¶…过 20 个字符") + private String qq; + + @Schema(description = "微信", example = "123456789") + @DiffLogField(name = "微信") + @Size(max = 255, message = "微信长度ä¸èƒ½è¶…过 255 个字符") + private String wechat; + + @Schema(description = "邮箱", example = "123456789@qq.com") + @DiffLogField(name = "邮箱") + @Email(message = "邮箱格å¼ä¸æ­£ç¡®") + @Size(max = 255, message = "邮箱长度ä¸èƒ½è¶…过 255 个字符") + private String email; + + @Schema(description = "客户æè¿°", example = "ä»»æ„文字") + @DiffLogField(name = "客户æè¿°") + @Size(max = 4096, message = "客户æ述长度ä¸èƒ½è¶…过 4096 个字符") + private String description; + + @Schema(description = "备注", example = "éšä¾¿") + @DiffLogField(name = "备注") + private String remark; + + @Schema(description = "地区编å·", example = "20158") + @DiffLogField(name = "地区编å·", function = SysAreaParseFunction.NAME) + private Integer areaId; + + @Schema(description = "详细地å€", example = "北京市海淀区") + @DiffLogField(name = "详细地å€") + private String detailAddress; + + @Schema(description = "下次è”系时间") + @DiffLogField(name = "下次è”系时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime contactNextTime; + + @Schema(description = "负责人的用户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") + private Long ownerUserId; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerTransferReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerTransferReqVO.java new file mode 100644 index 000000000..9bdc43532 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerTransferReqVO.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo; + +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管ç†åŽå° - CRM 客户转移 Request VO") +@Data +public class CrmCustomerTransferReqVO { + + @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 newOwnerUserId; + + /** + * è€è´Ÿè´£äººåŠ å…¥å›¢é˜ŸåŽçš„æƒé™çº§åˆ«ã€‚如果 null 说明移除 + * + * å…³è” {@link CrmPermissionLevelEnum} + */ + @Schema(description = "è€è´Ÿè´£äººåŠ å…¥å›¢é˜ŸåŽçš„æƒé™çº§åˆ«", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Integer oldOwnerPermissionLevel; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/limitconfig/CrmCustomerLimitConfigPageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/limitconfig/CrmCustomerLimitConfigPageReqVO.java new file mode 100644 index 000000000..37ce11009 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/limitconfig/CrmCustomerLimitConfigPageReqVO.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig; + +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 CrmCustomerLimitConfigPageReqVO extends PageParam { + + @Schema(description = "规则类型", example = "1") + private Integer type; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/limitconfig/CrmCustomerLimitConfigRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/limitconfig/CrmCustomerLimitConfigRespVO.java new file mode 100644 index 000000000..8ff03ad66 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/limitconfig/CrmCustomerLimitConfigRespVO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig; + +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管ç†åŽå° - 客户é™åˆ¶é…ç½® Response VO") +@Data +public class CrmCustomerLimitConfigRespVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "27930") + private Long id; + + @Schema(description = "规则类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Integer type; + + @Schema(description = "规则适用人群") + private List userIds; + + @Schema(description = "规则适用部门") + private List deptIds; + + @Schema(description = "æ•°é‡ä¸Šé™", requiredMode = Schema.RequiredMode.REQUIRED, example = "28384") + private Integer maxCount; + + @Schema(description = "æˆäº¤å®¢æˆ·æ˜¯å¦å æœ‰æ‹¥æœ‰å®¢æˆ·æ•°") + private Boolean dealCountEnabled; + + @Schema(description = "规则适用人群å称") + private List users; + + @Schema(description = "规则适用部门å称") + private List depts; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/limitconfig/CrmCustomerLimitConfigSaveReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/limitconfig/CrmCustomerLimitConfigSaveReqVO.java new file mode 100644 index 000000000..a0e88e3f6 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/limitconfig/CrmCustomerLimitConfigSaveReqVO.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig; + +import cn.iocoder.yudao.module.crm.framework.operatelog.core.SysAdminUserParseFunction; +import cn.iocoder.yudao.module.crm.framework.operatelog.core.SysDeptParseFunction; +import com.mzt.logapi.starter.annotation.DiffLogField; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管ç†åŽå° - 客户é™åˆ¶é…置创建/æ›´æ–° Request VO") +@Data +public class CrmCustomerLimitConfigSaveReqVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "27930") + private Long id; + + @Schema(description = "规则类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @NotNull(message = "规则类型ä¸èƒ½ä¸ºç©º") + @DiffLogField(name = "规则类型") + private Integer type; + + @Schema(description = "规则适用人群") + @DiffLogField(name = "规则适用人群", function = SysAdminUserParseFunction.NAME) + private List userIds; + + @Schema(description = "规则适用部门") + @DiffLogField(name = "规则适用部门", function = SysDeptParseFunction.NAME) + private List deptIds; + + @Schema(description = "æ•°é‡ä¸Šé™", requiredMode = Schema.RequiredMode.REQUIRED, example = "28384") + @NotNull(message = "æ•°é‡ä¸Šé™ä¸èƒ½ä¸ºç©º") + @DiffLogField(name = "æ•°é‡ä¸Šé™") + private Integer maxCount; + + @Schema(description = "æˆäº¤å®¢æˆ·æ˜¯å¦å æœ‰æ‹¥æœ‰å®¢æˆ·æ•°(当 type = 1 æ—¶)") + @DiffLogField(name = "æˆäº¤å®¢æˆ·æ˜¯å¦å æœ‰æ‹¥æœ‰å®¢æˆ·æ•°") + private Boolean dealCountEnabled; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/poolconfig/CrmCustomerPoolConfigRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/poolconfig/CrmCustomerPoolConfigRespVO.java new file mode 100644 index 000000000..2aeb3402e --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/poolconfig/CrmCustomerPoolConfigRespVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.poolconfig; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管ç†åŽå° - CRM 客户公海规则 Response VO") +@Data +public class CrmCustomerPoolConfigRespVO { + + @Schema(description = "是å¦å¯ç”¨å®¢æˆ·å…¬æµ·", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是å¦å¯ç”¨å®¢æˆ·å…¬æµ·ä¸èƒ½ä¸ºç©º") + private Boolean enabled; + + @Schema(description = "未跟进放入公海天数", example = "2") + private Integer contactExpireDays; + + @Schema(description = "未æˆäº¤æ”¾å…¥å…¬æµ·å¤©æ•°", example = "2") + private Integer dealExpireDays; + + @Schema(description = "是å¦å¼€å¯æå‰æ醒", example = "true") + private Boolean notifyEnabled; + + @Schema(description = "æå‰æ醒天数", example = "2") + private Integer notifyDays; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/poolconfig/CrmCustomerPoolConfigSaveReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/poolconfig/CrmCustomerPoolConfigSaveReqVO.java new file mode 100644 index 000000000..3215f8645 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/poolconfig/CrmCustomerPoolConfigSaveReqVO.java @@ -0,0 +1,65 @@ +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.poolconfig; + +import cn.hutool.core.util.BooleanUtil; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.mzt.logapi.starter.annotation.DiffLogField; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.Objects; + +@Schema(description = "管ç†åŽå° - CRM 客户公海é…置的创建/æ›´æ–° Request VO") +@Data +public class CrmCustomerPoolConfigSaveReqVO { + + @Schema(description = "是å¦å¯ç”¨å®¢æˆ·å…¬æµ·", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @DiffLogField(name = "是å¦å¯ç”¨å®¢æˆ·å…¬æµ·") + @NotNull(message = "是å¦å¯ç”¨å®¢æˆ·å…¬æµ·ä¸èƒ½ä¸ºç©º") + private Boolean enabled; + + @Schema(description = "未跟进放入公海天数", example = "2") + @DiffLogField(name = "未跟进放入公海天数") + private Integer contactExpireDays; + + @Schema(description = "未æˆäº¤æ”¾å…¥å…¬æµ·å¤©æ•°", example = "2") + @DiffLogField(name = "未æˆäº¤æ”¾å…¥å…¬æµ·å¤©æ•°") + private Integer dealExpireDays; + + @Schema(description = "是å¦å¼€å¯æå‰æ醒", example = "true") + @DiffLogField(name = "是å¦å¼€å¯æå‰æ醒") + private Boolean notifyEnabled; + + @Schema(description = "æå‰æ醒天数", example = "2") + @DiffLogField(name = "æå‰æ醒天数") + private Integer notifyDays; + + @AssertTrue(message = "未æˆäº¤æ”¾å…¥å…¬æµ·å¤©æ•°ä¸èƒ½ä¸ºç©º") + @JsonIgnore + public boolean isDealExpireDaysValid() { + if (!BooleanUtil.isTrue(getEnabled())) { + return true; + } + return Objects.nonNull(getDealExpireDays()); + } + + @AssertTrue(message = "未跟进放入公海天数ä¸èƒ½ä¸ºç©º") + @JsonIgnore + public boolean isContactExpireDaysValid() { + if (!BooleanUtil.isTrue(getEnabled())) { + return true; + } + return Objects.nonNull(getContactExpireDays()); + } + + @AssertTrue(message = "æå‰æ醒天数ä¸èƒ½ä¸ºç©º") + @JsonIgnore + public boolean isNotifyDaysValid() { + if (!BooleanUtil.isTrue(getNotifyEnabled())) { + return true; + } + return Objects.nonNull(getNotifyDays()); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/CrmFollowUpRecordController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/CrmFollowUpRecordController.java new file mode 100644 index 000000000..f0b726353 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/CrmFollowUpRecordController.java @@ -0,0 +1,92 @@ +package cn.iocoder.yudao.module.crm.controller.admin.followup; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordSaveReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO; +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; +import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; +import cn.iocoder.yudao.module.crm.service.followup.CrmFollowUpRecordService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + + +@Tag(name = "管ç†åŽå° - 跟进记录") +@RestController +@RequestMapping("/crm/follow-up-record") +@Validated +public class CrmFollowUpRecordController { + + @Resource + private CrmFollowUpRecordService followUpRecordService; + @Resource + private CrmContactService contactService; + @Resource + private CrmBusinessService businessService; + + @PostMapping("/create") + @Operation(summary = "创建跟进记录") + @PreAuthorize("@ss.hasPermission('crm:follow-up-record:create')") + public CommonResult createFollowUpRecord(@Valid @RequestBody CrmFollowUpRecordSaveReqVO createReqVO) { + return success(followUpRecordService.createFollowUpRecord(createReqVO)); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除跟进记录") + @Parameter(name = "id", description = "ç¼–å·", required = true) + @PreAuthorize("@ss.hasPermission('crm:follow-up-record:delete')") + public CommonResult deleteFollowUpRecord(@RequestParam("id") Long id) { + followUpRecordService.deleteFollowUpRecord(id, getLoginUserId()); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得跟进记录") + @Parameter(name = "id", description = "ç¼–å·", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('crm:follow-up-record:query')") + public CommonResult getFollowUpRecord(@RequestParam("id") Long id) { + CrmFollowUpRecordDO followUpRecord = followUpRecordService.getFollowUpRecord(id); + return success(BeanUtils.toBean(followUpRecord, CrmFollowUpRecordRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得跟进记录分页") + @PreAuthorize("@ss.hasPermission('crm:follow-up-record:query')") + public CommonResult> getFollowUpRecordPage(@Valid CrmFollowUpRecordPageReqVO pageReqVO) { + PageResult pageResult = followUpRecordService.getFollowUpRecordPage(pageReqVO); + /// æ‹¼æŽ¥æ•°æ® + Map contactMap = convertMap(contactService.getContactListByIds( + convertSetByFlatMap(pageResult.getList(), item -> item.getContactIds().stream())), CrmContactDO::getId); + Map businessMap = convertMap(businessService.getBusinessList( + convertSetByFlatMap(pageResult.getList(), item -> item.getBusinessIds().stream())), CrmBusinessDO::getId); + PageResult voPageResult = BeanUtils.toBean(pageResult, CrmFollowUpRecordRespVO.class, record -> { + record.setContactNames(new ArrayList<>()).setBusinessNames(new ArrayList<>()); + record.getContactIds().forEach(id -> MapUtils.findAndThen(contactMap, id, + contact -> record.getContactNames().add(contact.getName()))); + record.getContactIds().forEach(id -> MapUtils.findAndThen(businessMap, id, + business -> record.getBusinessNames().add(business.getName()))); + }); + return success(voPageResult); + } + +} \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordPageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordPageReqVO.java new file mode 100644 index 000000000..78c28a08f --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordPageReqVO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.crm.controller.admin.followup.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 CrmFollowUpRecordPageReqVO extends PageParam { + + @Schema(description = "æ•°æ®ç±»åž‹", example = "2") + private Integer bizType; + + @Schema(description = "æ•°æ®ç¼–å·", example = "5564") + private Long bizId; + +} \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordRespVO.java new file mode 100644 index 000000000..83bfd9edc --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordRespVO.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.crm.controller.admin.followup.vo; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_FOLLOW_UP_TYPE; + +@Schema(description = "管ç†åŽå° - 跟进记录 Response VO") +@Data +@ExcelIgnoreUnannotated +public class CrmFollowUpRecordRespVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "28800") + private Long id; + + @Schema(description = "æ•°æ®ç±»åž‹", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Integer bizType; + + @Schema(description = "æ•°æ®ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "5564") + private Long bizId; + + @Schema(description = "跟进类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @DictFormat(CRM_FOLLOW_UP_TYPE) + private Integer type; + + @Schema(description = "跟进内容", requiredMode = Schema.RequiredMode.REQUIRED) + private String content; + + @Schema(description = "下次è”系时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime nextTime; + + @Schema(description = "å…³è”的商机编å·æ•°ç»„") + private List businessIds; + @Schema(description = "å…³è”的商机å称数组") + private List businessNames; + + @Schema(description = "å…³è”çš„è”系人编å·æ•°ç»„") + private List contactIds; + @Schema(description = "å…³è”çš„è”系人å称数组") + private List contactNames; + + @Schema(description = "图片") + private List picUrls; + @Schema(description = "附件") + private List fileUrls; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordSaveReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordSaveReqVO.java new file mode 100644 index 000000000..c4d53859b --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordSaveReqVO.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.crm.controller.admin.followup.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管ç†åŽå° - 跟进记录新增/修改 Request VO") +@Data +public class CrmFollowUpRecordSaveReqVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "28800") + private Long id; + + @Schema(description = "æ•°æ®ç±»åž‹", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @NotNull(message = "æ•°æ®ç±»åž‹ä¸èƒ½ä¸ºç©º") + private Integer bizType; + + @Schema(description = "æ•°æ®ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "5564") + @NotNull(message = "æ•°æ®ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long bizId; + + @Schema(description = "跟进类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @NotNull(message = "跟进类型ä¸èƒ½ä¸ºç©º") + private Integer type; + + @Schema(description = "跟进内容", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "跟进内容ä¸èƒ½ä¸ºç©º") + private String content; + + @Schema(description = "下次è”系时间", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "下次è”系时间ä¸èƒ½ä¸ºç©º") + private LocalDateTime nextTime; + + @Schema(description = "å…³è”的商机编å·æ•°ç»„") + private List businessIds; + @Schema(description = "å…³è”çš„è”系人编å·æ•°ç»„") + private List contactIds; + + @Schema(description = "图片") + private List picUrls; + @Schema(description = "附件") + private List fileUrls; + +} \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/CrmOperateLogController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/CrmOperateLogController.java new file mode 100644 index 000000000..1793f66d2 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/CrmOperateLogController.java @@ -0,0 +1,64 @@ +package cn.iocoder.yudao.module.crm.controller.admin.operatelog; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo.CrmOperateLogPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo.CrmOperateLogV2RespVO; +import cn.iocoder.yudao.module.crm.enums.LogRecordConstants; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.system.api.logger.OperateLogApi; +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2PageReqDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; +import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; + +@Tag(name = "管ç†åŽå° - CRM æ“作日志") +@RestController +@RequestMapping("/crm/operate-log") +@Validated +public class CrmOperateLogController { + + @Resource + private OperateLogApi operateLogApi; + + /** + * {@link CrmBizTypeEnum} 与 {@link LogRecordConstants} 的映射关系 + */ + private static final Map BIZ_TYPE_MAP = new HashMap<>(); + + static { + BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_LEADS.getType(), CRM_LEADS_TYPE); + BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_CUSTOMER.getType(), CRM_CUSTOMER_TYPE); + BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_CONTACT.getType(), CRM_CONTACT_TYPE); + BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_BUSINESS.getType(), CRM_BUSINESS_TYPE); + BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_CONTRACT.getType(), CRM_CONTRACT_TYPE); + BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_PRODUCT.getType(), CRM_PRODUCT_TYPE); + BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_RECEIVABLE.getType(), CRM_RECEIVABLE_TYPE); + BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType(), CRM_RECEIVABLE_PLAN_TYPE); + } + + @GetMapping("/page") + @Operation(summary = "获得æ“作日志") + @PreAuthorize("@ss.hasPermission('crm:operate-log:query')") + public CommonResult> getCustomerOperateLog(@Valid CrmOperateLogPageReqVO pageReqVO) { + OperateLogV2PageReqDTO reqDTO = new OperateLogV2PageReqDTO(); + reqDTO.setPageSize(PAGE_SIZE_NONE); // 默认ä¸åˆ†é¡µï¼Œéœ€è¦åˆ†é¡µéœ€æ³¨é‡Š + reqDTO.setBizType(BIZ_TYPE_MAP.get(pageReqVO.getBizType())).setBizId(pageReqVO.getBizId()); + return success(BeanUtils.toBean(operateLogApi.getOperateLogPage(reqDTO).getCheckedData(), CrmOperateLogV2RespVO.class)); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogPageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogPageReqVO.java new file mode 100644 index 000000000..f49ccb38b --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogPageReqVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管ç†åŽå° - CRM æ“作日志 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CrmOperateLogPageReqVO extends PageParam { + + @Schema(description = "æ•°æ®ç±»åž‹", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @InEnum(CrmBizTypeEnum.class) + @NotNull(message = "æ•°æ®ç±»åž‹ä¸èƒ½ä¸ºç©º") + private Integer bizType; + + @Schema(description = "æ•°æ®ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @NotNull(message = "æ•°æ®ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long bizId; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogV2RespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogV2RespVO.java new file mode 100644 index 000000000..b3405428f --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogV2RespVO.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管ç†åŽå° - CRM è·Ÿè¿› Response VO") +@Data +@ExcelIgnoreUnannotated +public class CrmOperateLogV2RespVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") + private Long id; + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long userId; + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + private String userName; + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer userType; + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") + private String type; + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "修改客户") + private String subType; + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") + private Long bizId; + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "将什么从什么改为了什么") + private String action; + + @Schema(description = "ç¼–å·", example = "{orderId: 1}") + private String extra; + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-01-01") + private LocalDateTime createTime; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.http b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.http new file mode 100644 index 000000000..1ef2bc1a1 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.http @@ -0,0 +1,32 @@ +### 请求 /add +POST {{baseUrl}}/crm/permission/create +Content-Type: application/json +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} + +{ + "userId": 1, + "bizType": 2, + "bizId": 2, + "level": 1 +} + +### 请求 /update +PUT {{baseUrl}}/crm/permission/update +Content-Type: application/json +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} + +{ + "userId": 1, + "bizType": 2, + "bizId": 2, + "level": 1, + "id": 1 +} + +### 请求 /delete +DELETE {{baseUrl}}/crm/permission/delete?bizType=2&bizId=1&id=1 +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} + diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.java new file mode 100644 index 000000000..63085cab0 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.java @@ -0,0 +1,116 @@ +package cn.iocoder.yudao.module.crm.controller.admin.permission; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionCreateReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO; +import cn.iocoder.yudao.module.crm.convert.permission.CrmPermissionConvert; +import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; +import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; +import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; +import cn.iocoder.yudao.module.system.api.dept.DeptApi; +import cn.iocoder.yudao.module.system.api.dept.PostApi; +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; +import cn.iocoder.yudao.module.system.api.dept.dto.PostRespDTO; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.*; +import java.util.stream.Stream; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "管ç†åŽå° - CRM æ•°æ®æƒé™") +@RestController +@RequestMapping("/crm/permission") +@Validated +public class CrmPermissionController { + + @Resource + private CrmPermissionService permissionService; + + @Resource + private AdminUserApi adminUserApi; + @Resource + private DeptApi deptApi; + @Resource + private PostApi postApi; + + @PostMapping("/create") + @Operation(summary = "创建数æ®æƒé™") + @PreAuthorize("@ss.hasPermission('crm:permission:create')") + @CrmPermission(bizTypeValue = "#reqVO.bizType", bizId = "#reqVO.bizId", level = CrmPermissionLevelEnum.OWNER) + public CommonResult addPermission(@Valid @RequestBody CrmPermissionCreateReqVO reqVO) { + permissionService.createPermission(BeanUtils.toBean(reqVO, CrmPermissionCreateReqBO.class)); + return success(true); + } + + @PutMapping("/update") + @Operation(summary = "编辑数æ®æƒé™") + @PreAuthorize("@ss.hasPermission('crm:permission:update')") + @CrmPermission(bizTypeValue = "#updateReqVO.bizType", bizId = "#updateReqVO.bizId" + , level = CrmPermissionLevelEnum.OWNER) + public CommonResult updatePermission(@Valid @RequestBody CrmPermissionUpdateReqVO updateReqVO) { + permissionService.updatePermission(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除数æ®æƒé™") + @Parameter(name = "ids", description = "æ•°æ®æƒé™ç¼–å·", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('crm:permission:delete')") + public CommonResult deletePermission(@RequestParam("ids") Collection ids) { + permissionService.deletePermissionBatch(ids, getLoginUserId()); + return success(true); + } + + @DeleteMapping("/delete-self") + @Operation(summary = "删除自己的数æ®æƒé™") + @Parameter(name = "id", description = "æ•°æ®æƒé™ç¼–å·", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('crm:permission:delete')") + public CommonResult deleteSelfPermission(@RequestParam("id") Long id) { + permissionService.deleteSelfPermission(id, getLoginUserId()); + return success(true); + } + + @GetMapping("/list") + @Operation(summary = "获得数æ®æƒé™åˆ—表") + @Parameters({ + @Parameter(name = "bizType", description = "CRM 类型", required = true, example = "2"), + @Parameter(name = "bizId", description = "CRM 类型数æ®ç¼–å·", required = true, example = "1024") + }) + @PreAuthorize("@ss.hasPermission('crm:permission:query')") + public CommonResult> getPermissionList(@RequestParam("bizType") Integer bizType, + @RequestParam("bizId") Long bizId) { + List permission = permissionService.getPermissionListByBiz(bizType, bizId); + if (CollUtil.isEmpty(permission)) { + return success(Collections.emptyList()); + } + + // æ‹¼æŽ¥æ•°æ® + List userList = adminUserApi.getUserList(convertSet(permission, CrmPermissionDO::getUserId)) + .getCheckedData(); + Map deptMap = deptApi.getDeptMap(convertSet(userList, AdminUserRespDTO::getDeptId)); + Set postIds = CollectionUtils.convertSetByFlatMap(userList, AdminUserRespDTO::getPostIds, + item -> item != null ? item.stream() : Stream.empty()); + Map postMap = postApi.getPostMap(postIds); + return success(CrmPermissionConvert.INSTANCE.convert(permission, userList, deptMap, postMap)); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionBaseVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionBaseVO.java new file mode 100644 index 000000000..796b3cd46 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionBaseVO.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.crm.controller.admin.permission.vo; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotNull; + +/** + * æ•°æ®æƒé™ Base VO,æ供给添加ã€ä¿®æ”¹ã€è¯¦ç»†çš„å­ VO 使用 + * å¦‚æžœå­ VO 存在差异的字段,请ä¸è¦æ·»åŠ åˆ°è¿™é‡Œï¼Œå½±å“ Swagger æ–‡æ¡£ç”Ÿæˆ + * + * @author HUIHUI + */ +@Data +public class CrmPermissionBaseVO { + + @Schema(description = "用户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456") + @NotNull(message = "用户编å·ä¸èƒ½ä¸ºç©º") + private Long userId; + + @Schema(description = "CRM 类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @InEnum(CrmBizTypeEnum.class) + @NotNull(message = "CRM 类型ä¸èƒ½ä¸ºç©º") + private Integer bizType; + + @Schema(description = "CRM 类型数æ®ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "CRM 类型数æ®ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long bizId; + + @Schema(description = "æƒé™çº§åˆ«", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @InEnum(CrmPermissionLevelEnum.class) + @NotNull(message = "æƒé™çº§åˆ«ä¸èƒ½ä¸ºç©º") + private Integer level; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionCreateReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionCreateReqVO.java new file mode 100644 index 000000000..99793389b --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionCreateReqVO.java @@ -0,0 +1,14 @@ +package cn.iocoder.yudao.module.crm.controller.admin.permission.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 CrmPermissionCreateReqVO extends CrmPermissionBaseVO { + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionRespVO.java new file mode 100644 index 000000000..10f1ce198 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionRespVO.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.crm.controller.admin.permission.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.Set; + +@Schema(description = "管ç†åŽå° - CRM æ•°æ®æƒé™ Response VO") +@Data +public class CrmPermissionRespVO extends CrmPermissionBaseVO { + + @Schema(description = "æ•°æ®æƒé™ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") + private Long id; + + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + private String nickname; + + @Schema(description = "部门å称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ç ”å‘部") + private String deptName; + + @Schema(description = "å²—ä½å称数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[BOOS,ç»ç†]") + private Set postNames; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2023-01-01 00:00:00") + private LocalDateTime createTime; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionUpdateReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionUpdateReqVO.java new file mode 100644 index 000000000..26c94728a --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionUpdateReqVO.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.crm.controller.admin.permission.vo; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotNull; +import java.util.List; + +@Schema(description = "管ç†åŽå° - CRM æ•°æ®æƒé™æ›´æ–° Request VO") +@Data +public class CrmPermissionUpdateReqVO { + + @Schema(description = "æ•°æ®æƒé™ç¼–å·åˆ—表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2]") + @NotNull(message = "æ•°æ®æƒé™ç¼–å·åˆ—表ä¸èƒ½ä¸ºç©º") + private List ids; + + @Schema(description = "Crm 类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @InEnum(CrmBizTypeEnum.class) + @NotNull(message = "Crm 类型ä¸èƒ½ä¸ºç©º") + private Integer bizType; + + @Schema(description = "Crm 类型数æ®ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "Crm 类型数æ®ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long bizId; + + @Schema(description = "æƒé™çº§åˆ«", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @InEnum(CrmPermissionLevelEnum.class) + @NotNull(message = "æƒé™çº§åˆ«ä¸èƒ½ä¸ºç©º") + private Integer level; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/CrmProductCategoryController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/CrmProductCategoryController.java new file mode 100644 index 000000000..2c840d595 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/CrmProductCategoryController.java @@ -0,0 +1,73 @@ +package cn.iocoder.yudao.module.crm.controller.admin.product; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.crm.controller.admin.product.vo.category.CrmProductCategoryCreateReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.product.vo.category.CrmProductCategoryListReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.product.vo.category.CrmProductCategoryRespVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO; +import cn.iocoder.yudao.module.crm.service.product.CrmProductCategoryService; +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 jakarta.annotation.Resource; +import jakarta.validation.Valid; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管ç†åŽå° - CRM 产å“分类") +@RestController +@RequestMapping("/crm/product-category") +@Validated +public class CrmProductCategoryController { + + @Resource + private CrmProductCategoryService productCategoryService; + + @PostMapping("/create") + @Operation(summary = "创建产å“分类") + @PreAuthorize("@ss.hasPermission('crm:product-category:create')") + public CommonResult createProductCategory(@Valid @RequestBody CrmProductCategoryCreateReqVO createReqVO) { + return success(productCategoryService.createProductCategory(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新产å“分类") + @PreAuthorize("@ss.hasPermission('crm:product-category:update')") + public CommonResult updateProductCategory(@Valid @RequestBody CrmProductCategoryCreateReqVO updateReqVO) { + productCategoryService.updateProductCategory(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除产å“分类") + @Parameter(name = "id", description = "ç¼–å·", required = true) + @PreAuthorize("@ss.hasPermission('crm:product-category:delete')") + public CommonResult deleteProductCategory(@RequestParam("id") Long id) { + productCategoryService.deleteProductCategory(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得产å“分类") + @Parameter(name = "id", description = "ç¼–å·", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('crm:product-category:query')") + public CommonResult getProductCategory(@RequestParam("id") Long id) { + CrmProductCategoryDO category = productCategoryService.getProductCategory(id); + return success(BeanUtils.toBean(category, CrmProductCategoryRespVO.class)); + } + + @GetMapping("/list") + @Operation(summary = "获得产å“分类列表") + @PreAuthorize("@ss.hasPermission('crm:product-category:query')") + public CommonResult> getProductCategoryList(@Valid CrmProductCategoryListReqVO listReqVO) { + List list = productCategoryService.getProductCategoryList(listReqVO); + return success(BeanUtils.toBean(list, CrmProductCategoryRespVO.class)); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/CrmProductController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/CrmProductController.java new file mode 100644 index 000000000..94774373d --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/CrmProductController.java @@ -0,0 +1,126 @@ +package cn.iocoder.yudao.module.crm.controller.admin.product; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.SetUtils; +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.product.vo.product.CrmProductPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductSaveReqVO; +import cn.iocoder.yudao.module.crm.convert.product.CrmProductConvert; +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; +import cn.iocoder.yudao.module.crm.service.product.CrmProductCategoryService; +import cn.iocoder.yudao.module.crm.service.product.CrmProductService; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; +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/product") +@Validated +public class CrmProductController { + + @Resource + private CrmProductService productService; + @Resource + private CrmProductCategoryService productCategoryService; + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建产å“") + @PreAuthorize("@ss.hasPermission('crm:product:create')") + public CommonResult createProduct(@Valid @RequestBody CrmProductSaveReqVO createReqVO) { + return success(productService.createProduct(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新产å“") + @PreAuthorize("@ss.hasPermission('crm:product:update')") + public CommonResult updateProduct(@Valid @RequestBody CrmProductSaveReqVO updateReqVO) { + productService.updateProduct(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除产å“") + @Parameter(name = "id", description = "ç¼–å·", required = true) + @PreAuthorize("@ss.hasPermission('crm:product:delete')") + public CommonResult deleteProduct(@RequestParam("id") Long id) { + productService.deleteProduct(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得产å“") + @Parameter(name = "id", description = "ç¼–å·", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('crm:product:query')") + public CommonResult getProduct(@RequestParam("id") Long id) { + CrmProductDO product = productService.getProduct(id); + if (product == null) { + return success(null); + } + Map userMap = adminUserApi.getUserMap( + SetUtils.asSet(Long.valueOf(product.getCreator()), product.getOwnerUserId())); + CrmProductCategoryDO category = productCategoryService.getProductCategory(product.getCategoryId()); + return success(CrmProductConvert.INSTANCE.convert(product, userMap, category)); + } + + @GetMapping("/page") + @Operation(summary = "获得产å“分页") + @PreAuthorize("@ss.hasPermission('crm:product:query')") + public CommonResult> getProductPage(@Valid CrmProductPageReqVO pageVO) { + PageResult pageResult = productService.getProductPage(pageVO, getLoginUserId()); + return success(new PageResult<>(getProductDetailList(pageResult.getList()), pageResult.getTotal())); + } + + @GetMapping("/export-excel") + @Operation(summary = "å¯¼å‡ºäº§å“ Excel") + @PreAuthorize("@ss.hasPermission('crm:product:export')") + @OperateLog(type = EXPORT) + public void exportProductExcel(@Valid CrmProductPageReqVO exportReqVO, + HttpServletResponse response) throws IOException { + exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = productService.getProductPage(exportReqVO, getLoginUserId()).getList(); + // 导出 Excel + ExcelUtils.write(response, "产å“.xls", "æ•°æ®", CrmProductRespVO.class, + getProductDetailList(list)); + } + + private List getProductDetailList(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + Map userMap = adminUserApi.getUserMap( + convertSetByFlatMap(list, user -> Stream.of(Long.valueOf(user.getCreator()), user.getOwnerUserId()))); + List productCategoryList = productCategoryService.getProductCategoryList( + convertSet(list, CrmProductDO::getCategoryId)); + return CrmProductConvert.INSTANCE.convertList(list, userMap, productCategoryList); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/category/CrmProductCategoryCreateReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/category/CrmProductCategoryCreateReqVO.java new file mode 100644 index 000000000..bb17806a8 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/category/CrmProductCategoryCreateReqVO.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.crm.controller.admin.product.vo.category; + +import com.mzt.logapi.starter.annotation.DiffLogField; +import lombok.*; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotNull; + +@Schema(description = "管ç†åŽå° - CRM 产å“分类创建/æ›´æ–° Request VO") +@Data +public class CrmProductCategoryCreateReqVO{ + + @Schema(description = "分类编å·", example = "23902") + private Long id; + + @Schema(description = "分类å称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") + @NotNull(message = "分类å称ä¸èƒ½ä¸ºç©º") + @DiffLogField(name = "分类å称") + private String name; + + @Schema(description = "父级编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "4680") + @NotNull(message = "父级编å·ä¸èƒ½ä¸ºç©º") + private Long parentId; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/category/CrmProductCategoryListReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/category/CrmProductCategoryListReqVO.java new file mode 100644 index 000000000..6144c95c4 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/category/CrmProductCategoryListReqVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.crm.controller.admin.product.vo.category; + +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管ç†åŽå° - CRM 产å“分类列表 Request VO") +@Data +public class CrmProductCategoryListReqVO { + + @ExcelProperty("å称") + private String name; + + @ExcelProperty("父级 id") + private Long parentId; + + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/category/CrmProductCategoryRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/category/CrmProductCategoryRespVO.java new file mode 100644 index 000000000..4cea8e464 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/category/CrmProductCategoryRespVO.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.crm.controller.admin.product.vo.category; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +import java.time.LocalDateTime; + +@Schema(description = "管ç†åŽå° - CRM 产å“分类 Response VO") +@Data +public class CrmProductCategoryRespVO { + + @Schema(description = "分类编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "23902") + private Long id; + + @Schema(description = "分类å称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") + private String name; + + @Schema(description = "父级编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "4680") + private Long parentId; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/product/CrmProductPageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/product/CrmProductPageReqVO.java new file mode 100644 index 000000000..39b1090f8 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/product/CrmProductPageReqVO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.crm.controller.admin.product.vo.product; + +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 CrmProductPageReqVO extends PageParam { + + @Schema(description = "产å“å称", example = "æŽå››") + private String name; + + @Schema(description = "状æ€", example = "1") + private Integer status; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/product/CrmProductRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/product/CrmProductRespVO.java new file mode 100644 index 000000000..ceca3e5a0 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/product/CrmProductRespVO.java @@ -0,0 +1,74 @@ +package cn.iocoder.yudao.module.crm.controller.admin.product.vo.product; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.module.crm.enums.DictTypeConstants; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管ç†åŽå° - CRM äº§å“ Response VO") +@Data +@ExcelIgnoreUnannotated +public class CrmProductRespVO { + + @Schema(description = "产å“ç¼–å·", example = "20529") + @ExcelProperty("产å“ç¼–å·") + private Long id; + + @Schema(description = "产å“å称", requiredMode = Schema.RequiredMode.REQUIRED, example = "好产å“") + @ExcelProperty("产å“å称") + private String name; + + @Schema(description = "产å“ç¼–ç ", requiredMode = Schema.RequiredMode.REQUIRED, example = "12306") + @ExcelProperty("产å“ç¼–ç ") + private String no; + + @Schema(description = "å•ä½", example = "2") + @ExcelProperty(value = "å•ä½", converter = DictConvert.class) + @DictFormat(DictTypeConstants.CRM_PRODUCT_UNIT) + private Integer unit; + + @Schema(description = "ä»·æ ¼, å•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911") + @ExcelProperty("价格,å•ä½ï¼šåˆ†") + private Long price; + + @Schema(description = "状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "上架") + @ExcelProperty(value = "å•ä½", converter = DictConvert.class) + @DictFormat(DictTypeConstants.CRM_PRODUCT_STATUS) + private Integer status; + + @Schema(description = "产å“分类编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Long categoryId; + @Schema(description = "产å“分类åå­—", requiredMode = Schema.RequiredMode.REQUIRED, example = "è¡£æœ") + @ExcelProperty("产å“分类") + private String categoryName; + + @Schema(description = "产å“æè¿°", example = "你说的对") + @ExcelProperty("产å“æè¿°") + private String description; + + @Schema(description = "负责人的用户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "31926") + private Long ownerUserId; + @Schema(description = "负责人的用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋é“æºç ") + @ExcelProperty("负责人") + private String ownerUserName; + + @Schema(description = "创建人编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private String creator; + @Schema(description = "创建人åå­—", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋é“æºç ") + @ExcelProperty("创建人") + private String creatorName; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("更新时间") + private LocalDateTime updateTime; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/product/CrmProductSaveReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/product/CrmProductSaveReqVO.java new file mode 100644 index 000000000..01b2ae443 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/product/CrmProductSaveReqVO.java @@ -0,0 +1,54 @@ +package cn.iocoder.yudao.module.crm.controller.admin.product.vo.product; + +import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmProductStatusParseFunction; +import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmProductUnitParseFunction; +import com.mzt.logapi.starter.annotation.DiffLogField; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管ç†åŽå° - CRM 产å“创建/修改 Request VO") +@Data +public class CrmProductSaveReqVO { + + @Schema(description = "产å“ç¼–å·", example = "20529") + private Long id; + + @Schema(description = "产å“å称", requiredMode = Schema.RequiredMode.REQUIRED, example = "好产å“") + @NotNull(message = "产å“å称ä¸èƒ½ä¸ºç©º") + @DiffLogField(name = "产å“å称") + private String name; + + @Schema(description = "产å“ç¼–ç ", requiredMode = Schema.RequiredMode.REQUIRED, example = "12306") + @NotNull(message = "产å“ç¼–ç ä¸èƒ½ä¸ºç©º") + @DiffLogField(name = "产å“ç¼–ç ") + private String no; + + @Schema(description = "å•ä½", example = "2") + @DiffLogField(name = "å•ä½", function = CrmProductUnitParseFunction.NAME) + private Integer unit; + + @Schema(description = "ä»·æ ¼, å•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911") + @NotNull(message = "ä»·æ ¼ä¸èƒ½ä¸ºç©º") + @DiffLogField(name = "ä»·æ ¼") + private Long price; + + @Schema(description = "状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "上架") + @NotNull(message = "状æ€ä¸èƒ½ä¸ºç©º") + @DiffLogField(name = "状æ€", function = CrmProductStatusParseFunction.NAME) + private Integer status; + + @Schema(description = "产å“分类编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @NotNull(message = "产å“分类编å·ä¸èƒ½ä¸ºç©º") + @DiffLogField(name = "产å“分类编å·") + private Long categoryId; + + @Schema(description = "产å“æè¿°", example = "你说的对") + @DiffLogField(name = "产å“æè¿°") + private String description; + + @Schema(description = "负责人的用户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "31926") + @NotNull(message = "负责人的用户编å·ä¸èƒ½ä¸ºç©º") + private Long ownerUserId; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java new file mode 100644 index 000000000..8516ebd66 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java @@ -0,0 +1,147 @@ +package cn.iocoder.yudao.module.crm.controller.admin.receivable; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; +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.receivable.CrmReceivableCreateReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableUpdateReqVO; +import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivableConvert; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO; +import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; +import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; +import cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableService; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +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/receivable") +@Validated +public class CrmReceivableController { + + @Resource + private CrmReceivableService receivableService; + @Resource + private CrmContractService contractService; + @Resource + private CrmCustomerService customerService; + + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建回款") + @PreAuthorize("@ss.hasPermission('crm:receivable:create')") + public CommonResult createReceivable(@Valid @RequestBody CrmReceivableCreateReqVO createReqVO) { + return success(receivableService.createReceivable(createReqVO, getLoginUserId())); + } + + @PutMapping("/update") + @Operation(summary = "更新回款") + @PreAuthorize("@ss.hasPermission('crm:receivable:update')") + public CommonResult updateReceivable(@Valid @RequestBody CrmReceivableUpdateReqVO 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 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 getReceivable(@RequestParam("id") Long id) { + CrmReceivableDO receivable = receivableService.getReceivable(id); + return success(CrmReceivableConvert.INSTANCE.convert(receivable)); + } + + @GetMapping("/page") + @Operation(summary = "获得回款分页") + @PreAuthorize("@ss.hasPermission('crm:receivable:query')") + public CommonResult> getReceivablePage(@Valid CrmReceivablePageReqVO pageReqVO) { + PageResult pageResult = receivableService.getReceivablePage(pageReqVO, getLoginUserId()); + return success(buildReceivableDetailPage(pageResult)); + } + + @GetMapping("/page-by-customer") + @Operation(summary = "获得回款分页,基于指定客户") + public CommonResult> getReceivablePageByCustomer(@Valid CrmReceivablePageReqVO pageReqVO) { + Assert.notNull(pageReqVO.getCustomerId(), "客户编å·ä¸èƒ½ä¸ºç©º"); + PageResult pageResult = receivableService.getReceivablePageByCustomerId(pageReqVO); + return success(buildReceivableDetailPage(pageResult)); + } + + // TODO 芋艿:åŽé¢åœ¨ä¼˜åŒ–导出 + @GetMapping("/export-excel") + @Operation(summary = "导出回款 Excel") + @PreAuthorize("@ss.hasPermission('crm:receivable:export')") + @OperateLog(type = EXPORT) + public void exportReceivableExcel(@Valid CrmReceivablePageReqVO exportReqVO, + HttpServletResponse response) throws IOException { + exportReqVO.setPageSize(PAGE_SIZE_NONE); + PageResult pageResult = receivableService.getReceivablePage(exportReqVO, getLoginUserId()); + // 导出 Excel + ExcelUtils.write(response, "回款.xls", "æ•°æ®", CrmReceivableRespVO.class, + buildReceivableDetailPage(pageResult).getList()); + } + + /** + * 构建详细的回款分页结果 + * + * @param pageResult 简å•çš„回款分页结果 + * @return 详细的回款分页结果 + */ + private PageResult buildReceivableDetailPage(PageResult pageResult) { + List receivableList = pageResult.getList(); + if (CollUtil.isEmpty(receivableList)) { + return PageResult.empty(pageResult.getTotal()); + } + // 1. 获å–客户列表 + List customerList = customerService.getCustomerList( + convertSet(receivableList, CrmReceivableDO::getCustomerId)); + // 2. 获å–创建人ã€è´Ÿè´£äººåˆ—表 + Map userMap = adminUserApi.getUserMap(convertListByFlatMap(receivableList, + contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId()))); + // 3. 获得åˆåŒåˆ—表 + List contractList = contractService.getContractList( + convertSet(receivableList, CrmReceivableDO::getContractId)); + return CrmReceivableConvert.INSTANCE.convertPage(pageResult, userMap, customerList, contractList); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java new file mode 100644 index 000000000..252d714f4 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java @@ -0,0 +1,156 @@ +package cn.iocoder.yudao.module.crm.controller.admin.receivable; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; +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.plan.CrmReceivablePlanCreateReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanUpdateReqVO; +import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivablePlanConvert; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO; +import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; +import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; +import cn.iocoder.yudao.module.crm.service.receivable.CrmReceivablePlanService; +import cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableService; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.context.annotation.Lazy; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +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/receivable-plan") +@Validated +public class CrmReceivablePlanController { + + @Resource + private CrmReceivablePlanService receivablePlanService; + @Resource + private CrmReceivableService receivableService; + @Resource + @Lazy + private CrmContractService contractService; + @Resource + private CrmCustomerService customerService; + + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建回款计划") + @PreAuthorize("@ss.hasPermission('crm:receivable-plan:create')") + public CommonResult createReceivablePlan(@Valid @RequestBody CrmReceivablePlanCreateReqVO createReqVO) { + return success(receivablePlanService.createReceivablePlan(createReqVO, getLoginUserId())); + } + + @PutMapping("/update") + @Operation(summary = "更新回款计划") + @PreAuthorize("@ss.hasPermission('crm:receivable-plan:update')") + public CommonResult updateReceivablePlan(@Valid @RequestBody CrmReceivablePlanUpdateReqVO 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 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 getReceivablePlan(@RequestParam("id") Long id) { + CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(id); + return success(CrmReceivablePlanConvert.INSTANCE.convert(receivablePlan)); + } + + @GetMapping("/page") + @Operation(summary = "获得回款计划分页") + @PreAuthorize("@ss.hasPermission('crm:receivable-plan:query')") + public CommonResult> getReceivablePlanPage(@Valid CrmReceivablePlanPageReqVO pageReqVO) { + PageResult pageResult = receivablePlanService.getReceivablePlanPage(pageReqVO, getLoginUserId()); + return success(convertDetailReceivablePlanPage(pageResult)); + } + + @GetMapping("/page-by-customer") + @Operation(summary = "获得回款计划分页,基于指定客户") + public CommonResult> getReceivablePlanPageByCustomer(@Valid CrmReceivablePlanPageReqVO pageReqVO) { + Assert.notNull(pageReqVO.getCustomerId(), "客户编å·ä¸èƒ½ä¸ºç©º"); + PageResult pageResult = receivablePlanService.getReceivablePlanPageByCustomerId(pageReqVO); + return success(convertDetailReceivablePlanPage(pageResult)); + } + + // TODO 芋艿:åŽé¢åœ¨ä¼˜åŒ–导出 + @GetMapping("/export-excel") + @Operation(summary = "导出回款计划 Excel") + @PreAuthorize("@ss.hasPermission('crm:receivable-plan:export')") + @OperateLog(type = EXPORT) + public void exportReceivablePlanExcel(@Valid CrmReceivablePlanPageReqVO exportReqVO, + HttpServletResponse response) throws IOException { + exportReqVO.setPageSize(PAGE_SIZE_NONE); + PageResult pageResult = receivablePlanService.getReceivablePlanPage(exportReqVO, getLoginUserId()); + // 导出 Excel + ExcelUtils.write(response, "回款计划.xls", "æ•°æ®", CrmReceivablePlanRespVO.class, + convertDetailReceivablePlanPage(pageResult).getList()); + } + + /** + * 构建详细的回款计划分页结果 + * + * @param pageResult 简å•çš„回款计划分页结果 + * @return 详细的回款计划分页结果 + */ + private PageResult convertDetailReceivablePlanPage(PageResult pageResult) { + List receivablePlanList = pageResult.getList(); + if (CollUtil.isEmpty(receivablePlanList)) { + return PageResult.empty(pageResult.getTotal()); + } + // 1. 获å–客户列表 + List customerList = customerService.getCustomerList( + convertSet(receivablePlanList, CrmReceivablePlanDO::getCustomerId)); + // 2. 获å–创建人ã€è´Ÿè´£äººåˆ—表 + Map userMap = adminUserApi.getUserMap(convertListByFlatMap(receivablePlanList, + contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId()))); + // 3. 获得åˆåŒåˆ—表 + List contractList = contractService.getContractList( + convertSet(receivablePlanList, CrmReceivablePlanDO::getContractId)); + // 4. 获得还款列表 + List receivableList = receivableService.getReceivableList( + convertSet(receivablePlanList, CrmReceivablePlanDO::getReceivableId)); + return CrmReceivablePlanConvert.INSTANCE.convertPage(pageResult, userMap, customerList, contractList, receivableList); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanBaseVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanBaseVO.java new file mode 100644 index 000000000..70272b8e8 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanBaseVO.java @@ -0,0 +1,54 @@ +package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan; + +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; + +/** + * 回款计划 Base VO,æ供给添加ã€ä¿®æ”¹ã€è¯¦ç»†çš„å­ VO 使用 + * å¦‚æžœå­ VO 存在差异的字段,请ä¸è¦æ·»åŠ åˆ°è¿™é‡Œï¼Œå½±å“ Swagger æ–‡æ¡£ç”Ÿæˆ + */ +@Data +public class CrmReceivablePlanBaseVO { + + @Schema(description = "期数", example = "1") + private Integer period; + + @Schema(description = "回款计划编å·", example = "19852") + private Long receivableId; + + @Schema(description = "计划回款金é¢", example = "29675") + private Integer price; + + @Schema(description = "计划回款日期") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime returnTime; + + @Schema(description = "æå‰å‡ å¤©æ醒") + private Integer remindDays; + + @Schema(description = "æ醒日期") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime remindTime; + + @Schema(description = "客户å称", example = "18026") + private Long customerId; + + @Schema(description = "åˆåŒç¼–å·", example = "3473") + private Long contractId; + + // TODO @liuhongfengï¼šè´Ÿè´£äººç¼–å· + @Schema(description = "负责人编å·", example = "17828") + private Long ownerUserId; + + @Schema(description = "显示顺åº") + private Integer sort; + + @Schema(description = "备注", example = "备注") + private String remark; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanCreateReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanCreateReqVO.java new file mode 100644 index 000000000..193a44bf4 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanCreateReqVO.java @@ -0,0 +1,12 @@ +package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan; + +import lombok.*; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(description = "管ç†åŽå° - CRM 回款计划创建 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CrmReceivablePlanCreateReqVO extends CrmReceivablePlanBaseVO { + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanPageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanPageReqVO.java new file mode 100644 index 000000000..3675fba1f --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanPageReqVO.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum; +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 CrmReceivablePlanPageReqVO extends PageParam { + + /** + * æ醒类型 - 待回款 + */ + public final static Integer REMIND_TYPE_NEEDED = 1; + /** + * æ醒类型 - 已逾期 + */ + public final static Integer REMIND_TYPE_EXPIRED = 2; + /** + * æ醒类型 - 已回款 + */ + public final static Integer REMIND_TYPE_RECEIVED = 3; + + @Schema(description = "客户编å·", example = "18026") + private Long customerId; + + // TODO @芋艿:这个æœçš„应该是åˆåŒç¼–å· no + @Schema(description = "åˆåŒå称", example = "3473") + private Long contractId; + + @Schema(description = "场景类型", example = "1") + @InEnum(CrmSceneTypeEnum.class) + private Integer sceneType; // 场景类型,为 null 时则表示全部 + + @Schema(description = "æ醒类型", example = "1") + private Integer remindType; // æ醒类型,为 null 时则表示全部 + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java new file mode 100644 index 000000000..d5e9de187 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan; + +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 CrmReceivablePlanRespVO extends CrmReceivablePlanBaseVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "25153") + private Long id; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "客户åå­—", requiredMode = Schema.RequiredMode.REQUIRED, example = "test") + private String customerName; + + @Schema(description = "åˆåŒç¼–å·", example = "Q110") + private String contractNo; + + @Schema(description = "负责人", example = "test") + private String ownerUserName; + + @Schema(description = "创建人", example = "25682") + private String creator; + + @Schema(description = "创建人åå­—", example = "test") + private String creatorName; + + @Schema(description = "完æˆçŠ¶æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Boolean finishStatus; + + @Schema(description = "回款方å¼", example = "1") // æ¥è‡ª Receivable çš„ returnType 字段 + private Integer returnType; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanUpdateReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanUpdateReqVO.java new file mode 100644 index 000000000..2e83a1cbb --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanUpdateReqVO.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +import jakarta.validation.constraints.*; + +@Schema(description = "管ç†åŽå° - CRM 回款计划更新 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CrmReceivablePlanUpdateReqVO extends CrmReceivablePlanBaseVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "25153") + @NotNull(message = "IDä¸èƒ½ä¸ºç©º") + private Long id; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableBaseVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableBaseVO.java new file mode 100644 index 000000000..c32bc9ad5 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableBaseVO.java @@ -0,0 +1,61 @@ +package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum; +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; + +/** + * 回款 Base VO,æ供给添加ã€ä¿®æ”¹ã€è¯¦ç»†çš„å­ VO 使用 + * å¦‚æžœå­ VO 存在差异的字段,请ä¸è¦æ·»åŠ åˆ°è¿™é‡Œï¼Œå½±å“ Swagger æ–‡æ¡£ç”Ÿæˆ + */ +@Data +public class CrmReceivableBaseVO { + + @Schema(description = "回款编å·",requiredMode = Schema.RequiredMode.REQUIRED, example = "31177") + private String no; + + // TODO @liuhongfengï¼šå›žæ¬¾è®¡åˆ’ç¼–å· + @Schema(description = "回款计划", example = "31177") + private Long planId; + + // TODO @liuhongfengï¼šå®¢æˆ·ç¼–å· + @Schema(description = "客户å称", example = "4963") + private Long customerId; + + // TODO @liuhongfengï¼šå®¢æˆ·ç¼–å· + @Schema(description = "åˆåŒå称", example = "30305") + private Long contractId; + + // TODO @liuhongfeng:这个字段,应该ä¸æ˜¯å‰ç«¯ä¼ é€’的噢,而是åŽç«¯è‡ªå·±ç”Ÿæˆçš„ + @Schema(description = "审批状æ€", example = "1") + @InEnum(CrmAuditStatusEnum.class) + private Integer checkStatus; + + @Schema(description = "回款日期") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime returnTime; + + @Schema(description = "回款方å¼", example = "2") + private Integer returnType; + + @Schema(description = "回款金é¢ï¼Œå•ä½ï¼šåˆ†", example = "31859") + private Integer price; + + // TODO @liuhongfengï¼šè´Ÿè´£äººç¼–å· + @Schema(description = "负责人", example = "22202") + private Long ownerUserId; + + @Schema(description = "显示顺åº") + private Integer sort; + + @Schema(description = "备注", example = "备注") + private String remark; + + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableCreateReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableCreateReqVO.java new file mode 100644 index 000000000..4471b780a --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableCreateReqVO.java @@ -0,0 +1,12 @@ +package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable; + +import lombok.*; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(description = "管ç†åŽå° - CRM 回款创建 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CrmReceivableCreateReqVO extends CrmReceivableBaseVO { + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivablePageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivablePageReqVO.java new file mode 100644 index 000000000..e1fe83087 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivablePageReqVO.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum; +import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum; +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 CrmReceivablePageReqVO extends PageParam { + + @Schema(description = "回款编å·") + private String no; + + @Schema(description = "回款计划编å·", example = "31177") + private Long planId; + + @Schema(description = "客户编å·", example = "4963") + private Long customerId; + + @Schema(description = "场景类型", example = "1") + @InEnum(CrmSceneTypeEnum.class) + private Integer sceneType; // 场景类型,为 null 时则表示全部 + + @Schema(description = "审批状æ€", example = "20") + @InEnum(CrmAuditStatusEnum.class) + private Integer auditStatus; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableRespVO.java new file mode 100644 index 000000000..7c536bd51 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableRespVO.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.time.LocalDateTime; + +// TODO 芋艿:导出的 VO,å¯ä»¥è€ƒè™‘使用 @Excel 注解,实现导出功能 +@Schema(description = "管ç†åŽå° - CRM 回款 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CrmReceivableRespVO extends CrmReceivableBaseVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "25787") + private Long id; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "客户åå­—", requiredMode = Schema.RequiredMode.REQUIRED, example = "test") + private String customerName; + + @Schema(description = "审批状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + private Integer auditStatus; + + @Schema(description = "åˆåŒç¼–å·", example = "Q110") + private String contractNo; + + @Schema(description = "负责人", example = "test") + private String ownerUserName; + + @Schema(description = "创建人", example = "25682") + private String creator; + + @Schema(description = "创建人åå­—", example = "test") + private String creatorName; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableUpdateReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableUpdateReqVO.java new file mode 100644 index 000000000..0f63978c8 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableUpdateReqVO.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +import jakarta.validation.constraints.*; + +@Schema(description = "管ç†åŽå° - CRM 回款更新 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CrmReceivableUpdateReqVO extends CrmReceivableBaseVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "25787") + @NotNull(message = "IDä¸èƒ½ä¸ºç©º") + private Long id; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/app/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/app/package-info.java new file mode 100644 index 000000000..78d85635c --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/app/package-info.java @@ -0,0 +1,4 @@ +/** + * å ä½ + */ +package cn.iocoder.yudao.module.crm.controller.app; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/package-info.java new file mode 100644 index 000000000..8354b3176 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/package-info.java @@ -0,0 +1,6 @@ +/** + * æä¾› RESTful API ç»™å‰ç«¯ï¼š + * 1. admin 包:æ供给管ç†åŽå° yudao-ui-admin å‰ç«¯é¡¹ç›® + * 2. app 包:æ供给用户 APP yudao-ui-app å‰ç«¯é¡¹ç›®ï¼Œå®ƒçš„ Controller å’Œ VO 都è¦æ·»åŠ  App å‰ç¼€ï¼Œç”¨äºŽå’Œç®¡ç†åŽå°è¿›è¡ŒåŒºåˆ† + */ +package cn.iocoder.yudao.module.crm.controller; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessConvert.java new file mode 100644 index 000000000..d7f990043 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessConvert.java @@ -0,0 +1,57 @@ +package cn.iocoder.yudao.module.crm.convert.business; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; + +/** + * 商机 Convert + * + * @author ljlleo + */ +@Mapper +public interface CrmBusinessConvert { + + CrmBusinessConvert INSTANCE = Mappers.getMapper(CrmBusinessConvert.class); + + @Mapping(target = "bizId", source = "reqVO.id") + CrmPermissionTransferReqBO convert(CrmBusinessTransferReqVO reqVO, Long userId); + + default PageResult convertPage(PageResult pageResult, List customerList, + List statusTypeList, List statusList) { + PageResult voPageResult = BeanUtils.toBean(pageResult, CrmBusinessRespVO.class); + // 拼接关è”字段 + Map customerMap = convertMap(customerList, CrmCustomerDO::getId, CrmCustomerDO::getName); + Map statusTypeMap = convertMap(statusTypeList, CrmBusinessStatusTypeDO::getId, CrmBusinessStatusTypeDO::getName); + Map statusMap = convertMap(statusList, CrmBusinessStatusDO::getId, CrmBusinessStatusDO::getName); + voPageResult.getList().forEach(type -> type + .setCustomerName(customerMap.get(type.getCustomerId())) + .setStatusTypeName(statusTypeMap.get(type.getStatusTypeId())) + .setStatusName(statusMap.get(type.getStatusId()))); + return voPageResult; + } + + @Mapping(target = "id", source = "reqBO.bizId") + CrmBusinessDO convert(CrmUpdateFollowUpReqBO reqBO); + + default List convertList(List list) { + return CollectionUtils.convertList(list, INSTANCE::convert); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessStatusConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessStatusConvert.java new file mode 100644 index 000000000..52186e3d9 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessStatusConvert.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.crm.convert.business; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusRespVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * å•†æœºçŠ¶æ€ Convert + * + * @author ljlleo + */ +@Mapper +public interface CrmBusinessStatusConvert { + + CrmBusinessStatusConvert INSTANCE = Mappers.getMapper(CrmBusinessStatusConvert.class); + + List convertList(List list); + + PageResult convertPage(PageResult page); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessStatusTypeConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessStatusTypeConvert.java new file mode 100644 index 000000000..4876fb537 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessStatusTypeConvert.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.crm.convert.business; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeRespVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO; +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; + +// TODO @lzxhqs:看看是ä¸æ˜¯ç”¨ BeanUtils 替代了 +/** + * 商机状æ€ç±»åž‹ Convert + * + * @author ljlleo + */ +@Mapper +public interface CrmBusinessStatusTypeConvert { + + CrmBusinessStatusTypeConvert INSTANCE = Mappers.getMapper(CrmBusinessStatusTypeConvert.class); + + CrmBusinessStatusTypeRespVO convert(CrmBusinessStatusTypeDO bean); + + PageResult convertPage(PageResult page); + + default PageResult convertPage(PageResult page, List deptList) { + PageResult pageResult = convertPage(page); + // 拼接关è”字段 + Map deptMap = convertMap(deptList, DeptRespDTO::getId, DeptRespDTO::getName); + pageResult.getList().forEach(type -> type.setDeptNames(convertList(type.getDeptIds(), deptMap::get))); + return pageResult; + } + + default CrmBusinessStatusTypeRespVO convert(CrmBusinessStatusTypeDO bean, List statusList) { + return convert(bean).setStatusList(statusList); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/clue/CrmClueConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/clue/CrmClueConvert.java new file mode 100644 index 000000000..39e607bcb --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/clue/CrmClueConvert.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.crm.convert.clue; + +import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransferReqVO; +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * 线索 Convert + * + * @author Wanwan + */ +@Mapper +public interface CrmClueConvert { + + CrmClueConvert INSTANCE = Mappers.getMapper(CrmClueConvert.class); + + @Mapping(target = "bizId", source = "reqVO.id") + CrmPermissionTransferReqBO convert(CrmClueTransferReqVO reqVO, Long userId); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contact/CrmContactConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contact/CrmContactConvert.java new file mode 100644 index 000000000..5fd2afa67 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contact/CrmContactConvert.java @@ -0,0 +1,77 @@ +package cn.iocoder.yudao.module.crm.convert.contact; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; +import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactTransferReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; + +/** + * CRM è”系人 Convert + * + * @author 芋é“æºç  + */ +@Mapper +public interface CrmContactConvert { + + CrmContactConvert INSTANCE = Mappers.getMapper(CrmContactConvert.class); + + CrmContactRespVO convert(CrmContactDO bean); + + @Mapping(target = "bizId", source = "reqVO.id") + CrmPermissionTransferReqBO convert(CrmContactTransferReqVO reqVO, Long userId); + + default PageResult convertPage(PageResult pageResult, Map userMap, + List customerList, List parentContactList) { + PageResult voPageResult = BeanUtils.toBean(pageResult, CrmContactRespVO.class); + // 拼接关è”字段 + Map parentContactMap = convertMap(parentContactList, CrmContactDO::getId); + Map customerMap = convertMap(customerList, CrmCustomerDO::getId); + voPageResult.getList().forEach(item -> { + setUserInfo(item, userMap); + findAndThen(customerMap, item.getCustomerId(), customer -> item.setCustomerName(customer.getName())); + findAndThen(parentContactMap, item.getParentId(), contactDO -> item.setParentName(contactDO.getName())); + }); + return voPageResult; + } + + default CrmContactRespVO convert(CrmContactDO contactDO, Map userMap, + List customerList, List parentContactList) { + CrmContactRespVO contactVO = convert(contactDO); + setUserInfo(contactVO, userMap); + Map customerMap = CollectionUtils.convertMap(customerList, CrmCustomerDO::getId); + Map contactMap = CollectionUtils.convertMap(parentContactList, CrmContactDO::getId); + findAndThen(customerMap, contactDO.getCustomerId(), customer -> contactVO.setCustomerName(customer.getName())); + findAndThen(contactMap, contactDO.getParentId(), contact -> contactVO.setParentName(contact.getName())); + return contactVO; + } + + static void setUserInfo(CrmContactRespVO contactRespVO, Map userMap) { + contactRespVO.setAreaName(AreaUtils.format(contactRespVO.getAreaId())); + findAndThen(userMap, contactRespVO.getOwnerUserId(), user -> contactRespVO.setOwnerUserName(user.getNickname())); + findAndThen(userMap, Long.parseLong(contactRespVO.getCreator()), user -> contactRespVO.setCreatorName(user.getNickname())); + } + + @Mapping(target = "id", source = "reqBO.bizId") + CrmContactDO convert(CrmUpdateFollowUpReqBO reqBO); + + default List convertList(List list) { + return CollectionUtils.convertList(list, INSTANCE::convert); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/CrmContractConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/CrmContractConvert.java new file mode 100644 index 000000000..0d2e49934 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/CrmContractConvert.java @@ -0,0 +1,70 @@ +package cn.iocoder.yudao.module.crm.convert.contract; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractProductDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; + +/** + * åˆåŒ Convert + * + * @author dhb52 + */ +@Mapper +public interface CrmContractConvert { + + CrmContractConvert INSTANCE = Mappers.getMapper(CrmContractConvert.class); + + @Mapping(target = "bizId", source = "reqVO.id") + CrmPermissionTransferReqBO convert(CrmContractTransferReqVO reqVO, Long userId); + + default List convertList(List contractList, Map userMap, + List customerList, Map contactMap, + Map businessMap, Map contractProductMap, + List productList) { + List respVOList = BeanUtils.toBean(contractList, CrmContractRespVO.class); + // 拼接关è”字段 + Map customerMap = convertMap(customerList, CrmCustomerDO::getId); + respVOList.forEach(contract -> { + findAndThen(userMap, contract.getOwnerUserId(), user -> contract.setOwnerUserName(user.getNickname())); + findAndThen(userMap, Long.parseLong(contract.getCreator()), user -> contract.setCreatorName(user.getNickname())); + findAndThen(userMap, contract.getSignUserId(), user -> contract.setSignUserName(user.getNickname())); + findAndThen(customerMap, contract.getCustomerId(), customer -> contract.setCustomerName(customer.getName())); + findAndThen(contactMap, contract.getContactId(), contact -> contract.setContactName(contact.getName())); + findAndThen(businessMap, contract.getBusinessId(), business -> contract.setBusinessName(business.getName())); + }); + if (CollUtil.isNotEmpty(respVOList) && respVOList.size() == 1) { + setContractRespVOProductItems(respVOList.get(0), contractProductMap, productList); + } + return respVOList; + } + + default void setContractRespVOProductItems(CrmContractRespVO respVO, Map contractProductMap, + List productList) { + respVO.setProductItems(CollectionUtils.convertList(productList, product -> { + CrmContractRespVO.CrmContractProductItemRespVO productItemRespVO = BeanUtils.toBean(product, CrmContractRespVO.CrmContractProductItemRespVO.class); + findAndThen(contractProductMap, product.getId(), contractProduct -> + productItemRespVO.setCount(contractProduct.getCount()).setDiscountPercent(contractProduct.getDiscountPercent())); + return productItemRespVO; + })); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/customer/CrmCustomerConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/customer/CrmCustomerConvert.java new file mode 100644 index 000000000..6a6642968 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/customer/CrmCustomerConvert.java @@ -0,0 +1,66 @@ +package cn.iocoder.yudao.module.crm.convert.customer; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerTransferReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO; +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; + +/** + * 客户 Convert + * + * @author Wanwan + */ +@Mapper +public interface CrmCustomerConvert { + + CrmCustomerConvert INSTANCE = Mappers.getMapper(CrmCustomerConvert.class); + + default CrmCustomerRespVO convert(CrmCustomerDO customer, Map userMap, + Map deptMap) { + CrmCustomerRespVO customerResp = BeanUtils.toBean(customer, CrmCustomerRespVO.class); + setUserInfo(customerResp, userMap, deptMap); + return customerResp; + } + + default PageResult convertPage(PageResult pageResult, Map userMap, + Map deptMap, Map poolDayMap) { + PageResult result = BeanUtils.toBean(pageResult, CrmCustomerRespVO.class); + result.getList().forEach(item -> { + setUserInfo(item, userMap, deptMap); + findAndThen(poolDayMap, item.getId(), item::setPoolDay); + }); + return result; + } + + /** + * è®¾ç½®ç”¨æˆ·ä¿¡æ¯ + * + * @param customer CRM 客户 Response VO + * @param userMap ç”¨æˆ·ä¿¡æ¯ map + * @param deptMap ç”¨æˆ·éƒ¨é—¨ä¿¡æ¯ map + */ + static void setUserInfo(CrmCustomerRespVO customer, Map userMap, Map deptMap) { + customer.setAreaName(AreaUtils.format(customer.getAreaId())); + findAndThen(userMap, customer.getOwnerUserId(), user -> { + customer.setOwnerUserName(user.getNickname()); + findAndThen(deptMap, user.getDeptId(), dept -> customer.setOwnerUserDeptName(dept.getName())); + }); + findAndThen(userMap, Long.parseLong(customer.getCreator()), user -> customer.setCreatorName(user.getNickname())); + } + + @Mapping(target = "bizId", source = "reqVO.id") + CrmPermissionTransferReqBO convert(CrmCustomerTransferReqVO reqVO, Long userId); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/customer/CrmCustomerLimitConfigConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/customer/CrmCustomerLimitConfigConvert.java new file mode 100644 index 000000000..81550b378 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/customer/CrmCustomerLimitConfigConvert.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.crm.convert.customer; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigRespVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO; +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; + +/** + * 客户é™åˆ¶é…ç½® Convert + * + * @author Wanwan + */ +@Mapper +public interface CrmCustomerLimitConfigConvert { + + CrmCustomerLimitConfigConvert INSTANCE = Mappers.getMapper(CrmCustomerLimitConfigConvert.class); + + default PageResult convertPage( + PageResult pageResult, + Map userMap, Map deptMap) { + List list = CollectionUtils.convertList(pageResult.getList(), + limitConfig -> convert(limitConfig, userMap, deptMap)); + return new PageResult<>(list, pageResult.getTotal()); + } + + default CrmCustomerLimitConfigRespVO convert(CrmCustomerLimitConfigDO limitConfig, + Map userMap, Map deptMap) { + CrmCustomerLimitConfigRespVO limitConfigVO = BeanUtils.toBean(limitConfig, CrmCustomerLimitConfigRespVO.class); + limitConfigVO.setUsers(CollectionUtils.convertList(limitConfigVO.getUserIds(), userMap::get)); + limitConfigVO.setDepts(CollectionUtils.convertList(limitConfigVO.getDeptIds(), deptMap::get)); + return limitConfigVO; + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/package-info.java new file mode 100644 index 000000000..6fbc52508 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/package-info.java @@ -0,0 +1,6 @@ +/** + * æä¾› POJO ç±»çš„å®žä½“è½¬æ¢ + * + * ç›®å‰ä½¿ç”¨ MapStruct 框架 + */ +package cn.iocoder.yudao.module.crm.convert; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/permission/CrmPermissionConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/permission/CrmPermissionConvert.java new file mode 100644 index 000000000..f51544cae --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/permission/CrmPermissionConvert.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.crm.convert.permission; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; +import cn.iocoder.yudao.module.system.api.dept.dto.PostRespDTO; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import com.google.common.collect.Multimaps; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; + +/** + * Crm æ•°æ®æƒé™ Convert + * + * @author HUIHUI + */ +@Mapper +public interface CrmPermissionConvert { + + CrmPermissionConvert INSTANCE = Mappers.getMapper(CrmPermissionConvert.class); + + default List convert(List permissions, List userList, + Map deptMap, Map postMap) { + Map userMap = CollectionUtils.convertMap(userList, AdminUserRespDTO::getId); + return CollectionUtils.convertList(BeanUtils.toBean(permissions, CrmPermissionRespVO.class), item -> { + findAndThen(userMap, item.getUserId(), user -> { + item.setNickname(user.getNickname()); + findAndThen(deptMap, user.getDeptId(), deptRespDTO -> item.setDeptName(deptRespDTO.getName())); + if (CollUtil.isEmpty(user.getPostIds())) { + item.setPostNames(Collections.emptySet()); + return; + } + List postList = MapUtils.getList(Multimaps.forMap(postMap), user.getPostIds()); + item.setPostNames(CollectionUtils.convertSet(postList, PostRespDTO::getName)); + }); + return item; + }); + } + + default List convertList(CrmPermissionUpdateReqVO updateReqVO) { + return CollectionUtils.convertList(updateReqVO.getIds(), + id -> new CrmPermissionDO().setId(id).setLevel(updateReqVO.getLevel())); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/product/CrmProductConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/product/CrmProductConvert.java new file mode 100644 index 000000000..2165a208f --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/product/CrmProductConvert.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.crm.convert.product; + +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductRespVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; + +/** + * äº§å“ Convert + * + * @author ZanGe丶 + */ +@Mapper +public interface CrmProductConvert { + + CrmProductConvert INSTANCE = Mappers.getMapper(CrmProductConvert.class); + + default List convertList(List list, + Map userMap, + List categoryList) { + Map categoryMap = convertMap(categoryList, CrmProductCategoryDO::getId); + return CollectionUtils.convertList(list, + product -> convert(product, userMap, categoryMap.get(product.getCategoryId()))); + } + + default CrmProductRespVO convert(CrmProductDO product, + Map userMap, CrmProductCategoryDO category) { + CrmProductRespVO productVO = BeanUtils.toBean(product, CrmProductRespVO.class); + Optional.ofNullable(category).ifPresent(c -> productVO.setCategoryName(c.getName())); + MapUtils.findAndThen(userMap, productVO.getOwnerUserId(), user -> productVO.setOwnerUserName(user.getNickname())); + MapUtils.findAndThen(userMap, Long.valueOf(productVO.getCreator()), user -> productVO.setCreatorName(user.getNickname())); + return productVO; + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivableConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivableConvert.java new file mode 100644 index 000000000..3b0c23aae --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivableConvert.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.crm.convert.receivable; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableCreateReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableUpdateReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; + +/** + * 回款 Convert + * + * @author 赤焰 + */ +@Mapper +public interface CrmReceivableConvert { + + CrmReceivableConvert INSTANCE = Mappers.getMapper(CrmReceivableConvert.class); + + CrmReceivableDO convert(CrmReceivableCreateReqVO bean); + + CrmReceivableDO convert(CrmReceivableUpdateReqVO bean); + + CrmReceivableRespVO convert(CrmReceivableDO bean); + + default PageResult convertPage(PageResult pageResult, Map userMap, + List customerList, List contractList) { + PageResult voPageResult = BeanUtils.toBean(pageResult, CrmReceivableRespVO.class); + // 拼接关è”字段 + Map customerMap = convertMap(customerList, CrmCustomerDO::getId); + Map contractMap = convertMap(contractList, CrmContractDO::getId); + voPageResult.getList().forEach(receivable -> { + findAndThen(userMap, receivable.getOwnerUserId(), user -> receivable.setOwnerUserName(user.getNickname())); + findAndThen(userMap, Long.parseLong(receivable.getCreator()), user -> receivable.setCreatorName(user.getNickname())); + findAndThen(customerMap, receivable.getCustomerId(), customer -> receivable.setCustomerName(customer.getName())); + findAndThen(contractMap, receivable.getContractId(), contract -> receivable.setContractNo(contract.getNo())); + }); + return voPageResult; + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivablePlanConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivablePlanConvert.java new file mode 100644 index 000000000..9b6bb3e82 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivablePlanConvert.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.crm.convert.receivable; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanCreateReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanUpdateReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; + +/** + * 回款计划 Convert + * + * @author 芋é“æºç  + */ +@Mapper +public interface CrmReceivablePlanConvert { + + CrmReceivablePlanConvert INSTANCE = Mappers.getMapper(CrmReceivablePlanConvert.class); + + CrmReceivablePlanDO convert(CrmReceivablePlanCreateReqVO bean); + + CrmReceivablePlanDO convert(CrmReceivablePlanUpdateReqVO bean); + + CrmReceivablePlanRespVO convert(CrmReceivablePlanDO bean); + + default PageResult convertPage(PageResult pageResult, Map userMap, + List customerList, List contractList, + List receivableList) { + PageResult voPageResult = BeanUtils.toBean(pageResult, CrmReceivablePlanRespVO.class); + // 拼接关è”字段 + Map customerMap = convertMap(customerList, CrmCustomerDO::getId); + Map contractMap = convertMap(contractList, CrmContractDO::getId); + Map receivableMap = convertMap(receivableList, CrmReceivableDO::getId); + voPageResult.getList().forEach(receivablePlan -> { + findAndThen(userMap, receivablePlan.getOwnerUserId(), user -> receivablePlan.setOwnerUserName(user.getNickname())); + findAndThen(userMap, Long.parseLong(receivablePlan.getCreator()), user -> receivablePlan.setCreatorName(user.getNickname())); + findAndThen(customerMap, receivablePlan.getCustomerId(), customer -> receivablePlan.setCustomerName(customer.getName())); + findAndThen(contractMap, receivablePlan.getContractId(), contract -> receivablePlan.setContractNo(contract.getNo())); + findAndThen(receivableMap, receivablePlan.getReceivableId(), receivable -> receivablePlan.setReturnType(receivable.getReturnType())); + }); + return voPageResult; + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/ã€ŠèŠ‹é“ Spring Boot å¯¹è±¡è½¬æ¢ MapStruct 入门》.md b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/ã€ŠèŠ‹é“ Spring Boot å¯¹è±¡è½¬æ¢ MapStruct 入门》.md new file mode 100644 index 000000000..8153487b7 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/ã€ŠèŠ‹é“ Spring Boot å¯¹è±¡è½¬æ¢ MapStruct 入门》.md @@ -0,0 +1 @@ + diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessDO.java new file mode 100644 index 000000000..3a543712b --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessDO.java @@ -0,0 +1,108 @@ +package cn.iocoder.yudao.module.crm.dal.dataobject.business; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.enums.business.CrmBizEndStatus; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.time.LocalDateTime; + +/** + * 商机 DO + * + * @author ljlleo + */ +@TableName("crm_business") +@KeySequence("crm_business_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CrmBusinessDO extends BaseDO { + + /** + * 主键 + */ + @TableId + private Long id; + /** + * 商机å称 + */ + private String name; + /** + * 商机状æ€ç±»åž‹ç¼–å· + * + * å…³è” {@link CrmBusinessStatusTypeDO#getId()} + */ + private Long statusTypeId; + /** + * 商机状æ€ç¼–å· + * + * å…³è” {@link CrmBusinessStatusDO#getId()} + */ + private Long statusId; + /** + * 下次è”系时间 + */ + private LocalDateTime contactNextTime; + /** + * å®¢æˆ·ç¼–å· + * + * TODO @ljileo:这个字段,åŽç»­è¦å†™ä¸‹å…³è”的实体哈 + * å…³è” {@link CrmCustomerDO#getId()} + */ + private Long customerId; + /** + * 预计æˆäº¤æ—¥æœŸ + */ + private LocalDateTime dealTime; + /** + * å•†æœºé‡‘é¢ + * + */ + private Integer price; + /** + * æ•´å•æŠ˜æ‰£ + * + */ + private Integer discountPercent; + /** + * 产å“总金é¢ï¼Œå•ä½ï¼šåˆ† + */ + private Integer productPrice; + /** + * 备注 + */ + private String remark; + /** + * 结æŸçŠ¶æ€ + * + * 枚举 {@link CrmBizEndStatus} + */ + private Integer endStatus; + /** + * 结æŸæ—¶çš„备注 + */ + private String endRemark; + /** + * 最åŽè·Ÿè¿›æ—¶é—´ + */ + private LocalDateTime contactLastTime; + /** + * è·Ÿè¿›çŠ¶æ€ + */ + private Boolean followUpStatus; + + /** + * è´Ÿè´£äººçš„ç”¨æˆ·ç¼–å· + * + * å…³è” AdminUserDO çš„ id 字段 + */ + private Long ownerUserId; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessProductDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessProductDO.java new file mode 100644 index 000000000..79d6a2a7b --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessProductDO.java @@ -0,0 +1,63 @@ +package cn.iocoder.yudao.module.crm.dal.dataobject.business; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 商机产å“å…³è”表 DO + * + * @author lzxhqs + */ +@TableName("crm_business_product") +@KeySequence("crm_business_product_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CrmBusinessProductDO extends BaseDO { + + /** + * 主键 + */ + @TableId + private Long id; + /** + * å•†æœºç¼–å· + * + * å…³è” {@link CrmBusinessDO#getId()} + */ + private Long businessId; + /** + * 产å“ç¼–å· + * + * å…³è” {@link CrmProductDO#getId()} + */ + private Long productId; + /** + * 产å“å•ä»· + */ + private Integer price; + /** + * 销售价格, å•ä½ï¼šåˆ† + */ + private Integer salesPrice; + /** + * æ•°é‡ + */ + private Integer count; + /** + * 折扣 + */ + private Integer discountPercent; + /** + * 总计价格(折扣åŽä»·æ ¼ï¼‰ + */ + private Integer totalPrice; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessStatusDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessStatusDO.java new file mode 100644 index 000000000..17ce4f88c --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessStatusDO.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.crm.dal.dataobject.business; + +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * å•†æœºçŠ¶æ€ DO + * + * @author ljlleo + */ +@TableName("crm_business_status") +@KeySequence("crm_business_status_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CrmBusinessStatusDO { + + /** + * 主键 + */ + @TableId + private Long id; + /** + * 状æ€ç±»åž‹ç¼–å· + * + * å…³è” {@link CrmBusinessStatusTypeDO#getId()} + */ + private Long typeId; + /** + * 状æ€å + */ + private String name; + /** + * èµ¢å•çŽ‡ï¼Œç™¾åˆ†æ¯” + */ + private Integer percent; + /** + * æŽ’åº + */ + private Integer sort; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessStatusTypeDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessStatusTypeDO.java new file mode 100644 index 000000000..aa9f86251 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessStatusTypeDO.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.module.crm.dal.dataobject.business; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.util.List; + +/** + * 商机状æ€ç±»åž‹ DO + * + * @author ljlleo + */ +@TableName(value = "crm_business_status_type", autoResultMap = true) +@KeySequence("crm_business_status_type_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CrmBusinessStatusTypeDO extends BaseDO { + + /** + * 主键 + */ + @TableId + private Long id; + /** + * 状æ€ç±»åž‹å + */ + private String name; + /** + * ä½¿ç”¨çš„éƒ¨é—¨ç¼–å· + */ + @TableField(typeHandler = LongListTypeHandler.class) + private List deptIds; + /** + * å¼€å¯çŠ¶æ€ + * + * TODO æ”¹æˆ Integerï¼Œå…³è” CommonStatus + * 枚举 {@link CommonStatusEnum} + */ + private Boolean status; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/clue/CrmClueDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/clue/CrmClueDO.java new file mode 100644 index 000000000..5ea1dea28 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/clue/CrmClueDO.java @@ -0,0 +1,116 @@ +package cn.iocoder.yudao.module.crm.dal.dataobject.clue; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.enums.DictTypeConstants; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.time.LocalDateTime; + +// TODO 芋艿:字段的顺åºï¼Œéœ€è¦æ•´ç†ä¸‹ï¼› +/** + * 线索 DO + * + * @author Wanwan + */ +@TableName("crm_clue") +@KeySequence("crm_clue_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CrmClueDO extends BaseDO { + + /** + * ç¼–å·ï¼Œä¸»é”®è‡ªå¢ž + */ + @TableId + private Long id; + /** + * è½¬åŒ–çŠ¶æ€ + */ + private Boolean transformStatus; + /** + * è·Ÿè¿›çŠ¶æ€ + */ + private Boolean followUpStatus; + /** + * 线索å称 + */ + private String name; + /** + * 客户 id + * + * å…³è” {@link CrmCustomerDO#getId()} + */ + private Long customerId; + /** + * 下次è”系时间 + */ + private LocalDateTime contactNextTime; + /** + * ç”µè¯ + */ + private String telephone; + /** + * æ‰‹æœºå· + */ + private String mobile; + /** + * åœ°å€ + */ + private String address; + /** + * 最åŽè·Ÿè¿›æ—¶é—´ TODO 添加跟进记录时更新该值 + */ + private LocalDateTime contactLastTime; + /** + * 备注 + */ + private String remark; + /** + * è´Ÿè´£äººçš„ç”¨æˆ·ç¼–å· + * å…³è” AdminUserDO çš„ id 字段 + */ + private Long ownerUserId; + /** + * 所属行业 + * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_INDUSTRY} + */ + private Integer industryId; + /** + * 客户等级 + * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_LEVEL} + */ + private Integer level; + /** + * 客户æ¥æº + * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_SOURCE} + */ + private Integer source; + /** + * ç½‘å€ + */ + private String website; + /** + * QQ + */ + private String qq; + /** + * wechat + */ + private String wechat; + /** + * email + */ + private String email; + /** + * 客户æè¿° + */ + private String description; +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/clue/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/clue/package-info.java new file mode 100644 index 000000000..929b9b6fe --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/clue/package-info.java @@ -0,0 +1,4 @@ +/** + * 线索 + */ +package cn.iocoder.yudao.module.crm.dal.dataobject.clue; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contact/CrmContactBusinessDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contact/CrmContactBusinessDO.java new file mode 100644 index 000000000..46185a5ac --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contact/CrmContactBusinessDO.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.crm.dal.dataobject.contact; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * CRM è”ç³»äººä¸Žå•†æœºçš„å…³è” DO + * + * @author 芋é“æºç  + */ +@TableName("crm_contact_business") +@KeySequence("crm_contact_business_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CrmContactBusinessDO extends BaseDO { + + /** + * 主键 + */ + @TableId + private Long id; + /** + * è”ç³»äººç¼–å· + * + * å…³è” {@link CrmContactDO#getId()} 字段 + */ + private Long contactId; + /** + * å•†æœºç¼–å· + * + * å…³è” {@link CrmBusinessDO#getId()} 字段 + */ + private Long businessId; + +} \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contact/CrmContactDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contact/CrmContactDO.java new file mode 100644 index 000000000..c2dec247d --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contact/CrmContactDO.java @@ -0,0 +1,117 @@ +package cn.iocoder.yudao.module.crm.dal.dataobject.contact; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.time.LocalDateTime; + +/** + * CRM è”系人 DO + * + * @author 芋é“æºç  + */ +@TableName("crm_contact") +@KeySequence("crm_contact_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CrmContactDO extends BaseDO { + + /** + * 主键 + */ + @TableId + private Long id; + /** + * å®¢æˆ·ç¼–å· + * + * å…³è” {@link CrmCustomerDO#getId()} + */ + private Long customerId; + /** + * æ‰‹æœºå· + */ + private String mobile; + /** + * ç”µè¯ + */ + private String telephone; + /** + * 电å­é‚®ç®± + */ + private String email; + /** + * 所在地 + * + * å…³è” {@link cn.iocoder.yudao.framework.ip.core.Area#getId()} 字段 + */ + private Integer areaId; + /** + * è¯¦ç»†åœ°å€ + */ + private String detailAddress; + /** + * 备注 + */ + private String remark; + + /** + * 直属上级 + * + * å…³è” {@link CrmContactDO#id} + */ + private Long parentId; + /** + * 姓å + */ + private String name; + /** + * èŒä½ + */ + private String post; + /** + * QQ + */ + private Long qq; + /** + * 微信 + */ + private String wechat; + /** + * 性别 + * + * 枚举 {@link cn.iocoder.yudao.module.system.enums.common.SexEnum} + */ + private Integer sex; + /** + * 是å¦å…³é”®å†³ç­–人 + */ + private Boolean master; + /** + * è´Ÿè´£äººç”¨æˆ·ç¼–å· + * + * å…³è” AdminUserDO çš„ id 字段 + */ + private Long ownerUserId; + + /** + * 最åŽè·Ÿè¿›æ—¶é—´ + */ + private LocalDateTime contactLastTime; + /** + * 最åŽè·Ÿè¿›å†…容 + */ + private String contactLastContent; + /** + * 下次è”系时间 + */ + private LocalDateTime contactNextTime; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contact/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contact/package-info.java new file mode 100644 index 000000000..dfe0898e3 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contact/package-info.java @@ -0,0 +1,4 @@ +/** + * è”系人 + */ +package cn.iocoder.yudao.module.crm.dal.dataobject.contact; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractDO.java new file mode 100644 index 000000000..0c01a3394 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractDO.java @@ -0,0 +1,121 @@ +package cn.iocoder.yudao.module.crm.dal.dataobject.contract; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.time.LocalDateTime; + +// TODO èŠ‹è‰¿ï¼šå®žä½“çš„æ¢³ç† +/** + * CRM åˆåŒ DO + * + * @author dhb52 + */ +@TableName("crm_contract") +@KeySequence("crm_contract_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CrmContractDO extends BaseDO { + + /** + * åˆåŒç¼–å· + */ + @TableId + private Long id; + /** + * åˆåŒç¼–å· + */ + private String no; + /** + * åˆåŒå称 + */ + private String name; + /** + * å®¢æˆ·ç¼–å· + * + * å…³è” {@link CrmCustomerDO#getId()} + */ + private Long customerId; + /** + * å•†æœºç¼–å· + * + * å…³è” {@link CrmBusinessDO#getId()} + */ + private Long businessId; + /** + * 工作æµç¼–å· + * + * å…³è” ProcessInstance çš„ id 属性 + */ + private String processInstanceId; + /** + * 下å•æ—¥æœŸ + */ + private LocalDateTime orderDate; + /** + * 开始时间 + */ + private LocalDateTime startTime; + /** + * 结æŸæ—¶é—´ + */ + private LocalDateTime endTime; + /** + * åˆåŒé‡‘é¢ï¼Œå•ä½ï¼šåˆ† + */ + private Integer price; + /** + * æ•´å•æŠ˜æ‰£ + */ + private Integer discountPercent; + /** + * 产å“总金é¢ï¼Œå•ä½ï¼šåˆ† + */ + private Integer productPrice; + /** + * 客户签约人 + * + * å…³è” {@link CrmContactDO#getId()} + */ + private Long contactId; + /** + * å…¬å¸ç­¾çº¦äºº + * + * å…³è” AdminUserDO çš„ id 字段 + */ + private Long signUserId; + /** + * 最åŽè·Ÿè¿›æ—¶é—´ + */ + private LocalDateTime contactLastTime; + /** + * 备注 + */ + private String remark; + + /** + * è´Ÿè´£äººçš„ç”¨æˆ·ç¼–å· + * + * å…³è” AdminUserDO çš„ id 字段 + */ + private Long ownerUserId; + + /** + * å®¡æ‰¹çŠ¶æ€ + * + * 枚举 {@link CrmAuditStatusEnum} + */ + private Integer auditStatus; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractProductDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractProductDO.java new file mode 100644 index 000000000..bc977c78f --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractProductDO.java @@ -0,0 +1,65 @@ +package cn.iocoder.yudao.module.crm.dal.dataobject.contract; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * åˆåŒäº§å“å…³è”表 DO + * + * @author HUIHUI + */ +@TableName("crm_contract_product") +@KeySequence("crm_contract_product_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CrmContractProductDO extends BaseDO { + + /** + * 主键 + */ + @TableId + private Long id; + /** + * 产å“ç¼–å· + * + * å…³è” {@link CrmProductDO#getId()} + */ + private Long productId; + /** + * åˆåŒç¼–å· + * + * å…³è” {@link CrmContractDO#getId()} + */ + private Long contractId; + /** + * 产å“å•ä»· + */ + private Integer price; + /** + * 销售价格, å•ä½ï¼šåˆ† + */ + private Integer salesPrice; + /** + * æ•°é‡ + */ + private Integer count; + /** + * 折扣 + */ + private Integer discountPercent; + /** + * 总计价格(折扣åŽä»·æ ¼ï¼‰ + * + * TODO @puhui999:å¯ä»¥å†™ä¸‹è®¡ç®—å…¬å¼å“ˆï¼› + */ + private Integer totalPrice; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerDO.java new file mode 100644 index 000000000..2bd614f57 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerDO.java @@ -0,0 +1,135 @@ +package cn.iocoder.yudao.module.crm.dal.dataobject.customer; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.crm.enums.DictTypeConstants; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.time.LocalDateTime; + +// TODO 芋艿:调整下字段 + +/** + * CRM 客户 DO + * + * @author Wanwan + */ +@TableName(value = "crm_customer") +@KeySequence("crm_customer_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CrmCustomerDO extends BaseDO { + + /** + * ç¼–å· + */ + @TableId + private Long id; + /** + * 客户å称 + */ + private String name; + /** + * è·Ÿè¿›çŠ¶æ€ + */ + private Boolean followUpStatus; + /** + * é”å®šçŠ¶æ€ + */ + private Boolean lockStatus; + /** + * æˆäº¤çŠ¶æ€ + */ + private Boolean dealStatus; + /** + * 所属行业 + * + * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_INDUSTRY} + */ + private Integer industryId; + /** + * 客户等级 + * + * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_LEVEL} + */ + private Integer level; + /** + * 客户æ¥æº + * + * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_SOURCE} + */ + private Integer source; + /** + * 手机 + */ + private String mobile; + /** + * ç”µè¯ + */ + private String telephone; + /** + * ç½‘å€ + */ + private String website; + /** + * QQ + */ + private String qq; + /** + * wechat + */ + private String wechat; + /** + * email + */ + private String email; + /** + * 客户æè¿° + */ + private String description; + /** + * 备注 + */ + private String remark; + /** + * è´Ÿè´£äººçš„ç”¨æˆ·ç¼–å· + * + * å…³è” AdminUserDO çš„ id 字段 + */ + private Long ownerUserId; + /** + * 所在地 + * + * å…³è” {@link cn.iocoder.yudao.framework.ip.core.Area#getId()} 字段 + */ + private Integer areaId; + /** + * è¯¦ç»†åœ°å€ + */ + private String detailAddress; + + /** + * 最åŽæŽ¥æ”¶æ—¶é—´ + */ + private LocalDateTime receiveTime; + /** + * 最åŽè·Ÿè¿›æ—¶é—´ + */ + private LocalDateTime contactLastTime; + + /** + * 最åŽè·Ÿè¿›å†…容 + */ + private String contactLastContent; + /** + * 下次è”系时间 + */ + private LocalDateTime contactNextTime; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerLimitConfigDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerLimitConfigDO.java new file mode 100644 index 000000000..df3d3be6f --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerLimitConfigDO.java @@ -0,0 +1,61 @@ +package cn.iocoder.yudao.module.crm.dal.dataobject.customer; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; +import cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.util.List; + +/** + * 客户é™åˆ¶é…ç½® DO + * + * @author Wanwan + */ +@TableName(value = "crm_customer_limit_config", autoResultMap = true) +@KeySequence("crm_customer_limit_config_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CrmCustomerLimitConfigDO extends BaseDO { + + /** + * ç¼–å· + */ + @TableId + private Long id; + /** + * 规则类型 + *

+ * 枚举 {@link CrmCustomerLimitConfigTypeEnum} + */ + private Integer type; + /** + * 规则适用人群 + */ + @TableField(typeHandler = LongListTypeHandler.class) + private List userIds; + /** + * 规则适用部门 + */ + @TableField(typeHandler = LongListTypeHandler.class) + private List deptIds; + /** + * æ•°é‡ä¸Šé™ + */ + private Integer maxCount; + /** + * æˆäº¤å®¢æˆ·æ˜¯å¦å æœ‰æ‹¥æœ‰å®¢æˆ·æ•° + * + * 当且仅当 {@link #type} 为 1 时,进行使用 + */ + private Boolean dealCountEnabled; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerPoolConfigDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerPoolConfigDO.java new file mode 100644 index 000000000..76f20dc3b --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerPoolConfigDO.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.crm.dal.dataobject.customer; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.*; +import lombok.*; + +/** + * 客户公海é…ç½® DO + * + * @author Wanwan + */ +@TableName(value = "crm_customer_pool_config") +@KeySequence("crm_customer_pool_config_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CrmCustomerPoolConfigDO extends BaseDO { + + /** + * ç¼–å· + */ + @TableId + private Long id; + /** + * 是å¦å¯ç”¨å®¢æˆ·å…¬æµ· + */ + private Boolean enabled; + /** + * 未跟进放入公海天数 + */ + @TableField(updateStrategy = FieldStrategy.ALWAYS) + private Integer contactExpireDays; + /** + * 未æˆäº¤æ”¾å…¥å…¬æµ·å¤©æ•° + */ + @TableField(updateStrategy = FieldStrategy.ALWAYS) + private Integer dealExpireDays; + /** + * 是å¦å¼€å¯æå‰æ醒 + */ + @TableField(updateStrategy = FieldStrategy.ALWAYS) + private Boolean notifyEnabled; + /** + * æå‰æ醒天数 + */ + @TableField(updateStrategy = FieldStrategy.ALWAYS) + private Integer notifyDays; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/followup/CrmFollowUpRecordDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/followup/CrmFollowUpRecordDO.java new file mode 100644 index 000000000..a01d47af9 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/followup/CrmFollowUpRecordDO.java @@ -0,0 +1,97 @@ +package cn.iocoder.yudao.module.crm.dal.dataobject.followup; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; +import cn.iocoder.yudao.framework.mybatis.core.type.StringListTypeHandler; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; +import cn.iocoder.yudao.module.crm.enums.DictTypeConstants; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 跟进记录 DO + * + * 用于记录客户ã€è”系人的æ¯ä¸€æ¬¡è·Ÿè¿› + * + * @author 芋é“æºç  + */ +@TableName(value = "crm_follow_up_record", autoResultMap = true) +@KeySequence("crm_follow_up_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CrmFollowUpRecordDO extends BaseDO { + + /** + * ç¼–å· + */ + @TableId + private Long id; + + /** + * æ•°æ®ç±»åž‹ + * + * 枚举 {@link CrmBizTypeEnum} + */ + private Integer bizType; + /** + * æ•°æ®ç¼–å· + * + * å…³è” {@link CrmBizTypeEnum} å¯¹åº”æ¨¡å— DO çš„ id 字段 + */ + private Long bizId; + + /** + * 跟进类型 + * + * å…³è” {@link DictTypeConstants#CRM_FOLLOW_UP_TYPE} å­—å…¸ + */ + private Integer type; + /** + * 跟进内容 + */ + private String content; + /** + * 下次è”系时间 + */ + private LocalDateTime nextTime; + + /** + * 图片 + */ + @TableField(typeHandler = StringListTypeHandler.class) + private List picUrls; + /** + * 附件 + */ + @TableField(typeHandler = StringListTypeHandler.class) + private List fileUrls; + + /** + * å…³è”的商机编å·æ•°ç»„ + * + * å…³è” {@link CrmBusinessDO#getId()} + */ + @TableField(typeHandler = LongListTypeHandler.class) + private List businessIds; + /** + * å…³è”çš„è”系人编å·æ•°ç»„ + * + * å…³è” {@link CrmContactDO#getId()} + */ + @TableField(typeHandler = LongListTypeHandler.class) + private List contactIds; + + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/permission/CrmPermissionDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/permission/CrmPermissionDO.java new file mode 100644 index 000000000..59e47a5a8 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/permission/CrmPermissionDO.java @@ -0,0 +1,59 @@ +package cn.iocoder.yudao.module.crm.dal.dataobject.permission; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * CRM æ•°æ®æƒé™ DO + * + * @author HUIHUI + */ +@TableName("crm_permission") +@KeySequence("crm_permission_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CrmPermissionDO extends BaseDO { + + /** + * ç¼–å·ï¼Œä¸»é”®è‡ªå¢ž + */ + @TableId + private Long id; + + /** + * æ•°æ®ç±»åž‹ + * + * 枚举 {@link CrmBizTypeEnum} + */ + private Integer bizType; + /** + * æ•°æ®ç¼–å· + * + * å…³è” {@link CrmBizTypeEnum} å¯¹åº”æ¨¡å— DO çš„ id 字段 + */ + private Long bizId; + + /** + * ç”¨æˆ·ç¼–å· + * + * å…³è” AdminUser çš„ id 字段 + */ + private Long userId; + + /** + * æƒé™çº§åˆ« + * + * å…³è” {@link CrmPermissionLevelEnum} + */ + private Integer level; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/product/CrmProductCategoryDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/product/CrmProductCategoryDO.java new file mode 100644 index 000000000..e0f4d6245 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/product/CrmProductCategoryDO.java @@ -0,0 +1,49 @@ +package cn.iocoder.yudao.module.crm.dal.dataobject.product; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 产å“分类 DO + * + * @author ZanGe丶 + */ +@TableName("crm_product_category") +@KeySequence("crm_product_category_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CrmProductCategoryDO extends BaseDO { + + /** + * çˆ¶åˆ†ç±»ç¼–å· - 根分类 + */ + public static final Long PARENT_ID_NULL = 0L; + /** + * é™å®šåˆ†ç±»å±‚级 + */ + public static final int CATEGORY_LEVEL = 2; + + /** + * åˆ†ç±»ç¼–å· + */ + @TableId + private Long id; + /** + * 分类å称 + */ + private String name; + /** + * çˆ¶çº§ç¼–å· + * + * å…³è” {@link CrmProductCategoryDO#getId()} + */ + private Long parentId; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/product/CrmProductDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/product/CrmProductDO.java new file mode 100644 index 000000000..a3c56ccc9 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/product/CrmProductDO.java @@ -0,0 +1,72 @@ +package cn.iocoder.yudao.module.crm.dal.dataobject.product; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.crm.enums.DictTypeConstants; +import cn.iocoder.yudao.module.crm.enums.product.CrmProductStatusEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * CRM äº§å“ DO + * + * @author ZanGe丶 + */ +@TableName("crm_product") +@KeySequence("crm_product_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CrmProductDO extends BaseDO { + + /** + * ç¼–å· + */ + @TableId + private Long id; + /** + * 产å“å称 + */ + private String name; + /** + * 产å“ç¼–ç  + */ + private String no; + /** + * å•ä½ + * + * å­—å…¸ {@link DictTypeConstants#CRM_PRODUCT_UNIT} + */ + private Integer unit; + /** + * 价格,å•ä½ï¼šåˆ† + */ + private Integer price; + /** + * çŠ¶æ€ + * + * å…³è” {@link CrmProductStatusEnum} + */ + private Integer status; + /** + * 产å“分类 ID + * + * å…³è” {@link CrmProductCategoryDO#getId()} 字段 + */ + private Long categoryId; + /** + * 产å“æè¿° + */ + private String description; + /** + * è´Ÿè´£äººçš„ç”¨æˆ·ç¼–å· + * + * å…³è” AdminUserDO çš„ id 字段 + */ + private Long ownerUserId; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/product/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/product/package-info.java new file mode 100644 index 000000000..4c7282d73 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/product/package-info.java @@ -0,0 +1,4 @@ +/** + * 产å“表 + */ +package cn.iocoder.yudao.module.crm.dal.dataobject.product; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivableDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivableDO.java new file mode 100644 index 000000000..842fc96b4 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivableDO.java @@ -0,0 +1,96 @@ +package cn.iocoder.yudao.module.crm.dal.dataobject.receivable; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.time.LocalDateTime; + +/** + * 回款 DO + * + * @author 赤焰 + */ +@TableName("crm_receivable") +@KeySequence("crm_receivable_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CrmReceivableDO extends BaseDO { + + /** + * ID + */ + @TableId + private Long id; + /** + * å›žæ¬¾ç¼–å· + */ + private String no; + // TODO @liuhongfeng:“对应实体â€ï¼Œå‚考别的模å—ï¼Œå…³è” {@link TableField.MetaInfo#getJdbcType()} + /** + * 回款计划 + * + * TODO @liuhongfeng:这个字段什么时候更新,也å¯ä»¥å†™ä¸‹ + * + * 对应实体 {@link CrmReceivablePlanDO} + */ + private Long planId; + /** + * 客户 ID + * + * 对应实体 {@link cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO} + */ + private Long customerId; + /** + * åˆåŒ ID + * + * 对应实体 {@link CrmContractDO} + */ + private Long contractId; + /** + * 工作æµç¼–å· + * + * TODO @liuhongfeng:这个字段,åŽç»­è¦å†™ä¸‹å…³è”的实体哈 + */ + private Long processInstanceId; + /** + * 回款日期 + */ + private LocalDateTime returnTime; + // TODO @liuhongfeng:少个枚举 + /** + * å›žæ¬¾æ–¹å¼ + */ + private Integer returnType; + /** + * å›žæ¬¾é‡‘é¢ + */ + private Integer price; + // TODO @liuhongfeng:少关è”实体; + /** + * 负责人 + */ + private Long ownerUserId; + /** + * æ˜¾ç¤ºé¡ºåº + */ + private Integer sort; + /** + * å®¡æ ¸çŠ¶æ€ + * + * 枚举 {@link cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum} + */ + private Integer auditStatus; + /** + * 备注 + */ + private String remark; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivablePlanDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivablePlanDO.java new file mode 100644 index 000000000..78658e609 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivablePlanDO.java @@ -0,0 +1,88 @@ +package cn.iocoder.yudao.module.crm.dal.dataobject.receivable; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.time.LocalDateTime; + +/** + * 回款计划 DO + * + * @author 芋é“æºç  + */ +@TableName("crm_receivable_plan") +@KeySequence("crm_receivable_plan_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CrmReceivablePlanDO extends BaseDO { + + /** + * ID + */ + @TableId + private Long id; + /** + * 期数 + */ + private Integer period; + /** + * 回款ID + * + * TODO @liuhongfeng:少关è”实体; + */ + private Long receivableId; + /** + * 完æˆçŠ¶æ€ + */ + private Boolean finishStatus; + /** + * 计划回款金é¢ï¼Œå•ä½ï¼šåˆ† + */ + private Integer price; + /** + * 计划回款日期 + */ + private LocalDateTime returnTime; + /** + * æå‰å‡ å¤©æ醒 + */ + private Integer remindDays; + /** + * æ醒日期 + */ + private LocalDateTime remindTime; + /** + * 客户 ID + * + * TODO @liuhongfeng:少关è”实体; + */ + private Long customerId; + /** + * åˆåŒ ID + * + * TODO @liuhongfeng:少关è”实体; + */ + private Long contractId; + /** + * 负责人 ID + * + * TODO @liuhongfeng:少关è”实体; + */ + private Long ownerUserId; + /** + * æ˜¾ç¤ºé¡ºåº + */ + private Integer sort; + /** + * 备注 + */ + private String remark; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/package-info.java new file mode 100644 index 000000000..f27442cdf --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/package-info.java @@ -0,0 +1,4 @@ +/** + * 回款 + */ +package cn.iocoder.yudao.module.crm.dal.dataobject.receivable; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/bi/CrmBiRankingMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/bi/CrmBiRankingMapper.java new file mode 100644 index 000000000..9b71df7b6 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/bi/CrmBiRankingMapper.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.crm.dal.mysql.bi; + +import cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRanKRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRankReqVO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * CRM BI 排行榜 Mapper + * + * @author anhaohao + */ +@Mapper +public interface CrmBiRankingMapper { + + /** + * 查询åˆåŒé‡‘é¢æŽ’行榜 + * + * @param rankReqVO å‚æ•° + * @return åˆåŒé‡‘é¢æŽ’行榜 + */ + List selectContractPriceRank(CrmBiRankReqVO rankReqVO); + + /** + * 查询回款金é¢æŽ’行榜 + * + * @param rankReqVO å‚æ•° + * @return 回款金é¢æŽ’行榜 + */ + List selectReceivablePriceRank(CrmBiRankReqVO rankReqVO); + + /** + * 查询签约åˆåŒæ•°é‡æŽ’行榜 + * + * @param rankReqVO å‚æ•° + * @return 签约åˆåŒæ•°é‡æŽ’行榜 + */ + List selectContractCountRank(CrmBiRankReqVO rankReqVO); + + /** + * 查询产å“销é‡æŽ’行榜 + * + * @param rankReqVO å‚æ•° + * @return 产å“销é‡æŽ’行榜 + */ + List selectProductSalesRank(CrmBiRankReqVO rankReqVO); + + /** + * 查询新增客户数排行榜 + * + * @param rankReqVO å‚æ•° + * @return 新增客户数排行榜 + */ + List selectCustomerCountRank(CrmBiRankReqVO rankReqVO); + + /** + * 查询è”系人数é‡æŽ’行榜 + * + * @param rankReqVO å‚æ•° + * @return è”系人数é‡æŽ’行榜 + */ + List selectContactsCountRank(CrmBiRankReqVO rankReqVO); + + /** + * 查询跟进次数排行榜 + * + * @param rankReqVO å‚æ•° + * @return 跟进次数排行榜 + */ + List selectFollowCountRank(CrmBiRankReqVO rankReqVO); + + /** + * 查询跟进客户数排行榜 + * + * @param rankReqVO å‚æ•° + * @return 跟进客户数排行榜 + */ + List selectFollowCustomerCountRank(CrmBiRankReqVO rankReqVO); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java new file mode 100644 index 000000000..d7438484f --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java @@ -0,0 +1,66 @@ +package cn.iocoder.yudao.module.crm.dal.mysql.business; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * 商机 Mapper + * + * @author ljlleo + */ +@Mapper +public interface CrmBusinessMapper extends BaseMapperX { + + default int updateOwnerUserIdById(Long id, Long ownerUserId) { + return update(new LambdaUpdateWrapper() + .eq(CrmBusinessDO::getId, id) + .set(CrmBusinessDO::getOwnerUserId, ownerUserId)); + } + + default PageResult selectPageByCustomerId(CrmBusinessPageReqVO pageReqVO) { + return selectPage(pageReqVO, new LambdaQueryWrapperX() + .eq(CrmBusinessDO::getCustomerId, pageReqVO.getCustomerId()) // æŒ‡å®šå®¢æˆ·ç¼–å· + .likeIfPresent(CrmBusinessDO::getName, pageReqVO.getName()) + .orderByDesc(CrmBusinessDO::getId)); + } + + default PageResult selectPageByContactId(CrmBusinessPageReqVO pageReqVO, Collection businessIds) { + return selectPage(pageReqVO, new LambdaQueryWrapperX() + .in(CrmBusinessDO::getId, businessIds) // æŒ‡å®šå•†æœºç¼–å· + .likeIfPresent(CrmBusinessDO::getName, pageReqVO.getName()) + .orderByDesc(CrmBusinessDO::getId)); + } + + default PageResult selectPage(CrmBusinessPageReqVO pageReqVO, Long userId) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX<>(); + // 拼接数æ®æƒé™çš„查询æ¡ä»¶ + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_BUSINESS.getType(), + CrmBusinessDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE); + // 拼接自身的查询æ¡ä»¶ + query.selectAll(CrmBusinessDO.class) + .likeIfPresent(CrmBusinessDO::getName, pageReqVO.getName()) + .orderByDesc(CrmBusinessDO::getId); + return selectJoinPage(pageReqVO, CrmBusinessDO.class, query); + } + + default List selectBatchIds(Collection ids, Long userId) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX<>(); + // 拼接数æ®æƒé™çš„查询æ¡ä»¶ + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_BUSINESS.getType(), ids, userId); + // 拼接自身的查询æ¡ä»¶ + query.selectAll(CrmBusinessDO.class).in(CrmBusinessDO::getId, ids).orderByDesc(CrmBusinessDO::getId); + return selectJoinList(CrmBusinessDO.class, query); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessProductMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessProductMapper.java new file mode 100644 index 000000000..2d1471c73 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessProductMapper.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.crm.dal.mysql.business; + + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * å•†æœºäº§å“ Mapper + * + * @author lzxhqs + */ +@Mapper +public interface CrmBusinessProductMapper extends BaseMapperX { + + // TODO @puhui999:用ä¸åˆ°çš„方法,看看是ä¸æ˜¯åˆ é™¤å“ˆ + default void deleteByBusinessId(Long getBusinessId) { // TODO @lzxhqs:第一个方法,和类之间最好空一行; + delete(CrmBusinessProductDO::getBusinessId, getBusinessId); + } + + default CrmBusinessProductDO selectByBusinessId(Long getBusinessId) { + return selectOne(CrmBusinessProductDO::getBusinessId, getBusinessId); + } + + default List selectListByBusinessId(Long businessId) { + // TODO @puhui999:å¯ä»¥ç®€åŒ–,selectList(CrmBusinessProductDO::getBusinessId, businessId) + return selectList(new LambdaQueryWrapperX().eq(CrmBusinessProductDO::getBusinessId, businessId)); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessStatusMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessStatusMapper.java new file mode 100644 index 000000000..be847ab9d --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessStatusMapper.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.crm.dal.mysql.business; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusQueryVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * å•†æœºçŠ¶æ€ Mapper + * + * @author ljlleo + */ +@Mapper +public interface CrmBusinessStatusMapper extends BaseMapperX { + + default PageResult selectPage(CrmBusinessStatusPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .orderByDesc(CrmBusinessStatusDO::getId)); + } + + default List selectList(CrmBusinessStatusQueryVO queryVO) { + return selectList(new LambdaQueryWrapperX() + .eqIfPresent(CrmBusinessStatusDO::getTypeId, queryVO.getTypeId()) + .inIfPresent(CrmBusinessStatusDO::getId, queryVO.getIdList()) + .orderByDesc(CrmBusinessStatusDO::getId)); + } + + default int delete(Long typeId) { + return delete(CrmBusinessStatusDO::getTypeId, typeId); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessStatusTypeMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessStatusTypeMapper.java new file mode 100644 index 000000000..410ebf050 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessStatusTypeMapper.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.crm.dal.mysql.business; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypePageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeQueryVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 商机状æ€ç±»åž‹ Mapper + * + * @author ljlleo + */ +@Mapper +public interface CrmBusinessStatusTypeMapper extends BaseMapperX { + + default PageResult selectPage(CrmBusinessStatusTypePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .orderByDesc(CrmBusinessStatusTypeDO::getId)); + } + + default List selectList(CrmBusinessStatusTypeQueryVO queryVO) { + return selectList(new LambdaQueryWrapperX() + .eqIfPresent(CrmBusinessStatusTypeDO::getStatus, queryVO.getStatus()) + .inIfPresent(CrmBusinessStatusTypeDO::getId, queryVO.getIdList())); + } + + // TODO @lzxhqs:这个å¯ä»¥æ”¹æˆ selectByName。业务上基于在判断 id 匹é…;这样更通用一些;mapper å°½é‡é€šç”¨ï¼Œä¸å…³æ³¨æˆ–者特别关è”业务; + /** + * æ ¹æ®IDå’Œname查询 + * + * @param id 商机状æ€ç±»åž‹id + * @param name 状æ€ç±»åž‹å + * @return result + */ + default CrmBusinessStatusTypeDO selectByIdAndName(Long id, String name) { + return selectOne(CrmBusinessStatusTypeDO::getId, id, CrmBusinessStatusTypeDO::getName, name); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/package-info.java new file mode 100644 index 000000000..72863e1f4 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/package-info.java @@ -0,0 +1,4 @@ +/** + * 商机(销售机会) + */ +package cn.iocoder.yudao.module.crm.dal.mysql.business; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/clue/CrmClueMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/clue/CrmClueMapper.java new file mode 100644 index 000000000..cab4c4662 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/clue/CrmClueMapper.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.crm.dal.mysql.clue; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; +import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * 线索 Mapper + * + * @author Wanwan + */ +@Mapper +public interface CrmClueMapper extends BaseMapperX { + + default int updateOwnerUserIdById(Long id, Long ownerUserId) { + return update(new LambdaUpdateWrapper() + .eq(CrmClueDO::getId, id) + .set(CrmClueDO::getOwnerUserId, ownerUserId)); + } + + default PageResult selectPage(CrmCluePageReqVO pageReqVO, Long userId) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX<>(); + // 拼接数æ®æƒé™çš„查询æ¡ä»¶ + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_LEADS.getType(), + CrmClueDO::getId, userId, pageReqVO.getSceneType(), pageReqVO.getPool()); + // 拼接自身的查询æ¡ä»¶ + query.selectAll(CrmClueDO.class) + .likeIfPresent(CrmClueDO::getName, pageReqVO.getName()) + .likeIfPresent(CrmClueDO::getTelephone, pageReqVO.getTelephone()) + .likeIfPresent(CrmClueDO::getMobile, pageReqVO.getMobile()) + .eqIfPresent(CrmClueDO::getIndustryId, pageReqVO.getIndustryId()) + .eqIfPresent(CrmClueDO::getLevel, pageReqVO.getLevel()) + .eqIfPresent(CrmClueDO::getSource, pageReqVO.getSource()) + .orderByDesc(CrmClueDO::getId); + return selectJoinPage(pageReqVO, CrmClueDO.class, query); + } + + default List selectBatchIds(Collection ids, Long userId) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX<>(); + // 拼接数æ®æƒé™çš„查询æ¡ä»¶ + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_LEADS.getType(), ids, userId); + query.selectAll(CrmClueDO.class).in(CrmClueDO::getId, ids).orderByDesc(CrmClueDO::getId); + // 拼接自身的查询æ¡ä»¶ + return selectJoinList(CrmClueDO.class, query); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/clue/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/clue/package-info.java new file mode 100644 index 000000000..f9978e868 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/clue/package-info.java @@ -0,0 +1,4 @@ +/** + * 线索 + */ +package cn.iocoder.yudao.module.crm.dal.mysql.clue; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java new file mode 100644 index 000000000..c35df47d3 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java @@ -0,0 +1,75 @@ +package cn.iocoder.yudao.module.crm.dal.mysql.contact; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; +import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactPageReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * CRM è”系人 Mapper + * + * @author 芋é“æºç  + */ +@Mapper +public interface CrmContactMapper extends BaseMapperX { + + default int updateOwnerUserIdById(Long id, Long ownerUserId) { + return update(new LambdaUpdateWrapper() + .eq(CrmContactDO::getId, id) + .set(CrmContactDO::getOwnerUserId, ownerUserId)); + } + + default int updateOwnerUserIdByCustomerId(Long customerId, Long ownerUserId) { + return update(new LambdaUpdateWrapper() + .eq(CrmContactDO::getCustomerId, customerId) + .set(CrmContactDO::getOwnerUserId, ownerUserId)); + } + + default PageResult selectPageByCustomerId(CrmContactPageReqVO pageVO) { + return selectPage(pageVO, new LambdaQueryWrapperX() + .eq(CrmContactDO::getCustomerId, pageVO.getCustomerId()) // æŒ‡å®šå®¢æˆ·ç¼–å· + .likeIfPresent(CrmContactDO::getName, pageVO.getName()) + .eqIfPresent(CrmContactDO::getMobile, pageVO.getMobile()) + .eqIfPresent(CrmContactDO::getTelephone, pageVO.getTelephone()) + .eqIfPresent(CrmContactDO::getEmail, pageVO.getEmail()) + .eqIfPresent(CrmContactDO::getQq, pageVO.getQq()) + .eqIfPresent(CrmContactDO::getWechat, pageVO.getWechat()) + .orderByDesc(CrmContactDO::getId)); + } + + default PageResult selectPage(CrmContactPageReqVO pageReqVO, Long userId) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX<>(); + // 拼接数æ®æƒé™çš„查询æ¡ä»¶ + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTACT.getType(), + CrmContactDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE); + // 拼接自身的查询æ¡ä»¶ + query.selectAll(CrmContactDO.class) + .likeIfPresent(CrmContactDO::getName, pageReqVO.getName()) + .eqIfPresent(CrmContactDO::getMobile, pageReqVO.getMobile()) + .eqIfPresent(CrmContactDO::getTelephone, pageReqVO.getTelephone()) + .eqIfPresent(CrmContactDO::getEmail, pageReqVO.getEmail()) + .eqIfPresent(CrmContactDO::getQq, pageReqVO.getQq()) + .eqIfPresent(CrmContactDO::getWechat, pageReqVO.getWechat()) + .orderByDesc(CrmContactDO::getId); + return selectJoinPage(pageReqVO, CrmContactDO.class, query); + } + + default List selectBatchIds(Collection ids, Long userId) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX<>(); + // 拼接数æ®æƒé™çš„查询æ¡ä»¶ + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTACT.getType(), ids, userId); + // 拼接自身的查询æ¡ä»¶ + query.selectAll(CrmContactDO.class).in(CrmContactDO::getId, ids).orderByDesc(CrmContactDO::getId); + return selectJoinList(CrmContactDO.class, query); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/package-info.java new file mode 100644 index 000000000..6cb7d4be2 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/package-info.java @@ -0,0 +1,4 @@ +/** + * è”系人 + */ +package cn.iocoder.yudao.module.crm.dal.mysql.contact; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contactbusinesslink/CrmContactBusinessMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contactbusinesslink/CrmContactBusinessMapper.java new file mode 100644 index 000000000..3eae483bc --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contactbusinesslink/CrmContactBusinessMapper.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.crm.dal.mysql.contactbusinesslink; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * CRM è”ç³»äººä¸Žå•†æœºçš„å…³è” Mapper + * + * @author 芋é“æºç  + */ +@Mapper +public interface CrmContactBusinessMapper extends BaseMapperX { + + default CrmContactBusinessDO selectByContactIdAndBusinessId(Long contactId, Long businessId) { + return selectOne(CrmContactBusinessDO::getContactId, contactId, + CrmContactBusinessDO::getBusinessId, businessId); + } + + default void deleteByContactIdAndBusinessId(Long contactId, Collection businessIds) { + delete(new LambdaQueryWrapper() + .eq(CrmContactBusinessDO::getContactId, contactId) + .in(CrmContactBusinessDO::getBusinessId, businessIds)); + } + + default List selectListByContactId(Long contactId) { + return selectList(CrmContactBusinessDO::getContactId, contactId); + } + +} \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java new file mode 100644 index 000000000..3ecd93fcb --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java @@ -0,0 +1,90 @@ +package cn.iocoder.yudao.module.crm.dal.mysql.contract; + +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; +import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; +import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.List; + +/** + * CRM åˆåŒ Mapper + * + * @author dhb52 + */ +@Mapper +public interface CrmContractMapper extends BaseMapperX { + + default int updateOwnerUserIdById(Long id, Long ownerUserId) { + return update(new LambdaUpdateWrapper() + .eq(CrmContractDO::getId, id) + .set(CrmContractDO::getOwnerUserId, ownerUserId)); + } + + default PageResult selectPageByCustomerId(CrmContractPageReqVO pageReqVO) { + return selectPage(pageReqVO, new LambdaQueryWrapperX() + .eq(CrmContractDO::getCustomerId, pageReqVO.getCustomerId()) + .likeIfPresent(CrmContractDO::getNo, pageReqVO.getNo()) + .likeIfPresent(CrmContractDO::getName, pageReqVO.getName()) + .eqIfPresent(CrmContractDO::getCustomerId, pageReqVO.getCustomerId()) + .eqIfPresent(CrmContractDO::getBusinessId, pageReqVO.getBusinessId()) + .orderByDesc(CrmContractDO::getId)); + } + + default PageResult selectPage(CrmContractPageReqVO pageReqVO, Long userId) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX<>(); + // 拼接数æ®æƒé™çš„查询æ¡ä»¶ + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTRACT.getType(), + CrmContractDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE); + // 拼接自身的查询æ¡ä»¶ + query.selectAll(CrmContractDO.class) + .likeIfPresent(CrmContractDO::getNo, pageReqVO.getNo()) + .likeIfPresent(CrmContractDO::getName, pageReqVO.getName()) + .eqIfPresent(CrmContractDO::getCustomerId, pageReqVO.getCustomerId()) + .eqIfPresent(CrmContractDO::getBusinessId, pageReqVO.getBusinessId()) + .eqIfPresent(CrmContractDO::getAuditStatus, pageReqVO.getAuditStatus()) + .orderByDesc(CrmContractDO::getId); + + // Backlog: å³å°†åˆ°æœŸçš„åˆåŒ + LocalDateTime beginOfToday = LocalDateTimeUtil.beginOfDay(LocalDateTime.now()); + LocalDateTime endOfToday = LocalDateTimeUtil.endOfDay(LocalDateTime.now()); + if (CrmContractPageReqVO.EXPIRY_TYPE_ABOUT_TO_EXPIRE.equals(pageReqVO.getExpiryType())) { // å³å°†åˆ°æœŸ + // TODO: @芋艿 需è¦é…ç½® æå‰æ醒天数 + int REMIND_DAYS = 20; + query.eq(CrmContractDO::getAuditStatus, CrmAuditStatusEnum.APPROVE.getStatus()) + .between(CrmContractDO::getEndTime, beginOfToday, endOfToday.plusDays(REMIND_DAYS)); + } else if (CrmContractPageReqVO.EXPIRY_TYPE_EXPIRED.equals(pageReqVO.getExpiryType())) { // 已到期 + query.eq(CrmContractDO::getAuditStatus, CrmAuditStatusEnum.APPROVE.getStatus()) + .lt(CrmContractDO::getEndTime, endOfToday); + } + return selectJoinPage(pageReqVO, CrmContractDO.class, query); + } + + default List selectBatchIds(Collection ids, Long userId) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX<>(); + // 构建数æ®æƒé™è¿žè¡¨æ¡ä»¶ + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTRACT.getType(), ids, userId); + // 拼接自身的查询æ¡ä»¶ + query.selectAll(CrmContractDO.class).in(CrmContractDO::getId, ids).orderByDesc(CrmContractDO::getId); + return selectJoinList(CrmContractDO.class, query); + } + + default Long selectCountByContactId(Long contactId) { + return selectCount(CrmContractDO::getContactId, contactId); + } + + default Long selectCountByBusinessId(Long businessId) { + return selectCount(CrmContractDO::getBusinessId, businessId); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractProductMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractProductMapper.java new file mode 100644 index 000000000..814024125 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractProductMapper.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.crm.dal.mysql.contract; + + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractProductDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * åˆåŒäº§å“ Mapper + * + * @author HUIHUI + */ +@Mapper +public interface CrmContractProductMapper extends BaseMapperX { + + // TODO @puhui999:用ä¸åˆ°çš„方法,看看是ä¸æ˜¯åˆ é™¤å“ˆ + default void deleteByContractId(Long contractId) { // TODO @lzxhqs:第一个方法,和类之间最好空一行; + delete(CrmContractProductDO::getContractId, contractId); + } + + default CrmContractProductDO selectByContractId(Long contractId) { + return selectOne(CrmContractProductDO::getContractId, contractId); + } + + default List selectListByContractId(Long contractId) { + return selectList(new LambdaQueryWrapperX().eq(CrmContractProductDO::getContractId, contractId)); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerLimitConfigMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerLimitConfigMapper.java new file mode 100644 index 000000000..08beaf808 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerLimitConfigMapper.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.crm.dal.mysql.customer; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigPageReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 客户é™åˆ¶é…ç½® Mapper + * + * @author Wanwan + */ +@Mapper +public interface CrmCustomerLimitConfigMapper extends BaseMapperX { + + default PageResult selectPage(CrmCustomerLimitConfigPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(CrmCustomerLimitConfigDO::getType, reqVO.getType()) + .orderByDesc(CrmCustomerLimitConfigDO::getId)); + } + + default List selectListByTypeAndUserIdAndDeptId( + Integer type, Long userId, Long deptId) { + LambdaQueryWrapperX query = new LambdaQueryWrapperX() + .eq(CrmCustomerLimitConfigDO::getType, type); + query.apply("FIND_IN_SET({0}, user_ids) > 0", userId); + if (deptId != null) { + query.apply("FIND_IN_SET({0}, dept_ids) > 0", deptId); + } + return selectList(query); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java new file mode 100644 index 000000000..29cd47c66 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java @@ -0,0 +1,150 @@ +package cn.iocoder.yudao.module.crm.dal.mysql.customer; + +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; +import cn.iocoder.yudao.module.crm.controller.admin.backlog.vo.CrmTodayCustomerPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.lang.Nullable; + +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.List; + +/** + * 客户 Mapper + * + * @author Wanwan + */ +@Mapper +public interface CrmCustomerMapper extends BaseMapperX { + + default Long selectCountByLockStatusAndOwnerUserId(Boolean lockStatus, Long ownerUserId) { + return selectCount(new LambdaUpdateWrapper() + .eq(CrmCustomerDO::getLockStatus, lockStatus) + .eq(CrmCustomerDO::getOwnerUserId, ownerUserId)); + } + + default Long selectCountByDealStatusAndOwnerUserId(@Nullable Boolean dealStatus, Long ownerUserId) { + return selectCount(new LambdaQueryWrapperX() + .eqIfPresent(CrmCustomerDO::getDealStatus, dealStatus) + .eq(CrmCustomerDO::getOwnerUserId, ownerUserId)); + } + + default int updateOwnerUserIdById(Long id, Long ownerUserId) { + return update(new LambdaUpdateWrapper() + .eq(CrmCustomerDO::getId, id) + .set(CrmCustomerDO::getOwnerUserId, ownerUserId)); + } + + default PageResult selectPage(CrmCustomerPageReqVO pageReqVO, Long userId) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX<>(); + // 拼接数æ®æƒé™çš„查询æ¡ä»¶ + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(), + CrmCustomerDO::getId, userId, pageReqVO.getSceneType(), pageReqVO.getPool()); + // 拼接自身的查询æ¡ä»¶ + query.selectAll(CrmCustomerDO.class) + .likeIfPresent(CrmCustomerDO::getName, pageReqVO.getName()) + .eqIfPresent(CrmCustomerDO::getMobile, pageReqVO.getMobile()) + .eqIfPresent(CrmCustomerDO::getIndustryId, pageReqVO.getIndustryId()) + .eqIfPresent(CrmCustomerDO::getLevel, pageReqVO.getLevel()) + .eqIfPresent(CrmCustomerDO::getSource, pageReqVO.getSource()); + return selectJoinPage(pageReqVO, CrmCustomerDO.class, query); + } + + default List selectBatchIds(Collection ids, Long userId) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX<>(); + // 拼接数æ®æƒé™çš„查询æ¡ä»¶ + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(), ids, userId); + // 拼接自身的查询æ¡ä»¶ + query.selectAll(CrmCustomerDO.class).in(CrmCustomerDO::getId, ids).orderByDesc(CrmCustomerDO::getId); + return selectJoinList(CrmCustomerDO.class, query); + } + + /** + * 待办事项 - 今日需è”系客户 + * + * @param pageReqVO 分页请求å‚æ•° + * @param userId 当å‰ç”¨æˆ·ID + * @return 分页结果 + */ + default PageResult selectTodayCustomerPage(CrmTodayCustomerPageReqVO pageReqVO, Long userId) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX<>(); + // 拼接数æ®æƒé™çš„查询æ¡ä»¶ + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(), + CrmCustomerDO::getId, userId, pageReqVO.getSceneType(), null); + + // 拼接自身的查询æ¡ä»¶ + query.selectAll(CrmCustomerDO.class); + if (pageReqVO.getContactStatus() != null) { + LocalDateTime beginOfToday = LocalDateTimeUtil.beginOfDay(LocalDateTime.now()); + LocalDateTime endOfToday = LocalDateTimeUtil.endOfDay(LocalDateTime.now()); + if (pageReqVO.getContactStatus().equals(CrmTodayCustomerPageReqVO.CONTACT_TODAY)) { // 今天需è”ç³» + query.between(CrmCustomerDO::getContactNextTime, beginOfToday, endOfToday); + } else if (pageReqVO.getContactStatus().equals(CrmTodayCustomerPageReqVO.CONTACT_EXPIRED)) { // 已逾期 + query.lt(CrmCustomerDO::getContactNextTime, beginOfToday); + } else if (pageReqVO.getContactStatus().equals(CrmTodayCustomerPageReqVO.CONTACT_ALREADY)) { // å·²è”ç³» + query.between(CrmCustomerDO::getContactLastTime, beginOfToday, endOfToday); + } else { + throw new IllegalArgumentException("未知è”系状æ€ï¼š" + pageReqVO.getContactStatus()); + } + } + return selectJoinPage(pageReqVO, CrmCustomerDO.class, query); + } + + default List selectListByLockAndNotPool(Boolean lockStatus) { + return selectList(new LambdaQueryWrapper() + .eq(CrmCustomerDO::getLockStatus, lockStatus) + .gt(CrmCustomerDO::getOwnerUserId, 0)); + } + + default CrmCustomerDO selectByCustomerName(String name) { + return selectOne(CrmCustomerDO::getName, name); + } + + default PageResult selectPutInPoolRemindCustomerPage(CrmCustomerPageReqVO pageReqVO, + CrmCustomerPoolConfigDO poolConfigDO, + Long userId) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX<>(); + // 拼接数æ®æƒé™çš„查询æ¡ä»¶ + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(), + CrmCustomerDO::getId, userId, pageReqVO.getSceneType(), null); + // TODO @dhb52:lock 的情况,ä¸éœ€è¦æ醒哈; + + // 拼接自身的查询æ¡ä»¶ + query.selectAll(CrmCustomerDO.class); + // 情况一:未æˆäº¤æ醒日期区间 + Integer dealExpireDays = poolConfigDO.getDealExpireDays(); + LocalDateTime startDealRemindDate = LocalDateTimeUtil.beginOfDay(LocalDateTime.now()) + .minusDays(dealExpireDays); + LocalDateTime endDealRemindDate = LocalDateTimeUtil.endOfDay(LocalDateTime.now()) + .minusDays(Math.max(dealExpireDays - poolConfigDO.getNotifyDays(), 0)); + // 情况二:未跟进æ醒日期区间 + Integer contactExpireDays = poolConfigDO.getContactExpireDays(); + LocalDateTime startContactRemindDate = LocalDateTimeUtil.beginOfDay(LocalDateTime.now()) + .minusDays(contactExpireDays); + LocalDateTime endContactRemindDate = LocalDateTimeUtil.endOfDay(LocalDateTime.now()) + .minusDays(Math.max(contactExpireDays - poolConfigDO.getNotifyDays(), 0)); + query + // 情况一:1. 未æˆäº¤æ”¾å…¥å…¬æµ·æ醒 + .eq(CrmCustomerDO::getDealStatus, false) + .between(CrmCustomerDO::getCreateTime, startDealRemindDate, endDealRemindDate) + // 情况二:未跟进放入公海æ醒 + .or() // 2.1 contactLastTime 为空 TODO 芋艿:这个è¦ä¸è¦æžä¸ªé»˜è®¤å€¼ï¼› + .isNull(CrmCustomerDO::getContactLastTime) + .between(CrmCustomerDO::getCreateTime, startContactRemindDate, endContactRemindDate) + .or() // 2.2 ContactLastTime ä¸ä¸ºç©º + .between(CrmCustomerDO::getContactLastTime, startContactRemindDate, endContactRemindDate); + return selectJoinPage(pageReqVO, CrmCustomerDO.class, query); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerPoolConfigMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerPoolConfigMapper.java new file mode 100644 index 000000000..06cf44e4f --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerPoolConfigMapper.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.crm.dal.mysql.customer; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 客户公海é…ç½® Mapper + * + * @author Wanwan + */ +@Mapper +public interface CrmCustomerPoolConfigMapper extends BaseMapperX { + + default CrmCustomerPoolConfigDO selectOne() { + return selectOne(new LambdaQueryWrapperX().last("LIMIT 1")); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/followup/CrmFollowUpRecordMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/followup/CrmFollowUpRecordMapper.java new file mode 100644 index 000000000..45e2b412e --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/followup/CrmFollowUpRecordMapper.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.crm.dal.mysql.followup; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordPageReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * 跟进记录 Mapper + * + * @author 芋é“æºç  + */ +@Mapper +public interface CrmFollowUpRecordMapper extends BaseMapperX { + + default PageResult selectPage(CrmFollowUpRecordPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(CrmFollowUpRecordDO::getBizType, reqVO.getBizType()) + .eqIfPresent(CrmFollowUpRecordDO::getBizId, reqVO.getBizId()) + .orderByDesc(CrmFollowUpRecordDO::getId)); + } + + default void deleteByBiz(Integer bizType, Long bizId) { + delete(new LambdaQueryWrapperX() + .eq(CrmFollowUpRecordDO::getBizType, bizType) + .eq(CrmFollowUpRecordDO::getBizId, bizId)); + } + + default List selectListByBiz(Integer bizType, Collection bizIds) { + return selectList(new LambdaQueryWrapperX() + .eq(CrmFollowUpRecordDO::getBizType, bizType) + .in(CrmFollowUpRecordDO::getBizId, bizIds)); + } + +} \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/permission/CrmPermissionMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/permission/CrmPermissionMapper.java new file mode 100644 index 000000000..26f212e5e --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/permission/CrmPermissionMapper.java @@ -0,0 +1,74 @@ +package cn.iocoder.yudao.module.crm.dal.mysql.permission; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * crm æ•°æ®æƒé™ mapper + * + * @author HUIHUI + */ +@Mapper +public interface CrmPermissionMapper extends BaseMapperX { + + default CrmPermissionDO selectByBizTypeAndBizIdByUserId(Integer bizType, Long bizId, Long userId) { + return selectOne(new LambdaQueryWrapperX() + .eq(CrmPermissionDO::getBizType, bizType) + .eq(CrmPermissionDO::getBizId, bizId) + .eq(CrmPermissionDO::getUserId, userId)); + } + + default List selectByBizTypeAndBizId(Integer bizType, Long bizId) { + return selectList(new LambdaQueryWrapperX() + .eq(CrmPermissionDO::getBizType, bizType) + .eq(CrmPermissionDO::getBizId, bizId)); + } + + default List selectByBizTypeAndBizIds(Integer bizType, Collection bizIds) { + return selectList(new LambdaQueryWrapperX() + .eq(CrmPermissionDO::getBizType, bizType) + .in(CrmPermissionDO::getBizId, bizIds)); + } + + default List selectListByBizTypeAndUserId(Integer bizType, Long userId) { + return selectList(new LambdaQueryWrapperX() + .eq(CrmPermissionDO::getBizType, bizType) + .eq(CrmPermissionDO::getUserId, userId)); + } + + default List selectListByBizTypeAndBizIdAndLevel(Integer bizType, Long bizId, Integer level) { + return selectList(new LambdaQueryWrapperX() + .eq(CrmPermissionDO::getBizType, bizType) + .eq(CrmPermissionDO::getBizId, bizId) + .eq(CrmPermissionDO::getLevel, level)); + } + + default CrmPermissionDO selectByIdAndUserId(Long id, Long userId) { + return selectOne(CrmPermissionDO::getId, id, + CrmPermissionDO::getUserId, userId); + } + + default CrmPermissionDO selectByBizIdAndUserId(Long bizId, Long userId) { + return selectOne(CrmPermissionDO::getBizId, bizId, + CrmPermissionDO::getUserId, userId); + } + + default int deletePermission(Integer bizType, Long bizId) { + return delete(new LambdaQueryWrapperX() + .eq(CrmPermissionDO::getBizType, bizType) + .eq(CrmPermissionDO::getBizId, bizId)); + } + + default Long selectListByBiz(Collection bizTypes, Collection bizIds, Collection userIds) { + return selectCount(new LambdaQueryWrapperX() + .in(CrmPermissionDO::getBizType, bizTypes) + .in(CrmPermissionDO::getBizId, bizIds) + .in(CrmPermissionDO::getUserId, userIds)); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/permission/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/permission/package-info.java new file mode 100644 index 000000000..ff0e16b90 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/permission/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.yudao.module.crm.dal.mysql.permission; \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/product/CrmProductCategoryMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/product/CrmProductCategoryMapper.java new file mode 100644 index 000000000..3cced7356 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/product/CrmProductCategoryMapper.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.crm.dal.mysql.product; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.crm.controller.admin.product.vo.category.CrmProductCategoryListReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * CRM 产å“分类 Mapper + * + * @author ZanGe丶 + */ +@Mapper +public interface CrmProductCategoryMapper extends BaseMapperX { + + default List selectList(CrmProductCategoryListReqVO reqVO) { + return selectList(new LambdaQueryWrapperX() + .likeIfPresent(CrmProductCategoryDO::getName, reqVO.getName()) + .eqIfPresent(CrmProductCategoryDO::getParentId, reqVO.getParentId()) + .orderByDesc(CrmProductCategoryDO::getId)); + } + + default CrmProductCategoryDO selectByParentIdAndName(Long parentId, String name) { + return selectOne(CrmProductCategoryDO::getParentId, parentId, CrmProductCategoryDO::getName, name); + } + + default Long selectCountByParentId(Long parentId) { + return selectCount(CrmProductCategoryDO::getParentId, parentId); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/product/CrmProductMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/product/CrmProductMapper.java new file mode 100644 index 000000000..30a07eec2 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/product/CrmProductMapper.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.crm.dal.mysql.product; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; +import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductPageReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils; +import org.apache.ibatis.annotations.Mapper; + +/** + * CRM äº§å“ Mapper + * + * @author ZanGe丶 + */ +@Mapper +public interface CrmProductMapper extends BaseMapperX { + + default PageResult selectPage(CrmProductPageReqVO reqVO, Long userId) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX<>(); + // 拼接数æ®æƒé™çš„查询æ¡ä»¶ + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_PRODUCT.getType(), + CrmProductDO::getId, userId, null, Boolean.FALSE); + // 拼接自身的查询æ¡ä»¶ + query.selectAll(CrmProductDO.class) + .likeIfPresent(CrmProductDO::getName, reqVO.getName()) + .eqIfPresent(CrmProductDO::getStatus, reqVO.getStatus()) + .orderByDesc(CrmProductDO::getId); + return selectJoinPage(reqVO, CrmProductDO.class, query); + } + + default CrmProductDO selectByNo(String no) { + return selectOne(CrmProductDO::getNo, no); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java new file mode 100644 index 000000000..28b2298ec --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java @@ -0,0 +1,62 @@ +package cn.iocoder.yudao.module.crm.dal.mysql.receivable; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * 回款 Mapper + * + * @author 赤焰 + */ +@Mapper +public interface CrmReceivableMapper extends BaseMapperX { + + default int updateOwnerUserIdById(Long id, Long ownerUserId) { + return update(new LambdaUpdateWrapper() + .eq(CrmReceivableDO::getId, id) + .set(CrmReceivableDO::getOwnerUserId, ownerUserId)); + } + + default PageResult selectPageByCustomerId(CrmReceivablePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eq(CrmReceivableDO::getCustomerId, reqVO.getCustomerId()) // 必须传递 + .eqIfPresent(CrmReceivableDO::getNo, reqVO.getNo()) + .eqIfPresent(CrmReceivableDO::getPlanId, reqVO.getPlanId()) + .orderByDesc(CrmReceivableDO::getId)); + } + + default PageResult selectPage(CrmReceivablePageReqVO pageReqVO, Long userId) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX<>(); + // 拼接数æ®æƒé™çš„查询æ¡ä»¶ + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE.getType(), + CrmReceivableDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE); + // 拼接自身的查询æ¡ä»¶ + query.selectAll(CrmReceivableDO.class) + .eqIfPresent(CrmReceivableDO::getNo, pageReqVO.getNo()) + .eqIfPresent(CrmReceivableDO::getPlanId, pageReqVO.getPlanId()) + .eqIfPresent(CrmReceivableDO::getAuditStatus, pageReqVO.getAuditStatus()) + .orderByDesc(CrmReceivableDO::getId); + return selectJoinPage(pageReqVO, CrmReceivableDO.class, query); + } + + default List selectBatchIds(Collection ids, Long userId) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX<>(); + // 拼接数æ®æƒé™çš„查询æ¡ä»¶ + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE.getType(), ids, userId); + // 拼接自身的查询æ¡ä»¶ + query.selectAll(CrmReceivableDO.class).in(CrmReceivableDO::getId, ids).orderByDesc(CrmReceivableDO::getId); + return selectJoinList(CrmReceivableDO.class, query); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java new file mode 100644 index 000000000..b21d30c12 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.crm.dal.mysql.receivable; + +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.List; + +/** + * 回款计划 Mapper + * + * @author 芋é“æºç  + */ +@Mapper +public interface CrmReceivablePlanMapper extends BaseMapperX { + + default int updateOwnerUserIdById(Long id, Long ownerUserId) { + return update(new LambdaUpdateWrapper() + .eq(CrmReceivablePlanDO::getId, id) + .set(CrmReceivablePlanDO::getOwnerUserId, ownerUserId)); + } + + default PageResult selectPageByCustomerId(CrmReceivablePlanPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eq(CrmReceivablePlanDO::getCustomerId, reqVO.getCustomerId()) // 必须传递 + .eqIfPresent(CrmReceivablePlanDO::getContractId, reqVO.getContractId()) + .orderByDesc(CrmReceivablePlanDO::getId)); + } + + default PageResult selectPage(CrmReceivablePlanPageReqVO pageReqVO, Long userId) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX<>(); + // 拼接数æ®æƒé™çš„查询æ¡ä»¶ + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType(), + CrmReceivablePlanDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE); + // 拼接自身的查询æ¡ä»¶ + query.selectAll(CrmReceivablePlanDO.class) + .eqIfPresent(CrmReceivablePlanDO::getCustomerId, pageReqVO.getCustomerId()) + .eqIfPresent(CrmReceivablePlanDO::getContractId, pageReqVO.getContractId()) + .orderByDesc(CrmReceivablePlanDO::getId); + + // Backlog: 回款æ醒类型 + LocalDateTime beginOfToday = LocalDateTimeUtil.beginOfDay(LocalDateTime.now()); + LocalDateTime endOfToday = LocalDateTimeUtil.endOfDay(LocalDateTime.now()); + if (CrmReceivablePlanPageReqVO.REMIND_TYPE_NEEDED.equals(pageReqVO.getRemindType())) { // 待回款 + query.isNull(CrmReceivablePlanDO::getReceivableId) + .gt(CrmReceivablePlanDO::getReturnTime, beginOfToday) + // TODO @dhb52:这里看看怎么改æˆä¸è¦ä½¿ç”¨ to_days + .apply("to_days(return_time) <= to_days(now())+ remind_days"); + } else if (CrmReceivablePlanPageReqVO.REMIND_TYPE_EXPIRED.equals(pageReqVO.getRemindType())) { // 已逾期 + query.isNull(CrmReceivablePlanDO::getReceivableId) + .lt(CrmReceivablePlanDO::getReturnTime, endOfToday); + } else if (CrmReceivablePlanPageReqVO.REMIND_TYPE_RECEIVED.equals(pageReqVO.getRemindType())) { // 已回款 + query.isNotNull(CrmReceivablePlanDO::getReceivableId) + .gt(CrmReceivablePlanDO::getReturnTime, beginOfToday) + // TODO @dhb52:这里看看怎么改æˆä¸è¦ä½¿ç”¨ to_days + .apply("to_days(return_time) <= to_days(now()) + remind_days"); + } + + return selectJoinPage(pageReqVO, CrmReceivablePlanDO.class, query); + } + + default List selectBatchIds(Collection ids, Long userId) { + MPJLambdaWrapperX query = new MPJLambdaWrapperX<>(); + // 拼接数æ®æƒé™çš„查询æ¡ä»¶ + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType(), ids, userId); + // 拼接自身的查询æ¡ä»¶ + query.selectAll(CrmReceivablePlanDO.class).in(CrmReceivablePlanDO::getId, ids).orderByDesc(CrmReceivablePlanDO::getId); + return selectJoinList(CrmReceivablePlanDO.class, query); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmBusinessParseFunction.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmBusinessParseFunction.java new file mode 100644 index 000000000..d8bb50961 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmBusinessParseFunction.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.crm.framework.operatelog.core; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; +import com.mzt.logapi.service.IParseFunction; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * CRM 商机的 {@link IParseFunction} 实现类 + * + * @author HUIHUI + */ +@Component +@Slf4j +public class CrmBusinessParseFunction implements IParseFunction { + + public static final String NAME = "getBusinessById"; + + @Resource + private CrmBusinessService businessService; + + @Override + public boolean executeBefore() { + return true; // 先转æ¢å€¼åŽå¯¹æ¯” + } + + @Override + public String functionName() { + return NAME; + } + + @Override + public String apply(Object value) { + if (StrUtil.isEmptyIfStr(value)) { + return ""; + } + CrmBusinessDO businessDO = businessService.getBusiness(Long.parseLong(value.toString())); + return businessDO == null ? "" : businessDO.getName(); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmContactParseFunction.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmContactParseFunction.java new file mode 100644 index 000000000..91e8fd215 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmContactParseFunction.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.crm.framework.operatelog.core; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; +import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; +import com.mzt.logapi.service.IParseFunction; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * CRM è”系人的 {@link IParseFunction} 实现类 + * + * @author HUIHUI + */ +@Component +@Slf4j +public class CrmContactParseFunction implements IParseFunction { + + public static final String NAME = "getContactById"; + + @Resource + private CrmContactService contactService; + + @Override + public boolean executeBefore() { + return true; // 先转æ¢å€¼åŽå¯¹æ¯” + } + + @Override + public String functionName() { + return NAME; + } + + @Override + public String apply(Object value) { + if (StrUtil.isEmptyIfStr(value)) { + return ""; + } + CrmContactDO contactDO = contactService.getContact(Long.parseLong(value.toString())); + return contactDO == null ? "" : contactDO.getName(); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmContractParseFunction.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmContractParseFunction.java new file mode 100644 index 000000000..d3c58522e --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmContractParseFunction.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.crm.framework.operatelog.core; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; +import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; +import com.mzt.logapi.service.IParseFunction; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * CRM åˆåŒçš„ {@link IParseFunction} 实现类 + * + * @author HUIHUI + */ +@Component +@Slf4j +public class CrmContractParseFunction implements IParseFunction { + + public static final String NAME = "getContractById"; + + @Resource + private CrmContractService contractService; + + @Override + public boolean executeBefore() { + return true; // 先转æ¢å€¼åŽå¯¹æ¯” + } + + @Override + public String functionName() { + return NAME; + } + + @Override + public String apply(Object value) { + if (StrUtil.isEmptyIfStr(value)) { + return ""; + } + CrmContractDO contract = contractService.getContract(Long.parseLong(value.toString())); + return contract == null ? "" : contract.getName(); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmCustomerIndustryParseFunction.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmCustomerIndustryParseFunction.java new file mode 100644 index 000000000..ae3e0b23f --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmCustomerIndustryParseFunction.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.crm.framework.operatelog.core; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils; +import com.mzt.logapi.service.IParseFunction; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY; + +/** + * 行业的 {@link IParseFunction} 实现类 + * + * @author HUIHUI + */ +@Component +@Slf4j +public class CrmCustomerIndustryParseFunction implements IParseFunction { + + public static final String NAME = "getCustomerIndustry"; + + @Override + public boolean executeBefore() { + return true; // 先转æ¢å€¼åŽå¯¹æ¯” + } + + @Override + public String functionName() { + return NAME; + } + + @Override + public String apply(Object value) { + if (StrUtil.isEmptyIfStr(value)) { + return ""; + } + return DictFrameworkUtils.getDictDataLabel(CRM_CUSTOMER_INDUSTRY, value.toString()); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmCustomerLevelParseFunction.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmCustomerLevelParseFunction.java new file mode 100644 index 000000000..40bb6fb72 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmCustomerLevelParseFunction.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.crm.framework.operatelog.core; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils; +import com.mzt.logapi.service.IParseFunction; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_LEVEL; + +/** + * 客户等级的 {@link IParseFunction} 实现类 + * + * @author HUIHUI + */ +@Component +@Slf4j +public class CrmCustomerLevelParseFunction implements IParseFunction { + + public static final String NAME = "getCustomerLevel"; + + @Override + public boolean executeBefore() { + return true; // 先转æ¢å€¼åŽå¯¹æ¯” + } + + @Override + public String functionName() { + return NAME; + } + + @Override + public String apply(Object value) { + if (StrUtil.isEmptyIfStr(value)) { + return ""; + } + return DictFrameworkUtils.getDictDataLabel(CRM_CUSTOMER_LEVEL, value.toString()); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmCustomerParseFunction.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmCustomerParseFunction.java new file mode 100644 index 000000000..a58c0455d --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmCustomerParseFunction.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.crm.framework.operatelog.core; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; +import com.mzt.logapi.service.IParseFunction; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * CRM 客户的 {@link IParseFunction} 实现类 + * + * @author HUIHUI + */ +@Component +@Slf4j +public class CrmCustomerParseFunction implements IParseFunction { + + public static final String NAME = "getCustomerById"; + + @Resource + private CrmCustomerService customerService; + + @Override + public boolean executeBefore() { + return true; // 先转æ¢å€¼åŽå¯¹æ¯” + } + + @Override + public String functionName() { + return NAME; + } + + @Override + public String apply(Object value) { + if (StrUtil.isEmptyIfStr(value)) { + return ""; + } + CrmCustomerDO crmCustomerDO = customerService.getCustomer(Long.parseLong(value.toString())); + return crmCustomerDO == null ? "" : crmCustomerDO.getName(); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmCustomerSourceParseFunction.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmCustomerSourceParseFunction.java new file mode 100644 index 000000000..95377a88e --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmCustomerSourceParseFunction.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.crm.framework.operatelog.core; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils; +import com.mzt.logapi.service.IParseFunction; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_SOURCE; + +/** + * CRM 客户æ¥æºçš„ {@link IParseFunction} 实现类 + * + * @author HUIHUI + */ +@Component +@Slf4j +public class CrmCustomerSourceParseFunction implements IParseFunction { + + public static final String NAME = "getCustomerSource"; + + @Override + public boolean executeBefore() { + return true; // 先转æ¢å€¼åŽå¯¹æ¯” + } + + @Override + public String functionName() { + return NAME; + } + + @Override + public String apply(Object value) { + if (StrUtil.isEmptyIfStr(value)) { + return ""; + } + return DictFrameworkUtils.getDictDataLabel(CRM_CUSTOMER_SOURCE, value.toString()); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmProductStatusParseFunction.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmProductStatusParseFunction.java new file mode 100644 index 000000000..cf81f5e31 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmProductStatusParseFunction.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.crm.framework.operatelog.core; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils; +import cn.iocoder.yudao.module.crm.enums.DictTypeConstants; +import com.mzt.logapi.service.IParseFunction; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 产å“状æ€çš„ {@link IParseFunction} 实现类 + * + * @author anhaohao + */ +@Component +@Slf4j +public class CrmProductStatusParseFunction implements IParseFunction { + + public static final String NAME = "getProductStatusName"; + + @Override + public boolean executeBefore() { + return true; // 先转æ¢å€¼åŽå¯¹æ¯” + } + + @Override + public String functionName() { + return NAME; + } + + @Override + public String apply(Object value) { + if (StrUtil.isEmptyIfStr(value)) { + return ""; + } + return DictFrameworkUtils.getDictDataLabel(DictTypeConstants.CRM_PRODUCT_STATUS, value.toString()); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmProductUnitParseFunction.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmProductUnitParseFunction.java new file mode 100644 index 000000000..898bedd07 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmProductUnitParseFunction.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.crm.framework.operatelog.core; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils; +import cn.iocoder.yudao.module.crm.enums.DictTypeConstants; +import com.mzt.logapi.service.IParseFunction; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 产å“å•ä½çš„ {@link IParseFunction} 实现类 + * + * @author anhaohao + */ +@Component +@Slf4j +public class CrmProductUnitParseFunction implements IParseFunction { + + public static final String NAME = "getProductUnitName"; + + @Override + public boolean executeBefore() { + return true; // 先转æ¢å€¼åŽå¯¹æ¯” + } + + @Override + public String functionName() { + return NAME; + } + + @Override + public String apply(Object value) { + if (StrUtil.isEmptyIfStr(value)) { + return ""; + } + return DictFrameworkUtils.getDictDataLabel(DictTypeConstants.CRM_PRODUCT_UNIT, value.toString()); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/SysAdminUserParseFunction.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/SysAdminUserParseFunction.java new file mode 100644 index 000000000..224638957 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/SysAdminUserParseFunction.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.crm.framework.operatelog.core; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import com.mzt.logapi.service.IParseFunction; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 管ç†å‘˜åå­—çš„ {@link IParseFunction} 实现类 + * + * @author HUIHUI + */ +@Slf4j +@Component +public class SysAdminUserParseFunction implements IParseFunction { + + public static final String NAME = "getAdminUserById"; + + @Resource + private AdminUserApi adminUserApi; + + @Override + public String functionName() { + return NAME; + } + + @Override + public String apply(Object value) { + if (StrUtil.isEmptyIfStr(value)) { + return ""; + } + + // 获å–ç”¨æˆ·ä¿¡æ¯ + AdminUserRespDTO user = adminUserApi.getUser(Long.parseLong(value.toString())).getCheckedData(); + if (user == null) { + log.warn("[apply][获å–用户{{}}为空", value); + return ""; + } + // è¿”å›žæ ¼å¼ èŠ‹é“æºç (13888888888) + String nickname = user.getNickname(); + if (StrUtil.isEmpty(user.getMobile())) { + return nickname; + } + return StrUtil.format("{}({})", nickname, user.getMobile()); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/SysAreaParseFunction.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/SysAreaParseFunction.java new file mode 100644 index 000000000..3ccc76912 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/SysAreaParseFunction.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.crm.framework.operatelog.core; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; +import com.mzt.logapi.service.IParseFunction; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 地åçš„ {@link IParseFunction} 实现类 + * + * @author HUIHUI + */ +@Slf4j +@Component +public class SysAreaParseFunction implements IParseFunction { + + public static final String NAME = "getArea"; + + @Override + public boolean executeBefore() { + return true; // 先转æ¢å€¼åŽå¯¹æ¯” + } + + @Override + public String functionName() { + return NAME; + } + + @Override + public String apply(Object value) { + if (StrUtil.isEmptyIfStr(value)) { + return ""; + } + return AreaUtils.format(Integer.parseInt(value.toString())); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/SysBooleanParseFunction.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/SysBooleanParseFunction.java new file mode 100644 index 000000000..3e1000bf9 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/SysBooleanParseFunction.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.crm.framework.operatelog.core; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils; +import cn.iocoder.yudao.module.infra.enums.DictTypeConstants; +import com.mzt.logapi.service.IParseFunction; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 是å¦ç±»åž‹çš„ {@link IParseFunction} 实现类 + * + * @author HUIHUI + */ +@Component +@Slf4j +public class SysBooleanParseFunction implements IParseFunction { + + public static final String NAME = "getBoolean"; + + @Override + public boolean executeBefore() { + return true; // 先转æ¢å€¼åŽå¯¹æ¯” + } + + @Override + public String functionName() { + return NAME; + } + + @Override + public String apply(Object value) { + if (StrUtil.isEmptyIfStr(value)) { + return ""; + } + return DictFrameworkUtils.getDictDataLabel(DictTypeConstants.BOOLEAN_STRING, value.toString()); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/SysDeptParseFunction.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/SysDeptParseFunction.java new file mode 100644 index 000000000..5b002c9ce --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/SysDeptParseFunction.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.crm.framework.operatelog.core; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.system.api.dept.DeptApi; +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; +import com.mzt.logapi.service.IParseFunction; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 管ç†å‘˜åå­—çš„ {@link IParseFunction} 实现类 + * + * @author HUIHUI + */ +@Slf4j +@Component +public class SysDeptParseFunction implements IParseFunction { + + public static final String NAME = "getDeptById"; + + @Resource + private DeptApi deptApi; + + @Override + public String functionName() { + return NAME; + } + + @Override + public String apply(Object value) { + if (StrUtil.isEmptyIfStr(value)) { + return ""; + } + + // 获å–éƒ¨é—¨ä¿¡æ¯ + DeptRespDTO dept = deptApi.getDept(Long.parseLong(value.toString())).getCheckedData(); + if (dept == null) { + log.warn("[apply][获å–部门{{}}为空", value); + return ""; + } + return dept.getName(); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/SysSexParseFunction.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/SysSexParseFunction.java new file mode 100644 index 000000000..ccff080a2 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/SysSexParseFunction.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.crm.framework.operatelog.core; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils; +import cn.iocoder.yudao.module.system.enums.DictTypeConstants; +import com.mzt.logapi.service.IParseFunction; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 行业的 {@link IParseFunction} 实现类 + * + * @author HUIHUI + */ +@Component +@Slf4j +public class SysSexParseFunction implements IParseFunction { + + public static final String NAME = "getSex"; + + @Override + public boolean executeBefore() { + return true; // 先转æ¢å€¼åŽå¯¹æ¯” + } + + @Override + public String functionName() { + return NAME; + } + + @Override + public String apply(Object value) { + if (StrUtil.isEmptyIfStr(value)) { + return ""; + } + return DictFrameworkUtils.getDictDataLabel(DictTypeConstants.USER_SEX, value.toString()); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/package-info.java new file mode 100644 index 000000000..975a2eb51 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.yudao.module.crm.framework.operatelog; \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/package-info.java new file mode 100644 index 000000000..281e36c45 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/package-info.java @@ -0,0 +1,6 @@ +/** + * 属于 crm 模å—çš„ framework å°è£… + * + * @author 芋é“æºç  + */ +package cn.iocoder.yudao.module.crm.framework; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/annotations/CrmPermission.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/annotations/CrmPermission.java new file mode 100644 index 000000000..9ef6d4d02 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/annotations/CrmPermission.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.crm.framework.permission.core.annotations; + +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.METHOD; + +/** + * CRM æ•°æ®æ“作æƒé™æ ¡éªŒ AOP 注解 + * + * @author HUIHUI + */ +@Target({METHOD, ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface CrmPermission { + + /** + * CRM 类型 + */ + CrmBizTypeEnum[] bizType() default {}; + + /** + * CRM 类型扩展,通过 Spring EL 表达å¼èŽ·å–到 {@link #bizType()} + * + * 目的:用于 CrmPermissionController 团队æƒé™æ ¡éªŒ + */ + String bizTypeValue() default ""; + + /** + * æ•°æ®ç¼–å·ï¼Œé€šè¿‡ Spring EL 表达å¼èŽ·å– + */ + String bizId(); + + /** + * æ“作所需æƒé™çº§åˆ« + */ + CrmPermissionLevelEnum level(); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/aop/CrmPermissionAspect.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/aop/CrmPermissionAspect.java new file mode 100644 index 000000000..14e7c71fe --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/aop/CrmPermissionAspect.java @@ -0,0 +1,130 @@ +package cn.iocoder.yudao.module.crm.framework.permission.core.aop; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils; +import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; +import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; +import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; +import cn.iocoder.yudao.module.crm.framework.permission.core.util.CrmPermissionUtils; +import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.stereotype.Component; + +import java.util.*; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CRM_PERMISSION_DENIED; + +/** + * Crm æ•°æ®æƒé™æ ¡éªŒ AOP åˆ‡é¢ + * + * @author HUIHUI + */ +@Component +@Aspect +@Slf4j +public class CrmPermissionAspect { + + @Resource + private CrmPermissionService crmPermissionService; + + @Before("@annotation(crmPermission)") + public void doBefore(JoinPoint joinPoint, CrmPermission crmPermission) { + // 1.1 获å–相关属性值 + Map expressionValues = parseExpressions(joinPoint, crmPermission); + Integer bizType = StrUtil.isEmpty(crmPermission.bizTypeValue()) ? + crmPermission.bizType()[0].getType() : (Integer) expressionValues.get(crmPermission.bizTypeValue()); // 模å—类型 + // 1.2 处ç†å…¼å®¹å¤šä¸ª bizId 的情况 + Object object = expressionValues.get(crmPermission.bizId()); // 模å—æ•°æ®ç¼–å· + Set bizIds = new HashSet<>(); + if (object instanceof Collection) { + bizIds.addAll(convertSet((Collection) object, item -> Long.parseLong(item.toString()))); + } else { + bizIds.add(Long.parseLong(object.toString())); + } + Integer permissionLevel = crmPermission.level().getLevel(); // 需è¦çš„æƒé™çº§åˆ« + + // 2. é€ä¸ªæ ¡éªŒæƒé™ + List permissionList = crmPermissionService.getPermissionListByBiz(bizType, bizIds); + Map> multiMap = convertMultiMap(permissionList, CrmPermissionDO::getBizId); + bizIds.forEach(bizId -> validatePermission(bizType, multiMap.get(bizId), permissionLevel)); + } + + private void validatePermission(Integer bizType, List bizPermissions, Integer permissionLevel) { + // 1. 如果是超级管ç†å‘˜åˆ™ç›´æŽ¥é€šè¿‡ + if (CrmPermissionUtils.isCrmAdmin()) { + return; + } + // 1.1 没有数æ®æƒé™çš„情况 + if (CollUtil.isEmpty(bizPermissions)) { + // 公海数æ®å¦‚果没有团队æˆå‘˜å¤§å®¶ä¹Ÿå› è¯¥æœ‰è¯»æƒé™æ‰å¯¹ + if (CrmPermissionLevelEnum.isRead(permissionLevel)) { + return; + } + // 没有数æ®æƒé™çš„情况下超出了读æƒé™ç›´æŽ¥æŠ¥é”™ï¼Œé¿å…åŽé¢æ ¡éªŒç©ºæŒ‡é’ˆ + throw exception(CRM_PERMISSION_DENIED, CrmBizTypeEnum.getNameByType(bizType)); + } else { // 1.2 有数æ®æƒé™ä½†æ˜¯æ²¡æœ‰è´Ÿè´£äººçš„情况 + if (!anyMatch(bizPermissions, item -> CrmPermissionLevelEnum.isOwner(item.getLevel()))) { + if (CrmPermissionLevelEnum.isRead(permissionLevel)) { + return; + } + } + } + + // 2.1 情况一:如果自己是负责人,则默认有所有æƒé™ + CrmPermissionDO userPermission = CollUtil.findOne(bizPermissions, permission -> ObjUtil.equal(permission.getUserId(), getUserId())); + if (userPermission != null) { + if (CrmPermissionLevelEnum.isOwner(userPermission.getLevel())) { + return; + } + // 2.2 情况二:校验自己是å¦æœ‰è¯»æƒé™ + if (CrmPermissionLevelEnum.isRead(permissionLevel)) { + if (CrmPermissionLevelEnum.isRead(userPermission.getLevel()) // 校验当å‰ç”¨æˆ·æ˜¯å¦æœ‰è¯»æƒé™ + || CrmPermissionLevelEnum.isWrite(userPermission.getLevel())) { // 校验当å‰ç”¨æˆ·æ˜¯å¦æœ‰å†™æƒé™ + return; + } + } + // 2.3 情况三:校验自己是å¦æœ‰å†™æƒé™ + if (CrmPermissionLevelEnum.isWrite(permissionLevel)) { + if (CrmPermissionLevelEnum.isWrite(userPermission.getLevel())) { // 校验当å‰ç”¨æˆ·æ˜¯å¦æœ‰å†™æƒé™ + return; + } + } + } + // 2.4 没有æƒé™ï¼ŒæŠ›å‡ºå¼‚常 + log.info("[doBefore][userId({}) è¦æ±‚æƒé™({}) 实际æƒé™({}) æ•°æ®æ ¡éªŒé”™è¯¯]", // 打个 info 日志,方便åŽç»­æŽ’查问题ã€å®¡è®¡ + getUserId(), permissionLevel, toJsonString(userPermission)); + throw exception(CRM_PERMISSION_DENIED, CrmBizTypeEnum.getNameByType(bizType)); + } + + /** + * èŽ·å¾—ç”¨æˆ·ç¼–å· + * + * @return ç”¨æˆ·ç¼–å· + */ + private static Long getUserId() { + return WebFrameworkUtils.getLoginUserId(); + } + + private static Map parseExpressions(JoinPoint joinPoint, CrmPermission crmPermission) { + // 1. 需è¦è§£æžçš„è¡¨è¾¾å¼ + List expressionStrings = new ArrayList<>(2); + expressionStrings.add(crmPermission.bizId()); + if (StrUtil.isNotEmpty(crmPermission.bizTypeValue())) { // 为空则表示 bizType 有值 + expressionStrings.add(crmPermission.bizTypeValue()); + } + // 2. æ‰§è¡Œè§£æž + return SpringExpressionUtils.parseExpressions(joinPoint, expressionStrings); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/package-info.java new file mode 100644 index 000000000..d895fe969 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.yudao.module.crm.framework.permission.core; \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/util/CrmPermissionUtils.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/util/CrmPermissionUtils.java new file mode 100644 index 000000000..5beb5700c --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/util/CrmPermissionUtils.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.crm.framework.permission.core.util; + +import cn.hutool.extra.spring.SpringUtil; +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionRoleCodeEnum; +import cn.iocoder.yudao.module.system.api.permission.PermissionApi; + +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +/** + * æ•°æ®æƒé™å·¥å…·ç±» + * + * @author HUIHUI + */ +public class CrmPermissionUtils { + + /** + * 校验用户是å¦æ˜¯ CRM 管ç†å‘˜ + * + * @return 是/å¦ + */ + public static boolean isCrmAdmin() { + return SingletonManager.getPermissionApi().hasAnyRoles(getLoginUserId(), CrmPermissionRoleCodeEnum.CRM_ADMIN.getCode()).getCheckedData(); + } + + /** + * é™æ€å†…部类实现å•ä¾‹èŽ·å– + * + * @author HUIHUI + */ + private static class SingletonManager { + + private static final PermissionApi PERMISSION_API = SpringUtil.getBean(PermissionApi.class); + + public static PermissionApi getPermissionApi() { + return PERMISSION_API; + } + + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/package-info.java new file mode 100644 index 000000000..44f408016 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.yudao.module.crm.framework.permission; \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/rpc/config/RpcConfiguration.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/rpc/config/RpcConfiguration.java new file mode 100644 index 000000000..d36e50519 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/rpc/config/RpcConfiguration.java @@ -0,0 +1,14 @@ +package cn.iocoder.yudao.module.crm.framework.rpc.config; + +import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi; +import cn.iocoder.yudao.module.system.api.dept.DeptApi; +import cn.iocoder.yudao.module.system.api.dept.PostApi; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +@EnableFeignClients(clients = {AdminUserApi.class, DeptApi.class, PostApi.class, + BpmProcessInstanceApi.class}) +public class RpcConfiguration { +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/rpc/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/rpc/package-info.java new file mode 100644 index 000000000..79ee21420 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/rpc/package-info.java @@ -0,0 +1,4 @@ +/** + * å ä½ + */ +package cn.iocoder.yudao.module.crm.framework.rpc; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/security/config/SecurityConfiguration.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/security/config/SecurityConfiguration.java new file mode 100644 index 000000000..1ed002c22 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/security/config/SecurityConfiguration.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.crm.framework.security.config; + +import cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer; +import cn.iocoder.yudao.module.crm.enums.ApiConstants; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer; + +/** + * Crm 模å—çš„ Security é…ç½® + */ +@Configuration("crmSecurityConfiguration") +public class SecurityConfiguration { + + @Bean("crmAuthorizeRequestsCustomizer") + public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() { + return new AuthorizeRequestsCustomizer() { + + @Override + public void customize(AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry registry) { + // Swagger 接å£æ–‡æ¡£ + registry.requestMatchers("/v3/api-docs/**").permitAll() // å…ƒæ•°æ® + .requestMatchers("/swagger-ui.html").permitAll(); // Swagger UI + // Spring Boot Actuator 的安全é…ç½® + registry.requestMatchers("/actuator").permitAll() + .requestMatchers("/actuator/**").permitAll(); + // Druid 监控 + registry.requestMatchers("/druid/**").permitAll(); + // RPC æœåŠ¡çš„安全é…ç½® + registry.requestMatchers(ApiConstants.PREFIX + "/**").permitAll(); + } + + }; + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/security/core/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/security/core/package-info.java new file mode 100644 index 000000000..1e6b880ae --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/security/core/package-info.java @@ -0,0 +1,4 @@ +/** + * å ä½ + */ +package cn.iocoder.yudao.module.crm.framework.security.core; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/job/customer/CrmCustomerAutoPutPoolJob.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/job/customer/CrmCustomerAutoPutPoolJob.java new file mode 100644 index 000000000..5e0aac646 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/job/customer/CrmCustomerAutoPutPoolJob.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.crm.job.customer; + +import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; +import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; +import com.xxl.job.core.handler.annotation.XxlJob; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +/** + * 客户自动掉入公海 Job + * + * @author 芋é“æºç  + */ +@Component +public class CrmCustomerAutoPutPoolJob { + + @Resource + private CrmCustomerService customerService; + + @XxlJob("customerAutoPutPoolJob") + @TenantJob + public String execute() { + int count = customerService.autoPutCustomerPool(); + return String.format("掉入公海客户 %s 个", count); + } + +} \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/job/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/job/package-info.java new file mode 100644 index 000000000..144f64d02 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/job/package-info.java @@ -0,0 +1,4 @@ +/** + * TODO 芋艿:临时å ä½ï¼ŒåŽç»­å¯åˆ é™¤ + */ +package cn.iocoder.yudao.module.crm.job; \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/package-info.java new file mode 100644 index 000000000..2ea5f9f41 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/package-info.java @@ -0,0 +1,10 @@ +/** + * crm 包下,客户关系管ç†ï¼ˆCustomer Relationship Management)。 + * 例如说:客户ã€è”系人ã€å•†æœºã€åˆåŒã€å›žæ¬¾ç­‰ç­‰ + * + * 1. Controller URL:以 /crm/ 开头,é¿å…和其它 Module å†²çª + * 2. DataObject 表å:以 crm_ 开头,方便在数æ®åº“中区分 + * + * 注æ„,由于 Crm 模å—下,容易和其它模å—é‡å,所以类å都加载 Crm çš„å‰ç¼€~ + */ +package cn.iocoder.yudao.module.crm; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/bi/CrmBiRankingService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/bi/CrmBiRankingService.java new file mode 100644 index 000000000..2ff28d385 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/bi/CrmBiRankingService.java @@ -0,0 +1,80 @@ +package cn.iocoder.yudao.module.crm.service.bi; + + +import cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRanKRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRankReqVO; + +import java.util.List; + +/** + * CRM BI 排行榜 Service æŽ¥å£ + * + * @author anhaohao + */ +public interface CrmBiRankingService { + + /** + * 获得åˆåŒé‡‘é¢æŽ’行榜 + * + * @param rankReqVO 排行å‚æ•° + * @return åˆåŒé‡‘é¢æŽ’行榜 + */ + List getContractPriceRank(CrmBiRankReqVO rankReqVO); + + /** + * 获得回款金é¢æŽ’行榜 + * + * @param rankReqVO 排行å‚æ•° + * @return 回款金é¢æŽ’行榜 + */ + List getReceivablePriceRank(CrmBiRankReqVO rankReqVO); + + /** + * 获得签约åˆåŒæ•°é‡æŽ’行榜 + * + * @param rankReqVO 排行å‚æ•° + * @return 签约åˆåŒæ•°é‡æŽ’行榜 + */ + List getContractCountRank(CrmBiRankReqVO rankReqVO); + + /** + * 获得产å“销é‡æŽ’行榜 + * + * @param rankReqVO 排行å‚æ•° + * @return 产å“销é‡æŽ’行榜 + */ + List getProductSalesRank(CrmBiRankReqVO rankReqVO); + + /** + * 获得新增客户数排行榜 + * + * @param rankReqVO 排行å‚æ•° + * @return 新增客户数排行榜 + */ + List getCustomerCountRank(CrmBiRankReqVO rankReqVO); + + /** + * 获得è”系人数é‡æŽ’行榜 + * + * @param rankReqVO 排行å‚æ•° + * @return è”系人数é‡æŽ’行榜 + */ + List getContactsCountRank(CrmBiRankReqVO rankReqVO); + + /** + * 获得跟进次数排行榜 + * + * @param rankReqVO 排行å‚æ•° + * @return 跟进次数排行榜 + */ + List getFollowCountRank(CrmBiRankReqVO rankReqVO); + + /** + * 获得跟进客户数排行榜 + * + * @param rankReqVO 排行å‚æ•° + * @return 跟进客户数排行榜 + */ + List getFollowCustomerCountRank(CrmBiRankReqVO rankReqVO); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/bi/CrmBiRankingServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/bi/CrmBiRankingServiceImpl.java new file mode 100644 index 000000000..171132669 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/bi/CrmBiRankingServiceImpl.java @@ -0,0 +1,134 @@ +package cn.iocoder.yudao.module.crm.service.bi; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRanKRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.bi.vo.CrmBiRankReqVO; +import cn.iocoder.yudao.module.crm.dal.mysql.bi.CrmBiRankingMapper; +import cn.iocoder.yudao.module.system.api.dept.DeptApi; +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; + +/** + * CRM BI 排行榜 Service 实现类 + * + * @author anhaohao + */ +@Service +@Validated +public class CrmBiRankingServiceImpl implements CrmBiRankingService { + + @Resource + private CrmBiRankingMapper biRankingMapper; + + @Resource + private AdminUserApi adminUserApi; + @Resource + private DeptApi deptApi; + + @Override + public List getContractPriceRank(CrmBiRankReqVO rankReqVO) { + return getRank(rankReqVO, biRankingMapper::selectContractPriceRank); + } + + @Override + public List getReceivablePriceRank(CrmBiRankReqVO rankReqVO) { + return getRank(rankReqVO, biRankingMapper::selectReceivablePriceRank); + } + + @Override + public List getContractCountRank(CrmBiRankReqVO rankReqVO) { + return getRank(rankReqVO, biRankingMapper::selectContractCountRank); + } + + @Override + public List getProductSalesRank(CrmBiRankReqVO rankReqVO) { + return getRank(rankReqVO, biRankingMapper::selectProductSalesRank); + } + + @Override + public List getCustomerCountRank(CrmBiRankReqVO rankReqVO) { + return getRank(rankReqVO, biRankingMapper::selectCustomerCountRank); + } + + @Override + public List getContactsCountRank(CrmBiRankReqVO rankReqVO) { + return getRank(rankReqVO, biRankingMapper::selectContactsCountRank); + } + + @Override + public List getFollowCountRank(CrmBiRankReqVO rankReqVO) { + return getRank(rankReqVO, biRankingMapper::selectFollowCountRank); + } + + @Override + public List getFollowCustomerCountRank(CrmBiRankReqVO rankReqVO) { + return getRank(rankReqVO, biRankingMapper::selectFollowCustomerCountRank); + } + + /** + * èŽ·å¾—æŽ’è¡Œç‰ˆæ•°æ® + * + * @param rankReqVO å‚æ•° + * @param rankFunction 排行榜方法 + * @return æŽ’è¡Œç‰ˆæ•°æ® + */ + private List getRank(CrmBiRankReqVO rankReqVO, Function> rankFunction) { + // 1. 获得用户编å·æ•°ç»„ + rankReqVO.setUserIds(getUserIds(rankReqVO.getDeptId())); + if (CollUtil.isEmpty(rankReqVO.getUserIds())) { + return Collections.emptyList(); + } + // 2. èŽ·å¾—æŽ’è¡Œæ•°æ® + List ranks = rankFunction.apply(rankReqVO); + if (CollUtil.isEmpty(ranks)) { + return Collections.emptyList(); + } + ranks.sort(Comparator.comparing(CrmBiRanKRespVO::getCount).reversed()); + // 3. æ‹¼æŽ¥ç”¨æˆ·ä¿¡æ¯ + appendUserInfo(ranks); + return ranks; + } + + /** + * 拼接用户信æ¯ï¼ˆæ˜µç§°ã€éƒ¨é—¨ï¼‰ + * + * @param ranks æŽ’è¡Œæ¦œæ•°æ® + */ + private void appendUserInfo(List ranks) { + Map userMap = adminUserApi.getUserMap(convertSet(ranks, CrmBiRanKRespVO::getOwnerUserId)); + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); + ranks.forEach(rank -> MapUtils.findAndThen(userMap, rank.getOwnerUserId(), user -> { + rank.setNickname(user.getNickname()); + MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> rank.setDeptName(dept.getName())); + })); + } + + /** + * 获得部门下的用户编å·æ•°ç»„,包括å­éƒ¨é—¨çš„ + * + * @param deptId éƒ¨é—¨ç¼–å· + * @return 用户编å·æ•°ç»„ + */ + public List getUserIds(Long deptId) { + // 1. 获得部门列表 + List deptIds = convertList(deptApi.getChildDeptList(deptId).getCheckedData(), DeptRespDTO::getId); + deptIds.add(deptId); + // 2. èŽ·å¾—ç”¨æˆ·ç¼–å· + return convertList(adminUserApi.getUserListByDeptIds(deptIds).getCheckedData(), AdminUserRespDTO::getId); + } + +} \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessService.java new file mode 100644 index 000000000..683070d02 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessService.java @@ -0,0 +1,132 @@ +package cn.iocoder.yudao.module.crm.service.business; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessSaveReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.service.business.bo.CrmBusinessUpdateProductReqBO; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; + +/** + * 商机 Service æŽ¥å£ + * + * @author ljlleo + */ +public interface CrmBusinessService { + + /** + * 创建商机 + * + * @param createReqVO åˆ›å»ºä¿¡æ¯ + * @param userId ç”¨æˆ·ç¼–å· + * @return ç¼–å· + */ + Long createBusiness(@Valid CrmBusinessSaveReqVO createReqVO, Long userId); + + /** + * 更新商机 + * + * @param updateReqVO æ›´æ–°ä¿¡æ¯ + */ + void updateBusiness(@Valid CrmBusinessSaveReqVO updateReqVO); + + /** + * æ›´æ–°å•†æœºç›¸å…³è·Ÿè¿›ä¿¡æ¯ + * + * @param updateFollowUpReqBOList è·Ÿè¿›ä¿¡æ¯ + */ + void updateBusinessFollowUpBatch(List updateFollowUpReqBOList); + + /** + * 删除商机 + * + * @param id ç¼–å· + */ + void deleteBusiness(Long id); + + /** + * 商机转移 + * + * @param reqVO 请求 + * @param userId ç”¨æˆ·ç¼–å· + */ + void transferBusiness(CrmBusinessTransferReqVO reqVO, Long userId); + + /** + * 更新商机关è”å•†å“ + * + * @param updateProductReqBO 请求 + */ + void updateBusinessProduct(CrmBusinessUpdateProductReqBO updateProductReqBO); + + /** + * 获得商机 + * + * @param id ç¼–å· + * @return 商机 + */ + CrmBusinessDO getBusiness(Long id); + + /** + * 获得商机列表 + * + * @param ids ç¼–å· + * @return 商机列表 + */ + List getBusinessList(Collection ids, Long userId); + + /** + * 获得商机列表 + * + * @param ids ç¼–å· + * @return 商机列表 + */ + List getBusinessList(Collection ids); + + /** + * 获得商机分页 + * + * æ•°æ®æƒé™ï¼šåŸºäºŽ {@link CrmBusinessDO} + * + * @param pageReqVO 分页查询 + * @param userId ç”¨æˆ·ç¼–å· + * @return 商机分页 + */ + PageResult getBusinessPage(CrmBusinessPageReqVO pageReqVO, Long userId); + + /** + * 获得商机分页,基于指定客户 + * + * æ•°æ®æƒé™ï¼šåŸºäºŽ {@link CrmCustomerDO} è¯»å– + * + * @param pageReqVO 分页查询 + * @return 商机分页 + */ + PageResult getBusinessPageByCustomerId(CrmBusinessPageReqVO pageReqVO); + + /** + * 获得商机分页,基于指定è”系人 + * + * æ•°æ®æƒé™ï¼šåŸºäºŽ {@link CrmContactDO} è¯»å– + * + * @param pageReqVO 分页å‚æ•° + * @return 商机分页 + */ + PageResult getBusinessPageByContact(CrmBusinessPageReqVO pageReqVO); + + /** + * 获å–å…³è”å®¢æˆ·çš„å•†æœºæ•°é‡ + * + * @param customerId å®¢æˆ·ç¼–å· + * @return æ•°é‡ + */ + Long getBusinessCountByCustomerId(Long customerId); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java new file mode 100644 index 000000000..535578fd2 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java @@ -0,0 +1,298 @@ +package cn.iocoder.yudao.module.crm.service.business; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessSaveReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO; +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.dal.dataobject.business.CrmBusinessProductDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; +import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessMapper; +import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessProductMapper; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; +import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; +import cn.iocoder.yudao.module.crm.service.business.bo.CrmBusinessUpdateProductReqBO; +import cn.iocoder.yudao.module.crm.service.contact.CrmContactBusinessService; +import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; +import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; +import cn.iocoder.yudao.module.crm.service.product.CrmProductService; +import com.mzt.logapi.context.LogRecordContext; +import com.mzt.logapi.service.impl.DiffParseFunction; +import com.mzt.logapi.starter.annotation.LogRecord; +import jakarta.annotation.Resource; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; + +/** + * 商机 Service 实现类 + * + * @author ljlleo + */ +@Service +@Validated +public class CrmBusinessServiceImpl implements CrmBusinessService { + + @Resource + private CrmBusinessMapper businessMapper; + @Resource + private CrmBusinessProductMapper businessProductMapper; + + @Resource + @Lazy // 延迟加载,é¿å…循环ä¾èµ– + private CrmContractService contractService; + @Resource + private CrmPermissionService permissionService; + @Resource + private CrmContactBusinessService contactBusinessService; + @Resource + private CrmProductService productService; + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_CREATE_SUB_TYPE, bizNo = "{{#business.id}}", + success = CRM_BUSINESS_CREATE_SUCCESS) + public Long createBusiness(CrmBusinessSaveReqVO createReqVO, Long userId) { + createReqVO.setId(null); + // 1. æ’入商机 + CrmBusinessDO business = BeanUtils.toBean(createReqVO, CrmBusinessDO.class).setOwnerUserId(userId); + businessMapper.insert(business); + // 1.2 æ’入商机关è”å•†å“ + if (CollUtil.isNotEmpty(createReqVO.getProductItems())) { // å¦‚æžœæœ‰çš„è¯ + List productList = buildBusinessProductList(createReqVO.getProductItems(), business.getId()); + businessProductMapper.insertBatch(productList); + // æ›´æ–°åˆåŒå•†å“æ€»é‡‘é¢ + businessMapper.updateById(new CrmBusinessDO().setId(business.getId()).setProductPrice( + getSumValue(productList, CrmBusinessProductDO::getTotalPrice, Integer::sum))); + } + // TODO @puhui999:在è”系人的详情页,如果直接ã€æ–°å»ºå•†æœºã€‘,则需è¦å…³è”下。这里è¦æžä¸ª CrmContactBusinessDO 表 + createContactBusiness(business.getId(), createReqVO.getContactId()); + + // 2. 创建数æ®æƒé™ + // 设置当å‰æ“作的人为负责人 + permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_BUSINESS.getType()) + .setBizId(business.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); + + // 3. 记录æ“作日志上下文 + LogRecordContext.putVariable("business", business); + return business.getId(); + } + + // TODO @lzxhqs:CrmContactBusinessService 调用这个;这样逻辑æ‰èƒ½æ”¶æ•›å“ˆï¼› + private void createContactBusiness(Long businessId, Long contactId) { + CrmContactBusinessDO contactBusiness = new CrmContactBusinessDO(); + contactBusiness.setBusinessId(businessId); + contactBusiness.setContactId(contactId); + contactBusinessService.insert(contactBusiness); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", + success = CRM_BUSINESS_UPDATE_SUCCESS) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE) + public void updateBusiness(CrmBusinessSaveReqVO updateReqVO) { + // 1. 校验存在 + CrmBusinessDO oldBusiness = validateBusinessExists(updateReqVO.getId()); + + // 2.1 更新商机 + CrmBusinessDO updateObj = BeanUtils.toBean(updateReqVO, CrmBusinessDO.class); + businessMapper.updateById(updateObj); + // 2.2 更新商机关è”å•†å“ + List productList = buildBusinessProductList(updateReqVO.getProductItems(), updateObj.getId()); + updateBusinessProduct(productList, updateObj.getId()); + + // TODO @商机待定:如果状æ€å‘生å˜åŒ–,æ’入商机状æ€å˜æ›´è®°å½•è¡¨ + // 3. 记录æ“作日志上下文 + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldBusiness, CrmBusinessSaveReqVO.class)); + LogRecordContext.putVariable("businessName", oldBusiness.getName()); + } + + @Override + public void updateBusinessFollowUpBatch(List updateFollowUpReqBOList) { + businessMapper.updateBatch(CrmBusinessConvert.INSTANCE.convertList(updateFollowUpReqBOList)); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_DELETE_SUB_TYPE, bizNo = "{{#id}}", + success = CRM_BUSINESS_DELETE_SUCCESS) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) + public void deleteBusiness(Long id) { + // 校验存在 + CrmBusinessDO business = validateBusinessExists(id); + // TODO @商机待定:需è¦æ ¡éªŒæœ‰æ²¡å…³è”åˆåŒã€‚CrmContractDO çš„ businessId 字段 + validateContractExists(id); + + // 删除 + businessMapper.deleteById(id); + // 删除数æ®æƒé™ + permissionService.deletePermission(CrmBizTypeEnum.CRM_BUSINESS.getType(), id); + + // 记录æ“作日志上下文 + LogRecordContext.putVariable("businessName", business.getName()); + } + + private void updateBusinessProduct(List newProductList, Long businessId) { + List oldProducts = businessProductMapper.selectListByBusinessId(businessId); + List> diffList = CollectionUtils.diffList(oldProducts, newProductList, (oldValue, newValue) -> { + boolean condition = ObjectUtil.equal(oldValue.getProductId(), newValue.getProductId()); + if (condition) { + newValue.setId(oldValue.getId()); // 更新需è¦åŽŸå§‹ç¼–å· + } + return condition; + }); + if (CollUtil.isNotEmpty(diffList.get(0))) { + businessProductMapper.insertBatch(diffList.get(0)); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + businessProductMapper.updateBatch(diffList.get(1)); + } + if (CollUtil.isNotEmpty(diffList.get(2))) { + businessProductMapper.deleteBatchIds(convertSet(diffList.get(2), CrmBusinessProductDO::getId)); + } + } + + private List buildBusinessProductList(List productItems, + Long businessId) { + // 校验商å“存在 + Set productIds = convertSet(productItems, CrmBusinessSaveReqVO.CrmBusinessProductItem::getId); + List productList = productService.getProductList(productIds); + if (CollUtil.isEmpty(productIds) || productList.size() != productIds.size()) { + throw exception(PRODUCT_NOT_EXISTS); + } + Map productMap = convertMap(productList, CrmProductDO::getId); + return convertList(productItems, productItem -> { + CrmProductDO product = productMap.get(productItem.getId()); + return BeanUtils.toBean(product, CrmBusinessProductDO.class) + .setId(null).setProductId(productItem.getId()).setBusinessId(businessId) + .setCount(productItem.getCount()).setDiscountPercent(productItem.getDiscountPercent()) + .setTotalPrice(MoneyUtils.calculator(product.getPrice(), productItem.getCount(), productItem.getDiscountPercent())); + }); + } + + /** + * 删除校验åˆåŒæ˜¯å…³è”åˆåŒ + * + * @param businessId 商机id + * @author lzxhqs + */ + private void validateContractExists(Long businessId) { + if (contractService.getContractCountByBusinessId(businessId) > 0) { + throw exception(BUSINESS_CONTRACT_EXISTS); + } + } + + private CrmBusinessDO validateBusinessExists(Long id) { + CrmBusinessDO crmBusiness = businessMapper.selectById(id); + if (crmBusiness == null) { + throw exception(BUSINESS_NOT_EXISTS); + } + return crmBusiness; + } + + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.id}}", + success = CRM_BUSINESS_TRANSFER_SUCCESS) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER) + public void transferBusiness(CrmBusinessTransferReqVO reqVO, Long userId) { + // 1 校验商机是å¦å­˜åœ¨ + CrmBusinessDO business = validateBusinessExists(reqVO.getId()); + + // 2.1 æ•°æ®æƒé™è½¬ç§» + permissionService.transferPermission( + CrmBusinessConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_BUSINESS.getType())); + // 2.2 设置新的负责人 + businessMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId()); + + // 记录æ“作日志上下文 + LogRecordContext.putVariable("business", business); + } + + @Override + public void updateBusinessProduct(CrmBusinessUpdateProductReqBO updateProductReqBO) { + // 更新商机关è”å•†å“ + List productList = buildBusinessProductList( + BeanUtils.toBean(updateProductReqBO.getProductItems(), CrmBusinessSaveReqVO.CrmBusinessProductItem.class), updateProductReqBO.getId()); + updateBusinessProduct(productList, updateProductReqBO.getId()); + } + + //======================= 查询相关 ======================= + + @Override + @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#id", level = CrmPermissionLevelEnum.READ) + public CrmBusinessDO getBusiness(Long id) { + return businessMapper.selectById(id); + } + + @Override + public List getBusinessList(Collection ids, Long userId) { + if (CollUtil.isEmpty(ids)) { + return ListUtil.empty(); + } + return businessMapper.selectBatchIds(ids, userId); + } + + @Override + public List getBusinessList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return ListUtil.empty(); + } + return businessMapper.selectBatchIds(ids); + } + + @Override + public PageResult getBusinessPage(CrmBusinessPageReqVO pageReqVO, Long userId) { + return businessMapper.selectPage(pageReqVO, userId); + } + + @Override + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#pageReqVO.customerId", level = CrmPermissionLevelEnum.READ) + public PageResult getBusinessPageByCustomerId(CrmBusinessPageReqVO pageReqVO) { + return businessMapper.selectPageByCustomerId(pageReqVO); + } + + @Override + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#pageReqVO.contactId", level = CrmPermissionLevelEnum.READ) + public PageResult getBusinessPageByContact(CrmBusinessPageReqVO pageReqVO) { + // 1. 查询关è”çš„å•†æœºç¼–å· + List contactBusinessList = contactBusinessService.getContactBusinessListByContactId( + pageReqVO.getContactId()); + if (CollUtil.isEmpty(contactBusinessList)) { + return PageResult.empty(); + } + // 2. 查询商机分页 + return businessMapper.selectPageByContactId(pageReqVO, + convertSet(contactBusinessList, CrmContactBusinessDO::getBusinessId)); + } + + @Override + public Long getBusinessCountByCustomerId(Long customerId) { + return businessMapper.selectCount(CrmBusinessDO::getCustomerId, customerId); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusService.java new file mode 100644 index 000000000..a2fc2d18d --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusService.java @@ -0,0 +1,77 @@ +package cn.iocoder.yudao.module.crm.service.business; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusQueryVO; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusSaveReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO; + +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; + +/** + * å•†æœºçŠ¶æ€ Service æŽ¥å£ + * + * @author ljlleo + */ +public interface CrmBusinessStatusService { + + /** + * åˆ›å»ºå•†æœºçŠ¶æ€ + * + * @param createReqVO åˆ›å»ºä¿¡æ¯ + * @return ç¼–å· + */ + Long createBusinessStatus(@Valid CrmBusinessStatusSaveReqVO createReqVO); + + /** + * æ›´æ–°å•†æœºçŠ¶æ€ + * + * @param updateReqVO æ›´æ–°ä¿¡æ¯ + */ + void updateBusinessStatus(@Valid CrmBusinessStatusSaveReqVO updateReqVO); + + /** + * åˆ é™¤å•†æœºçŠ¶æ€ + * + * @param id ç¼–å· + */ + void deleteBusinessStatus(Long id); + + /** + * èŽ·å¾—å•†æœºçŠ¶æ€ + * + * @param id ç¼–å· + * @return å•†æœºçŠ¶æ€ + */ + CrmBusinessStatusDO getBusinessStatus(Long id); + + /** + * 获得商机状æ€åˆ†é¡µ + * + * @param pageReqVO 分页查询 + * @return 商机状æ€åˆ†é¡µ + */ + PageResult getBusinessStatusPage(CrmBusinessStatusPageReqVO pageReqVO); + + // TODO @ljlleo 常用的 ids 之类的查询,å¯ä»¥å°è£…å•ç‹¬çš„方法,ä¸ç”¨èµ°ç±»ä¼¼ QueryVO,用起æ¥æ›´æ–¹ä¾¿ã€‚ + // TODO @ljlleo 方法å用 getBusinessStatusList + /** + * 获得商机状æ€åˆ†é¡µ + * + * @param queryVO 查询å‚æ•° + * @return 商机状æ€åˆ†é¡µ + */ + List selectList(CrmBusinessStatusQueryVO queryVO); + + /** + * 获得商机状æ€åˆ—表 + * + * @param ids ç¼–å·æ•°ç»„ + * @return 商机状æ€åˆ—表 + */ + List getBusinessStatusList(Collection ids); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusServiceImpl.java new file mode 100644 index 000000000..2e49e99d7 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusServiceImpl.java @@ -0,0 +1,86 @@ +package cn.iocoder.yudao.module.crm.service.business; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusQueryVO; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusSaveReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO; +import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessStatusMapper; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; + +import java.util.Collection; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_STATUS_NOT_EXISTS; + +/** + * å•†æœºçŠ¶æ€ Service 实现类 + * + * @author ljlleo + */ +@Service +@Validated +public class CrmBusinessStatusServiceImpl implements CrmBusinessStatusService { + + @Resource + private CrmBusinessStatusMapper businessStatusMapper; + + @Override + public Long createBusinessStatus(CrmBusinessStatusSaveReqVO createReqVO) { + // æ’å…¥ + CrmBusinessStatusDO businessStatus = BeanUtils.toBean(createReqVO, CrmBusinessStatusDO.class); + businessStatusMapper.insert(businessStatus); + // 返回 + return businessStatus.getId(); + } + + @Override + public void updateBusinessStatus(CrmBusinessStatusSaveReqVO updateReqVO) { + // 校验存在 + validateBusinessStatusExists(updateReqVO.getId()); + // æ›´æ–° + CrmBusinessStatusDO updateObj = BeanUtils.toBean(updateReqVO, CrmBusinessStatusDO.class); + businessStatusMapper.updateById(updateObj); + } + + @Override + public void deleteBusinessStatus(Long id) { + // 校验存在 + validateBusinessStatusExists(id); + // TODO @ljlleo 这里å¯ä»¥è€ƒè™‘,如果有商机在使用,ä¸å…许删除 + // 删除 + businessStatusMapper.deleteById(id); + } + + private void validateBusinessStatusExists(Long id) { + if (businessStatusMapper.selectById(id) == null) { + throw exception(BUSINESS_STATUS_NOT_EXISTS); + } + } + + @Override + public CrmBusinessStatusDO getBusinessStatus(Long id) { + return businessStatusMapper.selectById(id); + } + + @Override + public PageResult getBusinessStatusPage(CrmBusinessStatusPageReqVO pageReqVO) { + return businessStatusMapper.selectPage(pageReqVO); + } + + @Override + public List selectList(CrmBusinessStatusQueryVO queryVO) { + return businessStatusMapper.selectList(queryVO); + } + + @Override + public List getBusinessStatusList(Collection ids) { + return businessStatusMapper.selectBatchIds(ids); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusTypeService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusTypeService.java new file mode 100644 index 000000000..20509994e --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusTypeService.java @@ -0,0 +1,75 @@ +package cn.iocoder.yudao.module.crm.service.business; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypePageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeQueryVO; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeSaveReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; + +/** + * 商机状æ€ç±»åž‹ Service æŽ¥å£ + * + * @author ljlleo + */ +public interface CrmBusinessStatusTypeService { + + /** + * 创建商机状æ€ç±»åž‹ + * + * @param createReqVO åˆ›å»ºä¿¡æ¯ + * @return ç¼–å· + */ + Long createBusinessStatusType(@Valid CrmBusinessStatusTypeSaveReqVO createReqVO); + + /** + * 更新商机状æ€ç±»åž‹ + * + * @param updateReqVO æ›´æ–°ä¿¡æ¯ + */ + void updateBusinessStatusType(@Valid CrmBusinessStatusTypeSaveReqVO updateReqVO); + + /** + * 删除商机状æ€ç±»åž‹ + * + * @param id ç¼–å· + */ + void deleteBusinessStatusType(Long id); + + /** + * 获得商机状æ€ç±»åž‹ + * + * @param id ç¼–å· + * @return 商机状æ€ç±»åž‹ + */ + CrmBusinessStatusTypeDO getBusinessStatusType(Long id); + + /** + * 获得商机状æ€ç±»åž‹åˆ†é¡µ + * + * @param pageReqVO 分页查询 + * @return 商机状æ€ç±»åž‹åˆ†é¡µ + */ + PageResult getBusinessStatusTypePage(CrmBusinessStatusTypePageReqVO pageReqVO); + + // TODO @ljlleo 常用的 ids 之类的查询,å¯ä»¥å°è£…å•ç‹¬çš„方法,ä¸ç”¨èµ°ç±»ä¼¼ QueryVO,用起æ¥æ›´æ–¹ä¾¿ã€‚ + /** + * 获得商机状æ€ç±»åž‹åˆ—表 + * + * @param queryVO 查询å‚æ•° + * @return 商机状æ€ç±»åž‹åˆ—表 + */ + List selectList(CrmBusinessStatusTypeQueryVO queryVO); + + /** + * 获得商机状æ€ç±»åž‹åˆ—表 + * + * @param ids ç¼–å·æ•°ç»„ + * @return 商机状æ€ç±»åž‹åˆ—表 + */ + List getBusinessStatusTypeList(Collection ids); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusTypeServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusTypeServiceImpl.java new file mode 100644 index 000000000..d9845976b --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusTypeServiceImpl.java @@ -0,0 +1,132 @@ +package cn.iocoder.yudao.module.crm.service.business; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypePageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeQueryVO; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeSaveReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO; +import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessStatusMapper; +import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessStatusTypeMapper; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; + +import java.util.Collection; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_STATUS_TYPE_NOT_EXISTS; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_STATUS_TYPE_NAME_EXISTS; + +/** + * 商机状æ€ç±»åž‹ Service 实现类 + * + * @author ljlleo + */ +@Service +@Validated +public class CrmBusinessStatusTypeServiceImpl implements CrmBusinessStatusTypeService { + + @Resource + private CrmBusinessStatusTypeMapper businessStatusTypeMapper; + + @Resource + private CrmBusinessStatusMapper businessStatusMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createBusinessStatusType(CrmBusinessStatusTypeSaveReqVO createReqVO) { + //检验å称是å¦å­˜åœ¨ + validateBusinessStatusTypeExists(createReqVO.getName(), null); + // æ’入类型 + CrmBusinessStatusTypeDO businessStatusType = BeanUtils.toBean(createReqVO, CrmBusinessStatusTypeDO.class); + businessStatusTypeMapper.insert(businessStatusType); + // æ’å…¥çŠ¶æ€ + if (CollUtil.isNotEmpty(createReqVO.getStatusList())) { + createReqVO.getStatusList().forEach(status -> status.setTypeId(businessStatusType.getId())); + businessStatusMapper.insertBatch(BeanUtils.toBean(createReqVO.getStatusList(), CrmBusinessStatusDO.class)); + } + return businessStatusType.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateBusinessStatusType(CrmBusinessStatusTypeSaveReqVO updateReqVO) { + // 校验存在 + validateBusinessStatusTypeExists(updateReqVO.getId()); + // 校验å称是å¦å­˜åœ¨ + validateBusinessStatusTypeExists(updateReqVO.getName(), updateReqVO.getId()); + // 更新类型 + CrmBusinessStatusTypeDO updateObj = BeanUtils.toBean(updateReqVO, CrmBusinessStatusTypeDO.class); + businessStatusTypeMapper.updateById(updateObj); + // 更新状æ€ï¼ˆåˆ é™¤ + 更新) + // TODO @ljlleo å¯ä»¥å‚考 DeliveryExpressTemplateServiceImpl çš„ updateExpressTemplateFree 方法;主è¦æ²¡å˜åŒ–的,还是ä¸åˆ é™¤äº†å“ˆã€‚ + businessStatusMapper.delete(updateReqVO.getId()); + updateReqVO.getStatusList().forEach(status -> status.setTypeId(updateReqVO.getId())); + businessStatusMapper.insertBatch(BeanUtils.toBean(updateReqVO.getStatusList(), CrmBusinessStatusDO.class)); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteBusinessStatusType(Long id) { + // TODO 待添加被引用校验 + //... + + // 校验存在 + validateBusinessStatusTypeExists(id); + // 删除类型 + businessStatusTypeMapper.deleteById(id); + // åˆ é™¤çŠ¶æ€ + businessStatusMapper.delete(id); + } + + private void validateBusinessStatusTypeExists(Long id) { + if (businessStatusTypeMapper.selectById(id) == null) { + throw exception(BUSINESS_STATUS_TYPE_NOT_EXISTS); + } + } + + // TODO @ljlleo 这个方法,这个å‚考 validateDeptNameUnique 实现。 + private void validateBusinessStatusTypeExists(String name, Long id) { + CrmBusinessStatusTypeDO businessStatusTypeDO = businessStatusTypeMapper.selectByIdAndName(id, name); + if (businessStatusTypeDO != null) { + throw exception(BUSINESS_STATUS_TYPE_NAME_EXISTS); + } +// LambdaQueryWrapper wrapper = new LambdaQueryWrapperX<>(); +// if(null != id) { +// wrapper.ne(CrmBusinessStatusTypeDO::getId, id); +// } +// long cnt = businessStatusTypeMapper.selectCount(wrapper.eq(CrmBusinessStatusTypeDO::getName, name)); +// if (cnt > 0) { +// throw exception(BUSINESS_STATUS_TYPE_NAME_EXISTS); +// } + } + + @Override + public CrmBusinessStatusTypeDO getBusinessStatusType(Long id) { + return businessStatusTypeMapper.selectById(id); + } + + @Override + public PageResult getBusinessStatusTypePage(CrmBusinessStatusTypePageReqVO pageReqVO) { + return businessStatusTypeMapper.selectPage(pageReqVO); + } + + @Override + public List selectList(CrmBusinessStatusTypeQueryVO queryVO) { + return businessStatusTypeMapper.selectList(queryVO); + } + + @Override + public List getBusinessStatusTypeList(Collection ids) { + return businessStatusTypeMapper.selectBatchIds(ids); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/bo/CrmBusinessUpdateProductReqBO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/bo/CrmBusinessUpdateProductReqBO.java new file mode 100644 index 000000000..34b2fa381 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/bo/CrmBusinessUpdateProductReqBO.java @@ -0,0 +1,49 @@ +package cn.iocoder.yudao.module.crm.service.business.bo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * æ›´æ–°å•†æœºå•†å“ Update Req BO + * + * @author HUIHUI + */ +@Data +public class CrmBusinessUpdateProductReqBO { + + /** + * å•†æœºç¼–å· + */ + @NotNull(message = "商机编å·ä¸èƒ½ä¸ºç©º") + private Long id; + + // TODO @芋艿:å†æƒ³æƒ³ + @NotEmpty(message = "产å“列表ä¸èƒ½ä¸ºç©º") + private List productItems; + + @Schema(description = "产å“列表") + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class CrmBusinessProductItem { + + @Schema(description = "产å“ç¼–å·", example = "20529") + @NotNull(message = "产å“ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long id; + + @Schema(description = "产å“æ•°é‡", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911") + @NotNull(message = "产å“æ•°é‡ä¸èƒ½ä¸ºç©º") + private Integer count; + + @Schema(description = "产å“折扣") + private Integer discountPercent; + + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueService.java new file mode 100644 index 000000000..1472bf02f --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueService.java @@ -0,0 +1,93 @@ +package cn.iocoder.yudao.module.crm.service.clue; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueSaveReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransferReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTranslateReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; + +/** + * 线索 Service æŽ¥å£ + * + * @author Wanwan + */ +public interface CrmClueService { + + /** + * 创建线索 + * + * @param createReqVO åˆ›å»ºä¿¡æ¯ + * @param userId ç”¨æˆ·ç¼–å· + * @return ç¼–å· + */ + Long createClue(@Valid CrmClueSaveReqVO createReqVO, Long userId); + + /** + * 更新线索 + * + * @param updateReqVO æ›´æ–°ä¿¡æ¯ + */ + void updateClue(@Valid CrmClueSaveReqVO updateReqVO); + + /** + * æ›´æ–°çº¿ç´¢ç›¸å…³çš„è·Ÿè¿›ä¿¡æ¯ + * + * @param clueUpdateFollowUpReqBO ä¿¡æ¯ + */ + void updateClueFollowUp(CrmUpdateFollowUpReqBO clueUpdateFollowUpReqBO); + + /** + * 删除线索 + * + * @param id ç¼–å· + */ + void deleteClue(Long id); + + /** + * 获得线索 + * + * @param id ç¼–å· + * @return 线索 + */ + CrmClueDO getClue(Long id); + + /** + * 获得线索列表 + * + * @param ids ç¼–å· + * @return 线索列表 + */ + List getClueList(Collection ids, Long userId); + + /** + * 获得线索分页 + * + * @param pageReqVO 分页查询 + * @param userId ç”¨æˆ·ç¼–å· + * @return 线索分页 + */ + PageResult getCluePage(CrmCluePageReqVO pageReqVO, Long userId); + + /** + * 线索转移 + * + * @param reqVO 请求 + * @param userId ç”¨æˆ·ç¼–å· + */ + void transferClue(CrmClueTransferReqVO reqVO, Long userId); + + /** + * 线索转化为客户 + * + * @param reqVO çº¿ç´¢ç¼–å· + * @param userId ç”¨æˆ·ç¼–å· + */ + void translateCustomer(CrmClueTranslateReqVO reqVO, Long userId); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java new file mode 100644 index 000000000..dfb044f50 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java @@ -0,0 +1,279 @@ +package cn.iocoder.yudao.module.crm.service.clue; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.extra.spring.SpringUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueSaveReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransferReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTranslateReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerSaveReqVO; +import cn.iocoder.yudao.module.crm.convert.clue.CrmClueConvert; +import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO; +import cn.iocoder.yudao.module.crm.dal.mysql.clue.CrmClueMapper; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; +import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; +import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; +import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO; +import cn.iocoder.yudao.module.crm.service.followup.CrmFollowUpRecordService; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmFollowUpCreateReqBO; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; +import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import com.mzt.logapi.context.LogRecordContext; +import com.mzt.logapi.service.impl.DiffParseFunction; +import com.mzt.logapi.starter.annotation.LogRecord; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.*; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_NOT_EXISTS; +import static java.util.Collections.singletonList; + +/** + * 线索 Service 实现类 + * + * @author Wanwan + */ +@Service +@Validated +public class CrmClueServiceImpl implements CrmClueService { + + @Resource + private CrmClueMapper clueMapper; + + @Resource + private CrmCustomerService customerService; + + @Resource + private CrmPermissionService crmPermissionService; + @Resource + private CrmFollowUpRecordService followUpRecordService; + @Resource + private AdminUserApi adminUserApi; + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_CREATE_SUB_TYPE, bizNo = "{{#clue.id}}", + success = CRM_LEADS_CREATE_SUCCESS) + public Long createClue(CrmClueSaveReqVO createReqVO, Long userId) { + // 1.1 校验关è”æ•°æ® + validateRelationDataExists(createReqVO); + // 1.2 校验负责人是å¦å­˜åœ¨ + if (createReqVO.getOwnerUserId() != null) { + adminUserApi.validateUserList(singletonList(createReqVO.getOwnerUserId())); + } else { + createReqVO.setOwnerUserId(userId); // 如果没有设置负责人那么默认æ“作人为负责人 + } + + // 2. æ’å…¥ + CrmClueDO clue = BeanUtils.toBean(createReqVO, CrmClueDO.class).setId(null); + clueMapper.insert(clue); + + // 3. 创建数æ®æƒé™ + CrmPermissionCreateReqBO createReqBO = new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_LEADS.getType()) + .setBizId(clue.getId()).setUserId(clue.getOwnerUserId()).setLevel(CrmPermissionLevelEnum.OWNER.getLevel()); + crmPermissionService.createPermission(createReqBO); + + // 4. 记录æ“作日志上下文 + LogRecordContext.putVariable("clue", clue); + return clue.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", + success = CRM_LEADS_UPDATE_SUCCESS) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#updateReq.id", level = CrmPermissionLevelEnum.OWNER) + public void updateClue(CrmClueSaveReqVO updateReq) { + Assert.notNull(updateReq.getId(), "线索编å·ä¸èƒ½ä¸ºç©º"); + // 1. 校验线索是å¦å­˜åœ¨ + CrmClueDO oldClue = validateClueExists(updateReq.getId()); + // 2. 校验关è”æ•°æ® + validateRelationDataExists(updateReq); + + // 3. æ›´æ–° + CrmClueDO updateObj = BeanUtils.toBean(updateReq, CrmClueDO.class); + clueMapper.updateById(updateObj); + + // 3. 记录æ“作日志上下文 + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldClue, CrmCustomerSaveReqVO.class)); + LogRecordContext.putVariable("clueName", oldClue.getName()); + } + + @Override + @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_UPDATE_SUB_TYPE, bizNo = "{{#updateReq.bizId}", + success = CRM_LEADS_UPDATE_SUCCESS) + public void updateClueFollowUp(CrmUpdateFollowUpReqBO updateReq) { + // 校验线索是å¦å­˜åœ¨ + CrmClueDO oldClue = validateClueExists(updateReq.getBizId()); + + // æ›´æ–° + clueMapper.updateById(BeanUtils.toBean(updateReq, CrmClueDO.class).setId(updateReq.getBizId())); + + // 3. 记录æ“作日志上下文 + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldClue, CrmUpdateFollowUpReqBO.class)); + LogRecordContext.putVariable("clueName", oldClue.getName()); + + } + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_DELETE_SUB_TYPE, bizNo = "{{#id}}", + success = CRM_LEADS_DELETE_SUCCESS) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) + public void deleteClue(Long id) { + // 1. 校验存在 + CrmClueDO clue = validateClueExists(id); + + // 2. 删除 + clueMapper.deleteById(id); + + // 3. 删除数æ®æƒé™ + crmPermissionService.deletePermission(CrmBizTypeEnum.CRM_LEADS.getType(), id); + + // 4. 删除跟进 + followUpRecordService.deleteFollowUpRecordByBiz(CrmBizTypeEnum.CRM_LEADS.getType(), id); + + // 记录æ“作日志上下文 + LogRecordContext.putVariable("clueName", clue.getName()); + } + + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.id}}", + success = CRM_LEADS_TRANSFER_SUCCESS) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) + public void transferClue(CrmClueTransferReqVO reqVO, Long userId) { + // 1 校验线索是å¦å­˜åœ¨ + CrmClueDO clue = validateClueExists(reqVO.getId()); + + // 2.1 æ•°æ®æƒé™è½¬ç§» + crmPermissionService.transferPermission(CrmClueConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_LEADS.getType())); + // 2.2 设置新的负责人 + clueMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId()); + + // 3. 记录转移日志 + LogRecordContext.putVariable("clue", clue); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) + public void translateCustomer(CrmClueTranslateReqVO reqVO, Long userId) { + // 1.1 校验线索都存在 + Set clueIds = reqVO.getIds(); + List clues = getClueList(clueIds, userId); + if (CollUtil.isEmpty(clues) || ObjectUtil.notEqual(clues.size(), clueIds.size())) { + clueIds.removeAll(convertSet(clues, CrmClueDO::getId)); + throw exception(CLUE_ANY_CLUE_NOT_EXISTS, clueIds); + } + // 1.2 存在已ç»è½¬åŒ–的,直接æ示哈。é¿å…æ“作的用户,以为都转化æˆåŠŸäº† + List translatedClues = filterList(clues, + clue -> ObjectUtil.equal(Boolean.TRUE, clue.getTransformStatus())); + if (CollUtil.isNotEmpty(translatedClues)) { + throw exception(CLUE_ANY_CLUE_ALREADY_TRANSLATED, convertSet(translatedClues, CrmClueDO::getId)); + } + + // 2.1 é历线索(未转化的线索),创建对应的客户 + clues.forEach(clue -> { + Long customerId = customerService.createCustomer(BeanUtils.toBean(clue, CrmCustomerCreateReqBO.class), userId); + clue.setCustomerId(customerId); + }); + // 2.2 更新线索 + clueMapper.updateBatch(convertList(clues, clue -> new CrmClueDO().setId(clue.getId()) + .setTransformStatus(Boolean.TRUE).setCustomerId(clue.getCustomerId()))); + // 2.3 å¤åˆ¶è·Ÿè¿›è®°å½• + copyFollowUpRecords(clues); + + // 3. 记录æ“作日志 + for (CrmClueDO clue : clues) { + getSelf().translateCustomerLog(clue); + } + } + + /** + * 线索被转æ¢å®¢æˆ·åŽï¼Œéœ€è¦å°†çº¿ç´¢çš„跟进记录,å¤åˆ¶åˆ°å®¢æˆ·ä¸Š + * + * @param clues 被转化的线索 + */ + private void copyFollowUpRecords(List clues) { + List followUpRecords = followUpRecordService.getFollowUpRecordByBiz( + CrmBizTypeEnum.CRM_LEADS.getType(), convertSet(clues, CrmClueDO::getId)); + if (CollUtil.isEmpty(followUpRecords)) { + return; + } + // 创建跟进 + Map clueMap = convertMap(clues, CrmClueDO::getId); + followUpRecordService.createFollowUpRecordBatch(convertList(followUpRecords, followUpRecord -> + BeanUtils.toBean(followUpRecord, CrmFollowUpCreateReqBO.class).setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()) + .setBizId(clueMap.get(followUpRecord.getBizId()).getCustomerId()))); + } + + @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_TRANSLATE_SUB_TYPE, bizNo = "{{#clue.id}}", + success = CRM_LEADS_TRANSLATE_SUCCESS) + public void translateCustomerLog(CrmClueDO clue) { + // 记录æ“作日志上下文 + LogRecordContext.putVariable("clue", clue); + } + + private void validateRelationDataExists(CrmClueSaveReqVO reqVO) { + // 校验负责人 + if (Objects.nonNull(reqVO.getOwnerUserId()) && + Objects.isNull(adminUserApi.getUser(reqVO.getOwnerUserId()))) { + throw exception(USER_NOT_EXISTS); + } + } + + private CrmClueDO validateClueExists(Long id) { + CrmClueDO crmClueDO = clueMapper.selectById(id); + if (crmClueDO == null) { + throw exception(CLUE_NOT_EXISTS); + } + return crmClueDO; + } + + @Override + @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#id", level = CrmPermissionLevelEnum.READ) + public CrmClueDO getClue(Long id) { + return clueMapper.selectById(id); + } + + @Override + public List getClueList(Collection ids, Long userId) { + if (CollUtil.isEmpty(ids)) { + return ListUtil.empty(); + } + return clueMapper.selectBatchIds(ids, userId); + } + + @Override + public PageResult getCluePage(CrmCluePageReqVO pageReqVO, Long userId) { + return clueMapper.selectPage(pageReqVO, userId); + } + + /** + * 获得自身的代ç†å¯¹è±¡ï¼Œè§£å†³ AOP 生效问题 + * + * @return 自己 + */ + private CrmClueServiceImpl getSelf() { + return SpringUtil.getBean(getClass()); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactBusinessService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactBusinessService.java new file mode 100644 index 000000000..8a9c4192f --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactBusinessService.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.crm.service.contact; + +import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactBusinessReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO; +import jakarta.validation.Valid; + +import java.util.List; + +/** + * CRM è”ç³»äººä¸Žå•†æœºçš„å…³è” Service æŽ¥å£ + * + * @author 芋é“æºç  + */ +public interface CrmContactBusinessService { + + /** + * 创建è”ç³»äººä¸Žå•†æœºçš„å…³è” + * + * @param createReqVO åˆ›å»ºä¿¡æ¯ + */ + void createContactBusinessList(@Valid CrmContactBusinessReqVO createReqVO); + + /** + * 删除è”ç³»äººä¸Žå•†æœºçš„å…³è” + * + * @param deleteReqVO åˆ é™¤ä¿¡æ¯ + */ + void deleteContactBusinessList(@Valid CrmContactBusinessReqVO deleteReqVO); + + /** + * 删除è”系人与商机的关è”,基于è”ç³»äººç¼–å· + * + * @param contactId è”ç³»äººç¼–å· + */ + void deleteContactBusinessByContactId(Long contactId); + + /** + * 获得è”系人与商机的关è”列表,基于è”ç³»äººç¼–å· + * + * @param contactId è”ç³»äººç¼–å· + * @return è”ç³»äººå•†æœºå…³è” + */ + List getContactBusinessListByContactId(Long contactId); + + /** + * 新增è”ç³»äººä¸Žå•†æœºçš„å…³è” + * + * @param contactBusiness 新增è”系人与商机的对象 + */ + void insert(CrmContactBusinessDO contactBusiness); + +} \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactBusinessServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactBusinessServiceImpl.java new file mode 100644 index 000000000..7b0711334 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactBusinessServiceImpl.java @@ -0,0 +1,99 @@ +package cn.iocoder.yudao.module.crm.service.contact; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactBusinessReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; +import cn.iocoder.yudao.module.crm.dal.mysql.contactbusinesslink.CrmContactBusinessMapper; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; +import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; +import jakarta.annotation.Resource; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.ArrayList; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_NOT_EXISTS; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CONTACT_NOT_EXISTS; + +/** + * è”ç³»äººä¸Žå•†æœºçš„å…³è” Service 实现类 + * + * @author 芋é“æºç  + */ +@Service +@Validated +public class CrmContactBusinessServiceImpl implements CrmContactBusinessService { + + @Resource + private CrmContactBusinessMapper contactBusinessMapper; + + @Resource + @Lazy // 延迟加载,为了解决延迟加载 + private CrmBusinessService businessService; + @Resource + @Lazy // 延迟加载,为了解决延迟加载 + private CrmContactService contactService; + + @Override + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#createReqVO.contactId", level = CrmPermissionLevelEnum.WRITE) + public void createContactBusinessList(CrmContactBusinessReqVO createReqVO) { + CrmContactDO contact = contactService.getContact(createReqVO.getContactId()); + if (contact == null) { + throw exception(CONTACT_NOT_EXISTS); + } + // é历处ç†ï¼Œè€ƒè™‘到一般数é‡ä¸ä¼šå¤ªå¤šï¼Œä»£ç å¤„ç†ç®€å• + List saveDOList = new ArrayList<>(); + createReqVO.getBusinessIds().forEach(businessId -> { + CrmBusinessDO business = businessService.getBusiness(businessId); + if (business == null) { + throw exception(BUSINESS_NOT_EXISTS); + } + // å…³è”åˆ¤é‡ + if (contactBusinessMapper.selectByContactIdAndBusinessId(createReqVO.getContactId(), businessId) != null) { + return; + } + saveDOList.add(new CrmContactBusinessDO(null, createReqVO.getContactId(), businessId)); + }); + // 批é‡æ’å…¥ + if (CollUtil.isNotEmpty(saveDOList)) { + contactBusinessMapper.insertBatch(saveDOList); + } + } + + @Override + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#deleteReqVO.contactId", level = CrmPermissionLevelEnum.WRITE) + public void deleteContactBusinessList(CrmContactBusinessReqVO deleteReqVO) { + CrmContactDO contact = contactService.getContact(deleteReqVO.getContactId()); + if (contact == null) { + throw exception(CONTACT_NOT_EXISTS); + } + // 直接删除 + contactBusinessMapper.deleteByContactIdAndBusinessId( + deleteReqVO.getContactId(), deleteReqVO.getBusinessIds()); + } + + @Override + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#contactId", level = CrmPermissionLevelEnum.WRITE) + public void deleteContactBusinessByContactId(Long contactId) { + contactBusinessMapper.delete(CrmContactBusinessDO::getContactId, contactId); + } + + @Override + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#contactId", level = CrmPermissionLevelEnum.READ) + public List getContactBusinessListByContactId(Long contactId) { + return contactBusinessMapper.selectListByContactId(contactId); + } + + @Override + public void insert(CrmContactBusinessDO contactBusiness) { + contactBusinessMapper.insert(contactBusiness); + } + +} \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactService.java new file mode 100644 index 000000000..d7688b8fb --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactService.java @@ -0,0 +1,137 @@ +package cn.iocoder.yudao.module.crm.service.contact; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactSaveReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactTransferReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; + +/** + * CRM è”系人 Service æŽ¥å£ + * + * @author 芋é“æºç  + */ +public interface CrmContactService { + + /** + * 创建è”系人 + * + * @param createReqVO åˆ›å»ºä¿¡æ¯ + * @param userId ç”¨æˆ·ç¼–å· + * @return ç¼–å· + */ + Long createContact(@Valid CrmContactSaveReqVO createReqVO, Long userId); + + /** + * æ›´æ–°è”系人 + * + * @param updateReqVO æ›´æ–°ä¿¡æ¯ + */ + void updateContact(@Valid CrmContactSaveReqVO updateReqVO); + + /** + * 删除è”系人 + * + * @param id ç¼–å· + */ + void deleteContact(Long id); + + /** + * è”系人转移 + * + * @param reqVO 请求 + * @param userId ç”¨æˆ·ç¼–å· + */ + void transferContact(CrmContactTransferReqVO reqVO, Long userId); + + /** + * 更新客户è”系人负责人 + * + * @param customerId å®¢æˆ·ç¼–å· + * @param ownerUserId ç”¨æˆ·ç¼–å· + */ + void updateOwnerUserIdByCustomerId(Long customerId, Long ownerUserId); + + /** + * æ›´æ–°è”ç³»äººç›¸å…³è·Ÿè¿›ä¿¡æ¯ + * + * @param updateFollowUpReqBOList è·Ÿè¿›ä¿¡æ¯ + */ + void updateContactFollowUpBatch(List updateFollowUpReqBOList); + + /** + * 获得è”系人 + * + * @param id ç¼–å· + * @return è”系人 + */ + CrmContactDO getContact(Long id); + + /** + * 获得è”系人列表 + * + * @param ids ç¼–å· + * @param userId ç”¨æˆ·ç¼–å· + * @return è”系人列表 + */ + List getContactListByIds(Collection ids, Long userId); + + /** + * 获得è”系人列表 + * + * @param ids ç¼–å· + * @return è”系人列表 + */ + List getContactListByIds(Collection ids); + + /** + * 获得è”系人列表 + * + * @return è”系人列表 + */ + List getContactList(); + + /** + * 获å–è”系人列表(校验æƒé™ï¼‰ + * + * @param userId ç”¨æˆ·ç¼–å· + * @return è”系人列表 + */ + List getSimpleContactList(Long userId); + + /** + * 获得è”系人分页 + * + * æ•°æ®æƒé™ï¼šåŸºäºŽ {@link CrmContactDO} + * + * @param pageReqVO 分页查询 + * @param userId ç”¨æˆ·ç¼–å· + * @return è”系人分页 + */ + PageResult getContactPage(CrmContactPageReqVO pageReqVO, Long userId); + + /** + * 获得è”系人分页 + * + * æ•°æ®æƒé™ï¼šåŸºäºŽ {@link CrmCustomerDO} + * + * @param pageVO 分页查询 + * @return è”系人分页 + */ + PageResult getContactPageByCustomerId(CrmContactPageReqVO pageVO); + + /** + * 获å–å…³è”客户的è”ç³»äººæ•°é‡ + * + * @param customerId å®¢æˆ·ç¼–å· + * @return æ•°é‡ + */ + Long getContactCountByCustomerId(Long customerId); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java new file mode 100644 index 000000000..08ce78b81 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java @@ -0,0 +1,256 @@ +package cn.iocoder.yudao.module.crm.service.contact; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactBusinessReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactSaveReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactTransferReqVO; +import cn.iocoder.yudao.module.crm.convert.contact.CrmContactConvert; +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; +import cn.iocoder.yudao.module.crm.dal.mysql.contact.CrmContactMapper; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; +import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; +import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; +import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; +import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import com.mzt.logapi.context.LogRecordContext; +import com.mzt.logapi.service.impl.DiffParseFunction; +import com.mzt.logapi.starter.annotation.LogRecord; +import jakarta.annotation.Resource; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_NOT_EXISTS; +import static java.util.Collections.singletonList; + +/** + * CRM è”系人 Service 实现类 + * + * @author 芋é“æºç  + */ +@Service +@Validated +public class CrmContactServiceImpl implements CrmContactService { + + @Resource + private CrmContactMapper contactMapper; + + @Resource + private CrmCustomerService customerService; + @Resource + private CrmPermissionService permissionService; + @Resource + @Lazy + private CrmContractService contractService; + @Resource + private CrmContactBusinessService contactBusinessService; + @Resource + private CrmBusinessService businessService; + + @Resource + private AdminUserApi adminUserApi; + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_CONTACT_TYPE, subType = CRM_CONTACT_CREATE_SUB_TYPE, bizNo = "{{#contact.id}}", + success = CRM_CONTACT_CREATE_SUCCESS) + public Long createContact(CrmContactSaveReqVO createReqVO, Long userId) { + createReqVO.setId(null); + // 1. 校验 + validateRelationDataExists(createReqVO); + + // 2. æ’å…¥è”系人 + CrmContactDO contact = BeanUtils.toBean(createReqVO, CrmContactDO.class); + contactMapper.insert(contact); + + // 3. 创建数æ®æƒé™ + permissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(userId) + .setBizType(CrmBizTypeEnum.CRM_CONTACT.getType()).setBizId(contact.getId()) + .setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); + + // 4. 如果有关è”商机,则需è¦åˆ›å»ºå…³è” + if (createReqVO.getBusinessId() != null) { + contactBusinessService.createContactBusinessList(new CrmContactBusinessReqVO() + .setContactId(contact.getId()).setBusinessIds(singletonList(createReqVO.getBusinessId()))); + } + + // 5. 记录æ“作日志 + LogRecordContext.putVariable("contact", contact); + return contact.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_CONTACT_TYPE, subType = CRM_CONTACT_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", + success = CRM_CONTACT_UPDATE_SUCCESS) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE) + public void updateContact(CrmContactSaveReqVO updateReqVO) { + // 1. 校验存在 + CrmContactDO oldContact = validateContactExists(updateReqVO.getId()); + validateRelationDataExists(updateReqVO); + + // 2. æ›´æ–°è”系人 + CrmContactDO updateObj = BeanUtils.toBean(updateReqVO, CrmContactDO.class); + contactMapper.updateById(updateObj); + + // 3. 记录æ“作日志 + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldContact, CrmContactSaveReqVO.class)); + LogRecordContext.putVariable("contactName", oldContact.getName()); + } + + /** + * 校验关è”çš„æ•°æ®éƒ½å­˜åœ¨ + * + * @param saveReqVO 新增/修改请求 VO + */ + private void validateRelationDataExists(CrmContactSaveReqVO saveReqVO) { + // 1. 校验客户 + if (saveReqVO.getCustomerId() != null && customerService.getCustomer(saveReqVO.getCustomerId()) == null) { + throw exception(CUSTOMER_NOT_EXISTS); + } + // 2. 校验负责人 + if (saveReqVO.getOwnerUserId() != null && adminUserApi.getUser(saveReqVO.getOwnerUserId()) == null) { + throw exception(USER_NOT_EXISTS); + } + // 3. 直属上级 + if (saveReqVO.getParentId() != null && contactMapper.selectById(saveReqVO.getParentId()) == null) { + throw exception(CONTACT_NOT_EXISTS); + } + // 4. 如果有关è”商机,则需è¦æ ¡éªŒå­˜åœ¨ + if (saveReqVO.getBusinessId() != null && businessService.getBusiness(saveReqVO.getBusinessId()) == null) { + throw exception(BUSINESS_NOT_EXISTS); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_CONTACT_TYPE, subType = CRM_CONTACT_DELETE_SUB_TYPE, bizNo = "{{#id}}", + success = CRM_CONTACT_DELETE_SUCCESS) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) + public void deleteContact(Long id) { + // 1.1 校验存在 + CrmContactDO contact = validateContactExists(id); + // 1.2 校验是å¦å…³è”åˆåŒ + if (contractService.getContractCountByContactId(id) > 0) { + throw exception(CONTACT_DELETE_FAIL_CONTRACT_LINK_EXISTS); + } + + // 2. 删除è”系人 + contactMapper.deleteById(id); + + // 4.1 删除数æ®æƒé™ + permissionService.deletePermission(CrmBizTypeEnum.CRM_CONTACT.getType(), id); + // 4.2 åˆ é™¤å•†æœºå…³è” + contactBusinessService.deleteContactBusinessByContactId(id); + + // 记录æ“作日志上下文 + LogRecordContext.putVariable("contactName", contact.getName()); + } + + private CrmContactDO validateContactExists(Long id) { + CrmContactDO contactDO = contactMapper.selectById(id); + if (contactDO == null) { + throw exception(CONTACT_NOT_EXISTS); + } + return contactDO; + } + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_CONTACT_TYPE, subType = CRM_CONTACT_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.id}}", + success = CRM_CONTACT_TRANSFER_SUCCESS) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER) + public void transferContact(CrmContactTransferReqVO reqVO, Long userId) { + // 1 校验è”系人是å¦å­˜åœ¨ + CrmContactDO contact = validateContactExists(reqVO.getId()); + + // 2.1 æ•°æ®æƒé™è½¬ç§» + permissionService.transferPermission( + CrmContactConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CONTACT.getType())); + // 2.2 设置新的负责人 + contactMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId()); + + // 3. 记录转移日志 + LogRecordContext.putVariable("contact", contact); + } + + @Override + public void updateOwnerUserIdByCustomerId(Long customerId, Long ownerUserId) { + contactMapper.updateOwnerUserIdByCustomerId(customerId, ownerUserId); + } + + @Override + public void updateContactFollowUpBatch(List updateFollowUpReqBOList) { + contactMapper.updateBatch(CrmContactConvert.INSTANCE.convertList(updateFollowUpReqBOList)); + } + + //======================= 查询相关 ======================= + + @Override + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#id", level = CrmPermissionLevelEnum.READ) + public CrmContactDO getContact(Long id) { + return contactMapper.selectById(id); + } + + @Override + public List getContactListByIds(Collection ids, Long userId) { + if (CollUtil.isEmpty(ids)) { + return ListUtil.empty(); + } + return contactMapper.selectBatchIds(ids, userId); + } + + @Override + public List getContactListByIds(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return ListUtil.empty(); + } + return contactMapper.selectBatchIds(ids); + } + + @Override + public List getContactList() { + return contactMapper.selectList(); + } + + @Override + public List getSimpleContactList(Long userId) { + CrmContactPageReqVO reqVO = new CrmContactPageReqVO(); + reqVO.setPageSize(PAGE_SIZE_NONE); // ä¸åˆ†é¡µ + return contactMapper.selectPage(reqVO, userId).getList(); + } + + @Override + public PageResult getContactPage(CrmContactPageReqVO pageReqVO, Long userId) { + return contactMapper.selectPage(pageReqVO, userId); + } + + @Override + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#pageVO.customerId", level = CrmPermissionLevelEnum.READ) + public PageResult getContactPageByCustomerId(CrmContactPageReqVO pageVO) { + return contactMapper.selectPageByCustomerId(pageVO); + } + + @Override + public Long getContactCountByCustomerId(Long customerId) { + return contactMapper.selectCount(CrmContactDO::getCustomerId, customerId); + } + +} \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java new file mode 100644 index 000000000..42d2848af --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java @@ -0,0 +1,146 @@ +package cn.iocoder.yudao.module.crm.service.contract; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +//import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO; +import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractProductDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; + +/** + * CRM åˆåŒ Service æŽ¥å£ + * + * @author dhb52 + */ +public interface CrmContractService { + + /** + * 创建åˆåŒ + * + * @param createReqVO åˆ›å»ºä¿¡æ¯ + * @param userId ç”¨æˆ·ç¼–å· + * @return ç¼–å· + */ + Long createContract(@Valid CrmContractSaveReqVO createReqVO, Long userId); + + /** + * æ›´æ–°åˆåŒ + * + * @param updateReqVO æ›´æ–°ä¿¡æ¯ + */ + void updateContract(@Valid CrmContractSaveReqVO updateReqVO); + + /** + * 删除åˆåŒ + * + * @param id ç¼–å· + */ + void deleteContract(Long id); + + /** + * åˆåŒè½¬ç§» + * + * @param reqVO 请求 + * @param userId ç”¨æˆ·ç¼–å· + */ + void transferContract(CrmContractTransferReqVO reqVO, Long userId); + + /** + * æ›´æ–°åˆåŒç›¸å…³çš„æ›´è¿›ä¿¡æ¯ + * + * @param contractUpdateFollowUpReqBO ä¿¡æ¯ + */ + void updateContractFollowUp(CrmUpdateFollowUpReqBO contractUpdateFollowUpReqBO); + + /** + * å‘èµ·åˆåŒå®¡æ‰¹æµç¨‹ + * + * @param id åˆåŒç¼–å· + * @param userId ç”¨æˆ·ç¼–å· + */ + void submitContract(Long id, Long userId); + +// /** +// * æ›´æ–°åˆåŒæµç¨‹å®¡æ‰¹ç»“æžœ +// * +// * @param event 审批结果 +// */ +// void updateContractAuditStatus(BpmResultListenerRespDTO event); + + /** + * 获得åˆåŒ + * + * @param id ç¼–å· + * @return åˆåŒ + */ + CrmContractDO getContract(Long id); + + /** + * 获得åˆåŒåˆ—表 + * + * @param ids ç¼–å· + * @return åˆåŒåˆ—表 + */ + List getContractList(Collection ids); + + /** + * 获得åˆåŒåˆ†é¡µ + * + * æ•°æ®æƒé™ï¼šåŸºäºŽ {@link CrmContractDO} è¯»å– + * + * @param pageReqVO 分页查询 + * @param userId ç”¨æˆ·ç¼–å· + * @return åˆåŒåˆ†é¡µ + */ + PageResult getContractPage(CrmContractPageReqVO pageReqVO, Long userId); + + /** + * 获得åˆåŒåˆ†é¡µï¼ŒåŸºäºŽæŒ‡å®šå®¢æˆ· + * + * æ•°æ®æƒé™ï¼šåŸºäºŽ {@link CrmCustomerDO} è¯»å– + * + * @param pageReqVO 分页查询 + * @return è”系人分页 + */ + PageResult getContractPageByCustomerId(CrmContractPageReqVO pageReqVO); + + /** + * 查询属于æŸä¸ªè”系人的åˆåŒæ•°é‡ + * + * @param contactId è”系人ID + * @return åˆåŒ + */ + Long getContractCountByContactId(Long contactId); + + /** + * 获å–å…³è”客户的åˆåŒæ•°é‡ + * + * @param customerId å®¢æˆ·ç¼–å· + * @return æ•°é‡ + */ + Long getContractCountByCustomerId(Long customerId); + + /** + * æ ¹æ®å•†æœºID获å–å…³è”客户的åˆåŒæ•°é‡ + * + * @param businessId å•†æœºç¼–å· + * @return æ•°é‡ + */ + Long getContractCountByBusinessId(Long businessId); + + /** + * 获å–åˆåŒå•†å“列表 + * + * @param contactId åˆåŒç¼–å· + * @return åˆåŒå•†å“列表 + */ + List getContractProductListByContractId(Long contactId); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java new file mode 100644 index 000000000..dac575555 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java @@ -0,0 +1,363 @@ +package cn.iocoder.yudao.module.crm.service.contract; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; +import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi; +import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; +import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; +import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO; +import cn.iocoder.yudao.module.crm.convert.contract.CrmContractConvert; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractProductDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; +import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractMapper; +import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractProductMapper; +import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; +import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; +import cn.iocoder.yudao.module.crm.service.business.bo.CrmBusinessUpdateProductReqBO; +import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; +import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; +import cn.iocoder.yudao.module.crm.service.product.CrmProductService; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import com.mzt.logapi.context.LogRecordContext; +import com.mzt.logapi.service.impl.DiffParseFunction; +import com.mzt.logapi.starter.annotation.LogRecord; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_NOT_EXISTS; + +/** + * CRM åˆåŒ Service 实现类 + * + * @author dhb52 + */ +@Service +@Validated +public class CrmContractServiceImpl implements CrmContractService { + + /** + * BPM åˆåŒå®¡æ‰¹æµç¨‹æ ‡è¯† + */ + public static final String CONTRACT_APPROVE = "contract-approve"; + + @Resource + private CrmContractMapper contractMapper; + @Resource + private CrmContractProductMapper contractProductMapper; + + @Resource + private CrmPermissionService crmPermissionService; + @Resource + private CrmProductService productService; + @Resource + private CrmCustomerService customerService; + @Resource + private CrmBusinessService businessService; + + @Resource + private AdminUserApi adminUserApi; + @Resource + private BpmProcessInstanceApi bpmProcessInstanceApi; + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_CREATE_SUB_TYPE, bizNo = "{{#contract.id}}", + success = CRM_CONTRACT_CREATE_SUCCESS) + public Long createContract(CrmContractSaveReqVO createReqVO, Long userId) { + validateRelationDataExists(createReqVO); + // 1.1 æ’å…¥åˆåŒ + CrmContractDO contract = BeanUtils.toBean(createReqVO, CrmContractDO.class).setId(null); + contractMapper.insert(contract); + // 1.2 æ’å…¥åˆåŒå…³è”å•†å“ + if (CollUtil.isNotEmpty(createReqVO.getProductItems())) { // å¦‚æžœæœ‰çš„è¯ + List productList = convertContractProductList(createReqVO, contract.getId()); + contractProductMapper.insertBatch(productList); + // æ›´æ–°åˆåŒå•†å“æ€»é‡‘é¢ + contractMapper.updateById(new CrmContractDO().setId(contract.getId()).setProductPrice( + getSumValue(productList, CrmContractProductDO::getTotalPrice, Integer::sum))); + // 如果存在åˆåŒå…³è”了商机则更新商机商å“å…³è” + if (contract.getBusinessId() != null) { + businessService.updateBusinessProduct(new CrmBusinessUpdateProductReqBO().setId(contract.getBusinessId()) + .setProductItems(BeanUtils.toBean(createReqVO.getProductItems(), CrmBusinessUpdateProductReqBO.CrmBusinessProductItem.class))); + } + } + + // 2. 创建数æ®æƒé™ + crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(userId) + .setBizType(CrmBizTypeEnum.CRM_CONTRACT.getType()).setBizId(contract.getId()) + .setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); + + // 3. 记录æ“作日志上下文 + LogRecordContext.putVariable("contract", contract); + return contract.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", + success = CRM_CONTRACT_UPDATE_SUCCESS) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE) + public void updateContract(CrmContractSaveReqVO updateReqVO) { + Assert.notNull(updateReqVO.getId(), "åˆåŒç¼–å·ä¸èƒ½ä¸ºç©º"); + // 1.1 校验存在 + CrmContractDO contract = validateContractExists(updateReqVO.getId()); + // 1.2 åªæœ‰è‰ç¨¿ã€å®¡æ‰¹ä¸­ï¼Œå¯ä»¥ç¼–辑; + if (!ObjectUtils.equalsAny(contract.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus(), + CrmAuditStatusEnum.PROCESS.getStatus())) { + throw exception(CONTRACT_UPDATE_FAIL_EDITING_PROHIBITED); + } + validateRelationDataExists(updateReqVO); + + // 2.1 æ›´æ–°åˆåŒ + CrmContractDO updateObj = BeanUtils.toBean(updateReqVO, CrmContractDO.class); + contractMapper.updateById(updateObj); + // 2.2 æ›´æ–°åˆåŒå…³è”å•†å“ + updateContractProduct(updateReqVO, updateObj.getId()); + + // 3. 记录æ“作日志上下文 + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(contract, CrmContractSaveReqVO.class)); + LogRecordContext.putVariable("contractName", contract.getName()); + } + + private void updateContractProduct(CrmContractSaveReqVO updateReqVO, Long contractId) { + if (CollUtil.isEmpty(updateReqVO.getProductItems())) { + return; + } + List newProductList = convertContractProductList(updateReqVO, contractId); + List oldProductList = contractProductMapper.selectListByContractId(contractId); + List> diffList = diffList(oldProductList, newProductList, (oldObj, newObj) -> { + boolean match = ObjUtil.equal(oldObj.getProductId(), newObj.getProductId()); + if (match) { + newObj.setId(oldObj.getId()); // 设置一下è€çš„ç¼–å·æ›´æ–°æ—¶éœ€è¦ä½¿ç”¨ + } + return match; + }); + if (CollUtil.isNotEmpty(diffList.get(0))) { + contractProductMapper.insertBatch(diffList.get(0)); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + contractProductMapper.updateBatch(diffList.get(1)); + } + if (CollUtil.isNotEmpty(diffList.get(2))) { + contractProductMapper.deleteBatchIds(convertList(diffList.get(2), CrmContractProductDO::getId)); + } + } + + // TODO @åˆåŒå¾…定:缺一个å–消åˆåŒçš„接å£ï¼›åªæœ‰è‰ç¨¿ã€å®¡æ‰¹ä¸­å¯ä»¥å–消;CrmAuditStatusEnum + + private List convertContractProductList(CrmContractSaveReqVO reqVO, Long contractId) { + // 校验商å“存在 + Set productIds = convertSet(reqVO.getProductItems(), CrmContractSaveReqVO.CrmContractProductItem::getId); + List productList = productService.getProductList(productIds); + if (CollUtil.isEmpty(productIds) || productList.size() != productIds.size()) { + throw exception(PRODUCT_NOT_EXISTS); + } + Map productMap = convertMap(productList, CrmProductDO::getId); + return convertList(reqVO.getProductItems(), productItem -> { + CrmProductDO product = productMap.get(productItem.getId()); + return BeanUtils.toBean(product, CrmContractProductDO.class) + .setId(null).setProductId(productItem.getId()).setContractId(contractId) + .setCount(productItem.getCount()).setDiscountPercent(productItem.getDiscountPercent()) + .setTotalPrice(MoneyUtils.calculator(product.getPrice(), productItem.getCount(), productItem.getDiscountPercent())); + }); + } + + /** + * 校验关è”æ•°æ®æ˜¯å¦å­˜åœ¨ + * + * @param reqVO 请求 + */ + private void validateRelationDataExists(CrmContractSaveReqVO reqVO) { + // 1. 校验客户 + if (reqVO.getCustomerId() != null && customerService.getCustomer(reqVO.getCustomerId()) == null) { + throw exception(CUSTOMER_NOT_EXISTS); + } + // 2. 校验负责人 + if (reqVO.getOwnerUserId() != null && adminUserApi.getUser(reqVO.getOwnerUserId()) == null) { + throw exception(USER_NOT_EXISTS); + } + // 3. 如果有关è”商机,则需è¦æ ¡éªŒå­˜åœ¨ + if (reqVO.getBusinessId() != null && businessService.getBusiness(reqVO.getBusinessId()) == null) { + throw exception(BUSINESS_NOT_EXISTS); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_DELETE_SUB_TYPE, bizNo = "{{#id}}", + success = CRM_CONTRACT_DELETE_SUCCESS) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) + public void deleteContract(Long id) { + // TODO @åˆåŒå¾…定:如果被 CrmReceivableDO 所使用,则ä¸å…许删除 + // 校验存在 + CrmContractDO contract = validateContractExists(id); + // 删除 + contractMapper.deleteById(id); + // 删除数æ®æƒé™ + crmPermissionService.deletePermission(CrmBizTypeEnum.CRM_CONTRACT.getType(), id); + + // 记录æ“作日志上下文 + LogRecordContext.putVariable("contractName", contract.getName()); + } + + private CrmContractDO validateContractExists(Long id) { + CrmContractDO contract = contractMapper.selectById(id); + if (contract == null) { + throw exception(CONTRACT_NOT_EXISTS); + } + return contract; + } + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.id}}", + success = CRM_CONTRACT_TRANSFER_SUCCESS) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER) + public void transferContract(CrmContractTransferReqVO reqVO, Long userId) { + // 1. 校验åˆåŒæ˜¯å¦å­˜åœ¨ + CrmContractDO contract = validateContractExists(reqVO.getId()); + + // 2.1 æ•°æ®æƒé™è½¬ç§» + crmPermissionService.transferPermission( + CrmContractConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CONTRACT.getType())); + // 2.2 设置负责人 + contractMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId()); + + // 3. 记录转移日志 + LogRecordContext.putVariable("contract", contract); + } + + @Override + public void updateContractFollowUp(CrmUpdateFollowUpReqBO contractUpdateFollowUpReqBO) { + contractMapper.updateById(BeanUtils.toBean(contractUpdateFollowUpReqBO, CrmContractDO.class).setId(contractUpdateFollowUpReqBO.getBizId())); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_SUBMIT_SUB_TYPE, bizNo = "{{#id}}", + success = CRM_CONTRACT_SUBMIT_SUCCESS) + public void submitContract(Long id, Long userId) { + // 1. 校验åˆåŒæ˜¯å¦åœ¨å®¡æ‰¹ + CrmContractDO contract = validateContractExists(id); + if (ObjUtil.notEqual(contract.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus())) { + throw exception(CONTRACT_SUBMIT_FAIL_NOT_DRAFT); + } + + // 2. 创建åˆåŒå®¡æ‰¹æµç¨‹å®žä¾‹ + String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO() + .setProcessDefinitionKey(CONTRACT_APPROVE).setBusinessKey(String.valueOf(id))).getCheckedData(); + + // 3. æ›´æ–°åˆåŒå·¥ä½œæµç¼–å· + contractMapper.updateById(new CrmContractDO().setId(id).setProcessInstanceId(processInstanceId) + .setAuditStatus(CrmAuditStatusEnum.PROCESS.getStatus())); + + // 3. 记录日志 + LogRecordContext.putVariable("contractName", contract.getName()); + } + +// @Override +// public void updateContractAuditStatus(BpmResultListenerRespDTO event) { +// // 判断下状æ€æ˜¯å¦ç¬¦åˆé¢„期 +// if (!isEndResult(event.getResult())) { +// return; +// } +// // 状æ€è½¬æ¢ +// if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.APPROVE.getResult())) { +// event.setResult(CrmAuditStatusEnum.APPROVE.getStatus()); +// } +// if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.REJECT.getResult())) { +// event.setResult(CrmAuditStatusEnum.REJECT.getStatus()); +// } +// if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.CANCEL.getResult())) { +// event.setResult(CrmAuditStatusEnum.CANCEL.getStatus()); +// } +// // æ›´æ–°åˆåŒçŠ¶æ€ +// contractMapper.updateById(new CrmContractDO().setId(Long.parseLong(event.getBusinessKey())) +// .setAuditStatus(event.getResult())); +// } + + /** + * 判断该结果是å¦å¤„于 End 最终结果 + * + * @param result 结果 + * @return æ˜¯å¦ + */ + public static boolean isEndResult(Integer result) { + return ObjectUtils.equalsAny(result, BpmProcessInstanceResultEnum.APPROVE.getResult(), + BpmProcessInstanceResultEnum.REJECT.getResult(), BpmProcessInstanceResultEnum.CANCEL.getResult()); + } + + //======================= 查询相关 ======================= + + @Override + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#id", level = CrmPermissionLevelEnum.READ) + public CrmContractDO getContract(Long id) { + return contractMapper.selectById(id); + } + + @Override + public List getContractList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return ListUtil.empty(); + } + return contractMapper.selectBatchIds(ids); + } + + @Override + public PageResult getContractPage(CrmContractPageReqVO pageReqVO, Long userId) { + return contractMapper.selectPage(pageReqVO, userId); + } + + @Override + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#pageReqVO.customerId", level = CrmPermissionLevelEnum.READ) + public PageResult getContractPageByCustomerId(CrmContractPageReqVO pageReqVO) { + return contractMapper.selectPageByCustomerId(pageReqVO); + } + + @Override + public Long getContractCountByContactId(Long contactId) { + return contractMapper.selectCountByContactId(contactId); + } + + @Override + public Long getContractCountByCustomerId(Long customerId) { + return contractMapper.selectCount(CrmContractDO::getCustomerId, customerId); + } + + @Override + public Long getContractCountByBusinessId(Long businessId) { + return contractMapper.selectCountByBusinessId(businessId); + } + + @Override + public List getContractProductListByContractId(Long contactId) { + return contractProductMapper.selectListByContractId(contactId); + } + + // TODO @åˆåŒå¾…定:需è¦æ–°å¢žä¸€ä¸ª ContractConfigDO 表,åˆåŒé…置,é‡ç‚¹æ˜¯åˆ°æœŸæ醒; +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerLimitConfigService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerLimitConfigService.java new file mode 100644 index 000000000..f67f377ac --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerLimitConfigService.java @@ -0,0 +1,64 @@ +package cn.iocoder.yudao.module.crm.service.customer; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigSaveReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO; +import jakarta.validation.Valid; + +import java.util.List; + +/** + * 客户é™åˆ¶é…ç½® Service æŽ¥å£ + * + * @author Wanwan + */ +public interface CrmCustomerLimitConfigService { + + /** + * 创建客户é™åˆ¶é…ç½® + * + * @param createReqVO åˆ›å»ºä¿¡æ¯ + * @return ç¼–å· + */ + Long createCustomerLimitConfig(@Valid CrmCustomerLimitConfigSaveReqVO createReqVO); + + /** + * 更新客户é™åˆ¶é…ç½® + * + * @param updateReqVO æ›´æ–°ä¿¡æ¯ + */ + void updateCustomerLimitConfig(@Valid CrmCustomerLimitConfigSaveReqVO updateReqVO); + + /** + * 删除客户é™åˆ¶é…ç½® + * + * @param id ç¼–å· + */ + void deleteCustomerLimitConfig(Long id); + + /** + * 获得客户é™åˆ¶é…ç½® + * + * @param id ç¼–å· + * @return 客户é™åˆ¶é…ç½® + */ + CrmCustomerLimitConfigDO getCustomerLimitConfig(Long id); + + /** + * 获得客户é™åˆ¶é…置分页 + * + * @param pageReqVO 分页查询 + * @return 客户é™åˆ¶é…置分页 + */ + PageResult getCustomerLimitConfigPage(CrmCustomerLimitConfigPageReqVO pageReqVO); + + /** + * 查询用户对应的é…置列表 + * + * @param type 类型 + * @param userId 用户类型 + */ + List getCustomerLimitConfigListByUserId(Integer type, Long userId); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerLimitConfigServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerLimitConfigServiceImpl.java new file mode 100644 index 000000000..eeddec327 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerLimitConfigServiceImpl.java @@ -0,0 +1,124 @@ +package cn.iocoder.yudao.module.crm.service.customer; + +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigSaveReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO; +import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerLimitConfigMapper; +import cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum; +import cn.iocoder.yudao.module.system.api.dept.DeptApi; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import com.mzt.logapi.context.LogRecordContext; +import com.mzt.logapi.service.impl.DiffParseFunction; +import com.mzt.logapi.starter.annotation.LogRecord; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CUSTOMER_LIMIT_CONFIG_NOT_EXISTS; +import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; + +/** + * 客户é™åˆ¶é…ç½® Service 实现类 + * + * @author Wanwan + */ +@Service +@Validated +public class CrmCustomerLimitConfigServiceImpl implements CrmCustomerLimitConfigService { + + @Resource + private CrmCustomerLimitConfigMapper customerLimitConfigMapper; + + @Resource + private DeptApi deptApi; + @Resource + private AdminUserApi adminUserApi; + + @Override + @LogRecord(type = CRM_CUSTOMER_LIMIT_CONFIG_TYPE, subType = CRM_CUSTOMER_LIMIT_CONFIG_CREATE_SUB_TYPE, bizNo = "{{#limitId}}", + success = CRM_CUSTOMER_LIMIT_CONFIG_CREATE_SUCCESS) + public Long createCustomerLimitConfig(CrmCustomerLimitConfigSaveReqVO createReqVO) { + validateUserAndDept(createReqVO.getUserIds(), createReqVO.getDeptIds()); + // æ’å…¥ + CrmCustomerLimitConfigDO limitConfig = BeanUtils.toBean(createReqVO, CrmCustomerLimitConfigDO.class); + customerLimitConfigMapper.insert(limitConfig); + + // 记录æ“作日志上下文 + LogRecordContext.putVariable("limitType", CrmCustomerLimitConfigTypeEnum.getNameByType(limitConfig.getType())); + LogRecordContext.putVariable("limitId", limitConfig.getId()); + return limitConfig.getId(); + } + + @Override + @LogRecord(type = CRM_CUSTOMER_LIMIT_CONFIG_TYPE, subType = CRM_CUSTOMER_LIMIT_CONFIG_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", + success = CRM_CUSTOMER_LIMIT_CONFIG_UPDATE_SUCCESS) + public void updateCustomerLimitConfig(CrmCustomerLimitConfigSaveReqVO updateReqVO) { + // 校验存在 + CrmCustomerLimitConfigDO oldLimitConfig = validateCustomerLimitConfigExists(updateReqVO.getId()); + validateUserAndDept(updateReqVO.getUserIds(), updateReqVO.getDeptIds()); + // æ›´æ–° + CrmCustomerLimitConfigDO updateObj = BeanUtils.toBean(updateReqVO, CrmCustomerLimitConfigDO.class); + customerLimitConfigMapper.updateById(updateObj); + + // 记录æ“作日志上下文 + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldLimitConfig, CrmCustomerLimitConfigSaveReqVO.class)); + } + + @Override + @LogRecord(type = CRM_CUSTOMER_LIMIT_CONFIG_TYPE, subType = CRM_CUSTOMER_LIMIT_CONFIG_DELETE_SUB_TYPE, bizNo = "{{#id}}", + success = CRM_CUSTOMER_LIMIT_CONFIG_DELETE_SUCCESS) + public void deleteCustomerLimitConfig(Long id) { + // 校验存在 + CrmCustomerLimitConfigDO limitConfig = validateCustomerLimitConfigExists(id); + // 删除 + customerLimitConfigMapper.deleteById(id); + + // 记录æ“作日志上下文 + LogRecordContext.putVariable("limitType", CrmCustomerLimitConfigTypeEnum.getNameByType(limitConfig.getType())); + } + + @Override + public CrmCustomerLimitConfigDO getCustomerLimitConfig(Long id) { + return customerLimitConfigMapper.selectById(id); + } + + @Override + public PageResult getCustomerLimitConfigPage(CrmCustomerLimitConfigPageReqVO pageReqVO) { + return customerLimitConfigMapper.selectPage(pageReqVO); + } + + private CrmCustomerLimitConfigDO validateCustomerLimitConfigExists(Long id) { + CrmCustomerLimitConfigDO limitConfigDO = customerLimitConfigMapper.selectById(id); + if (limitConfigDO == null) { + throw exception(CUSTOMER_LIMIT_CONFIG_NOT_EXISTS); + } + return limitConfigDO; + } + + /** + * 校验入å‚的用户和部门 + * + * @param userIds 用户 ids + * @param deptIds 部门 ids + */ + private void validateUserAndDept(Collection userIds, Collection deptIds) { + deptApi.validateDeptList(deptIds); + adminUserApi.validateUserList(userIds); + } + + @Override + public List getCustomerLimitConfigListByUserId(Integer type, Long userId) { + AdminUserRespDTO user = adminUserApi.getUser(userId).getCheckedData(); + Assert.notNull(user, "用户({})ä¸å­˜åœ¨", userId); + return customerLimitConfigMapper.selectListByTypeAndUserIdAndDeptId(type, userId, user.getDeptId()); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerPoolConfigService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerPoolConfigService.java new file mode 100644 index 000000000..30eb6f581 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerPoolConfigService.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.crm.service.customer; + +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.poolconfig.CrmCustomerPoolConfigSaveReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO; + +import jakarta.validation.Valid; + +/** + * 客户公海é…ç½® Service æŽ¥å£ + * + * @author Wanwan + */ +public interface CrmCustomerPoolConfigService { + + /** + * 获得客户公海é…ç½® + * + * @return 客户公海é…ç½® + */ + CrmCustomerPoolConfigDO getCustomerPoolConfig(); + + /** + * ä¿å­˜å®¢æˆ·å…¬æµ·é…ç½® + * + * @param saveReqVO æ›´æ–°ä¿¡æ¯ + */ + void saveCustomerPoolConfig(@Valid CrmCustomerPoolConfigSaveReqVO saveReqVO); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerPoolConfigServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerPoolConfigServiceImpl.java new file mode 100644 index 000000000..303a758d2 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerPoolConfigServiceImpl.java @@ -0,0 +1,65 @@ +package cn.iocoder.yudao.module.crm.service.customer; + +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.poolconfig.CrmCustomerPoolConfigSaveReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO; +import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerPoolConfigMapper; +import com.mzt.logapi.context.LogRecordContext; +import com.mzt.logapi.starter.annotation.LogRecord; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.Objects; + +import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; + +/** + * 客户公海é…ç½® Service 实现类 + * + * @author Wanwan + */ +@Service +@Validated +public class CrmCustomerPoolConfigServiceImpl implements CrmCustomerPoolConfigService { + + @Resource + private CrmCustomerPoolConfigMapper customerPoolConfigMapper; + + /** + * 获得客户公海é…ç½® + * + * @return 客户公海é…ç½® + */ + @Override + public CrmCustomerPoolConfigDO getCustomerPoolConfig() { + return customerPoolConfigMapper.selectOne(); + } + + /** + * ä¿å­˜å®¢æˆ·å…¬æµ·é…ç½® + * + * @param saveReqVO æ›´æ–°ä¿¡æ¯ + */ + @Override + @LogRecord(type = CRM_CUSTOMER_POOL_CONFIG_TYPE, subType = CRM_CUSTOMER_POOL_CONFIG_SUB_TYPE, bizNo = "{{#poolConfigId}}", + success = CRM_CUSTOMER_POOL_CONFIG_SUCCESS) + public void saveCustomerPoolConfig(CrmCustomerPoolConfigSaveReqVO saveReqVO) { + // 存在,则进行更新 + CrmCustomerPoolConfigDO dbConfig = getCustomerPoolConfig(); + CrmCustomerPoolConfigDO poolConfig = BeanUtils.toBean(saveReqVO, CrmCustomerPoolConfigDO.class); + if (Objects.nonNull(dbConfig)) { + customerPoolConfigMapper.updateById(poolConfig.setId(dbConfig.getId())); + // 记录æ“作日志上下文 + LogRecordContext.putVariable("isPoolConfigUpdate", Boolean.TRUE); + LogRecordContext.putVariable("poolConfigId", poolConfig.getId()); + return; + } + // ä¸å­˜åœ¨ï¼Œåˆ™è¿›è¡Œæ’å…¥ + customerPoolConfigMapper.insert(poolConfig); + // 记录æ“作日志上下文 + LogRecordContext.putVariable("isPoolConfigUpdate", Boolean.FALSE); + LogRecordContext.putVariable("poolConfigId", poolConfig.getId()); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java new file mode 100644 index 000000000..911260f76 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java @@ -0,0 +1,146 @@ +package cn.iocoder.yudao.module.crm.service.customer; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.*; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO; +import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; + +/** + * 客户 Service æŽ¥å£ + * + * @author Wanwan + */ +public interface CrmCustomerService { + + /** + * 创建客户 + * + * @param createReqVO åˆ›å»ºä¿¡æ¯ + * @param userId ç”¨æˆ·ç¼–å· + * @return ç¼–å· + */ + Long createCustomer(@Valid CrmCustomerSaveReqVO createReqVO, Long userId); + + /** + * 更新客户 + * + * @param updateReqVO æ›´æ–°ä¿¡æ¯ + */ + void updateCustomer(@Valid CrmCustomerSaveReqVO updateReqVO); + + /** + * 删除客户 + * + * @param id ç¼–å· + */ + void deleteCustomer(Long id); + + /** + * 获得客户 + * + * @param id ç¼–å· + * @return 客户 + */ + CrmCustomerDO getCustomer(Long id); + + /** + * 获得客户列表 + * + * @param ids 客户编å·æ•°ç»„ + * @return 客户列表 + * @author ljlleo + */ + List getCustomerList(Collection ids); + + /** + * 获得客户分页 + * + * @param pageReqVO 分页查询 + * @param userId ç”¨æˆ·ç¼–å· + * @return 客户分页 + */ + PageResult getCustomerPage(CrmCustomerPageReqVO pageReqVO, Long userId); + + /** + * 校验客户是å¦å­˜åœ¨ + * + * @param customerId 客户 id + */ + void validateCustomer(Long customerId); + + /** + * 客户转移 + * + * @param reqVO 请求 + * @param userId ç”¨æˆ·ç¼–å· + */ + void transferCustomer(CrmCustomerTransferReqVO reqVO, Long userId); + + /** + * é”定/解é”客户 + * + * @param lockReqVO æ›´æ–°ä¿¡æ¯ + * @param userId ç”¨æˆ·ç¼–å· + */ + void lockCustomer(@Valid CrmCustomerLockReqVO lockReqVO, Long userId); + + /** + * æ›´æ–°å®¢æˆ·ç›¸å…³æ›´è¿›ä¿¡æ¯ + * + * @param customerUpdateFollowUpReqBO 请求 + */ + void updateCustomerFollowUp(CrmUpdateFollowUpReqBO customerUpdateFollowUpReqBO); + + /** + * 创建客户 + * + * @param customerCreateReq è¯·æ±‚ä¿¡æ¯ + * @param userId ç”¨æˆ·ç¼–å· + * @return 客户列表 + */ + Long createCustomer(CrmCustomerCreateReqBO customerCreateReq, Long userId); + + /** + * 批é‡å¯¼å…¥å®¢æˆ· + * + * @param importCustomers 导入客户列表 + * @param importReqVO 请求 + * @return 导入结果 + */ + CrmCustomerImportRespVO importCustomerList(List importCustomers, CrmCustomerImportReqVO importReqVO); + + // ==================== 公海相关æ“作 ==================== + + /** + * 客户放入公海 + * + * @param id å®¢æˆ·ç¼–å· + */ + void putCustomerPool(Long id); + + /** + * 领å–公海客户 + * + * @param ids è¦é¢†å–的客户编å·æ•°ç»„ + * @param ownerUserId 负责人 + * @param isReceive 是/å¦é¢†å– + */ + void receiveCustomer(List ids, Long ownerUserId, Boolean isReceive); + + /** + * ã€ç³»ç»Ÿã€‘客户自动掉入公海 + * + * @return æŽ‰å…¥å…¬æµ·æ•°é‡ + */ + int autoPutCustomerPool(); + + PageResult getPutInPoolRemindCustomerPage(CrmCustomerPageReqVO pageVO, + CrmCustomerPoolConfigDO poolConfigDO, + Long loginUserId); +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java new file mode 100644 index 000000000..6351498ce --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java @@ -0,0 +1,572 @@ +package cn.iocoder.yudao.module.crm.service.customer; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.*; +import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO; +import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; +import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; +import cn.iocoder.yudao.module.crm.framework.permission.core.util.CrmPermissionUtils; +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; +import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; +import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; +import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; +import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import com.mzt.logapi.context.LogRecordContext; +import com.mzt.logapi.service.impl.DiffParseFunction; +import com.mzt.logapi.starter.annotation.LogRecord; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.time.LocalDateTime; +import java.util.*; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; +import static cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum.CUSTOMER_LOCK_LIMIT; +import static cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum.CUSTOMER_OWNER_LIMIT; +import static java.util.Collections.singletonList; + +/** + * 客户 Service 实现类 + * + * @author Wanwan + */ +@Service +@Slf4j +@Validated +public class CrmCustomerServiceImpl implements CrmCustomerService { + + @Resource + private CrmCustomerMapper customerMapper; + + @Resource + private CrmPermissionService permissionService; + @Resource + private CrmCustomerLimitConfigService customerLimitConfigService; + @Resource + @Lazy + private CrmCustomerPoolConfigService customerPoolConfigService; + @Resource + @Lazy + private CrmContactService contactService; + @Resource + @Lazy + private CrmBusinessService businessService; + @Resource + @Lazy + private CrmContractService contractService; + + @Resource + private AdminUserApi adminUserApi; + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_CREATE_SUB_TYPE, bizNo = "{{#customer.id}}", + success = CRM_CUSTOMER_CREATE_SUCCESS) + public Long createCustomer(CrmCustomerSaveReqVO createReqVO, Long userId) { + createReqVO.setId(null); + // 1. 校验拥有客户是å¦åˆ°è¾¾ä¸Šé™ + validateCustomerExceedOwnerLimit(createReqVO.getOwnerUserId(), 1); + + // 2. æ’入客户 + CrmCustomerDO customer = initCustomer(createReqVO, userId); + customerMapper.insert(customer); + + // 3. 创建数æ®æƒé™ + permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()) + .setBizId(customer.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当å‰æ“作的人为负责人 + + // 4. 记录æ“作日志上下文 + LogRecordContext.putVariable("customer", customer); + return customer.getId(); + } + + /** + * åˆå§‹åŒ–客户的通用字段 + * + * @param customer å®¢æˆ·ä¿¡æ¯ + * @param ownerUserId è´Ÿè´£äººç¼–å· + * @return å®¢æˆ·ä¿¡æ¯ DO + */ + private static CrmCustomerDO initCustomer(Object customer, Long ownerUserId) { + return BeanUtils.toBean(customer, CrmCustomerDO.class).setOwnerUserId(ownerUserId) + .setLockStatus(false).setDealStatus(false).setContactLastTime(LocalDateTime.now()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", + success = CRM_CUSTOMER_UPDATE_SUCCESS) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE) + public void updateCustomer(CrmCustomerSaveReqVO updateReqVO) { + Assert.notNull(updateReqVO.getId(), "客户编å·ä¸èƒ½ä¸ºç©º"); + updateReqVO.setOwnerUserId(null); // 更新的时候,è¦æŠŠ updateReqVO 负责人设置为空,é¿å…修改 + // 1. 校验存在 + CrmCustomerDO oldCustomer = validateCustomerExists(updateReqVO.getId()); + + // 2. 更新客户 + CrmCustomerDO updateObj = BeanUtils.toBean(updateReqVO, CrmCustomerDO.class); + customerMapper.updateById(updateObj); + + // 3. 记录æ“作日志上下文 + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldCustomer, CrmCustomerSaveReqVO.class)); + LogRecordContext.putVariable("customerName", oldCustomer.getName()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_DELETE_SUB_TYPE, bizNo = "{{#id}}", + success = CRM_CUSTOMER_DELETE_SUCCESS) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) + public void deleteCustomer(Long id) { + // 1.1 校验存在 + CrmCustomerDO customer = validateCustomerExists(id); + // 1.2 检查引用 + checkCustomerReference(id); + + // 2. 删除 + customerMapper.deleteById(id); + // 3. 删除数æ®æƒé™ + permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), id); + + // 4. 记录æ“作日志上下文 + LogRecordContext.putVariable("customerName", customer.getName()); + } + + /** + * 校验客户是å¦è¢«å¼•ç”¨ + * + * @param id å®¢æˆ·ç¼–å· + */ + private void checkCustomerReference(Long id) { + if (contactService.getContactCountByCustomerId(id) > 0) { + throw exception(CUSTOMER_DELETE_FAIL_HAVE_REFERENCE, CrmBizTypeEnum.CRM_CONTACT.getName()); + } + if (businessService.getBusinessCountByCustomerId(id) > 0) { + throw exception(CUSTOMER_DELETE_FAIL_HAVE_REFERENCE, CrmBizTypeEnum.CRM_BUSINESS.getName()); + } + if (contractService.getContractCountByCustomerId(id) > 0) { + throw exception(CUSTOMER_DELETE_FAIL_HAVE_REFERENCE, CrmBizTypeEnum.CRM_CONTRACT.getName()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.id}}", + success = CRM_CUSTOMER_TRANSFER_SUCCESS) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER) + public void transferCustomer(CrmCustomerTransferReqVO reqVO, Long userId) { + // 1.1 校验客户是å¦å­˜åœ¨ + CrmCustomerDO customer = validateCustomerExists(reqVO.getId()); + // 1.2 校验拥有客户是å¦åˆ°è¾¾ä¸Šé™ + validateCustomerExceedOwnerLimit(reqVO.getNewOwnerUserId(), 1); + + // 2.1 æ•°æ®æƒé™è½¬ç§» + permissionService.transferPermission( + CrmCustomerConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())); + // 2.2 转移åŽé‡æ–°è®¾ç½®è´Ÿè´£äºº + customerMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId()); + + // 3. 记录转移日志 + LogRecordContext.putVariable("customer", customer); + } + + @Override + @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_LOCK_SUB_TYPE, bizNo = "{{#lockReqVO.id}}", + success = CRM_CUSTOMER_LOCK_SUCCESS) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#lockReqVO.id", level = CrmPermissionLevelEnum.OWNER) + public void lockCustomer(CrmCustomerLockReqVO lockReqVO, Long userId) { + // 1.1 校验当å‰å®¢æˆ·æ˜¯å¦å­˜åœ¨ + CrmCustomerDO customer = validateCustomerExists(lockReqVO.getId()); + // 1.2 校验当å‰æ˜¯å¦é‡å¤æ“作é”定/解é”çŠ¶æ€ + if (customer.getLockStatus().equals(lockReqVO.getLockStatus())) { + throw exception(customer.getLockStatus() ? CUSTOMER_LOCK_FAIL_IS_LOCK : CUSTOMER_UNLOCK_FAIL_IS_UNLOCK); + } + // 1.3 校验é”定上é™ã€‚ + if (lockReqVO.getLockStatus()) { + validateCustomerExceedLockLimit(userId); + } + + // 2. æ›´æ–°é”å®šçŠ¶æ€ + customerMapper.updateById(BeanUtils.toBean(lockReqVO, CrmCustomerDO.class)); + + // 3. 记录æ“作日志上下文 + // tips: 因为这里使用的是è€çš„状æ€æ‰€ä»¥è®°å½•æ—¶åç€è®°å½•ï¼Œä¹Ÿå°±æ˜¯ lockStatus 为 true 那么就是解é”å之为é”定 + LogRecordContext.putVariable("customer", customer); + } + + @Override + public void updateCustomerFollowUp(CrmUpdateFollowUpReqBO customerUpdateFollowUpReqBO) { + customerMapper.updateById(BeanUtils.toBean(customerUpdateFollowUpReqBO, CrmCustomerDO.class).setId(customerUpdateFollowUpReqBO.getBizId())); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_CREATE_SUB_TYPE, bizNo = "{{#customer.id}}", + success = CRM_CUSTOMER_CREATE_SUCCESS) + public Long createCustomer(CrmCustomerCreateReqBO customerCreateReq, Long userId) { + // 1. æ’入客户 + CrmCustomerDO customer = BeanUtils.toBean(customerCreateReq, CrmCustomerDO.class).setOwnerUserId(userId) + .setLockStatus(false).setDealStatus(false).setReceiveTime(LocalDateTime.now()); + customerMapper.insert(customer); + + // 2. 创建数æ®æƒé™ + permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()) + .setBizId(customer.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当å‰æ“作的人为负责人 + + // 3. 记录æ“作日志上下文 + LogRecordContext.putVariable("customer", customer); + return customer.getId(); + } + + @Override + public CrmCustomerImportRespVO importCustomerList(List importCustomers, CrmCustomerImportReqVO importReqVO) { + if (CollUtil.isEmpty(importCustomers)) { + throw exception(CUSTOMER_IMPORT_LIST_IS_EMPTY); + } + CrmCustomerImportRespVO respVO = CrmCustomerImportRespVO.builder().createCustomerNames(new ArrayList<>()) + .updateCustomerNames(new ArrayList<>()).failureCustomerNames(new LinkedHashMap<>()).build(); + importCustomers.forEach(importCustomer -> { + // 校验,判断是å¦æœ‰ä¸ç¬¦åˆçš„原因 + // TODO @puhui999:å¯ä»¥ç”¨ ValidationUtils åšå‚数校验;å¯èƒ½è¦å°è£…一个方法,返回 message;这样的è¯ï¼Œå°±å¯ä»¥åœ¨ CrmCustomerImportExcelVO 写需è¦æ ¡éªŒçš„å‚数啦; + try { + validateCustomerForCreate(importCustomer); + } catch (ServiceException ex) { + respVO.getFailureCustomerNames().put(importCustomer.getName(), ex.getMessage()); + return; + } + // 情况一:判断如果ä¸å­˜åœ¨ï¼Œåœ¨è¿›è¡Œæ’å…¥ + CrmCustomerDO existCustomer = customerMapper.selectByCustomerName(importCustomer.getName()); + if (existCustomer == null) { + // 1.1 æ’å…¥å®¢æˆ·ä¿¡æ¯ + CrmCustomerDO customer = initCustomer(importCustomer, importReqVO.getOwnerUserId()); + customerMapper.insert(customer); + respVO.getCreateCustomerNames().add(importCustomer.getName()); + // 1.2 创建数æ®æƒé™ + if (importReqVO.getOwnerUserId() != null) { + permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()) + .setBizId(customer.getId()).setUserId(importReqVO.getOwnerUserId()).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); + } + // 1.3 记录æ“作日志 + getSelf().importCustomerLog(customer, false); + return; + } + + // 情况二:如果存在,判断是å¦å…许更新 + if (!importReqVO.getUpdateSupport()) { + respVO.getFailureCustomerNames().put(importCustomer.getName(), + StrUtil.format(CUSTOMER_NAME_EXISTS.getMsg(), importCustomer.getName())); + return; + } + // 2.1 æ›´æ–°å®¢æˆ·ä¿¡æ¯ + CrmCustomerDO updateCustomer = BeanUtils.toBean(importCustomer, CrmCustomerDO.class) + .setId(existCustomer.getId()); + customerMapper.updateById(updateCustomer); + respVO.getUpdateCustomerNames().add(importCustomer.getName()); + // 2.2 记录æ“作日志 + getSelf().importCustomerLog(updateCustomer, true); + }); + return respVO; + } + + /** + * 记录导入客户时的æ“作日志 + * + * @param customer å®¢æˆ·ä¿¡æ¯ + * @param isUpdate 是å¦æ›´æ–°ï¼›true - 更新,false - 新增 + */ + @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_IMPORT_SUB_TYPE, bizNo = "{{#customer.id}}", + success = CRM_CUSTOMER_IMPORT_SUCCESS) + public void importCustomerLog(CrmCustomerDO customer, boolean isUpdate) { + LogRecordContext.putVariable("customer", customer); + LogRecordContext.putVariable("isUpdate", isUpdate); + } + + private void validateCustomerForCreate(CrmCustomerImportExcelVO importCustomer) { + // 校验客户å称ä¸èƒ½ä¸ºç©º + if (StrUtil.isEmptyIfStr(importCustomer.getName())) { + throw exception(CUSTOMER_CREATE_NAME_NOT_NULL); + } + } + + // ==================== 公海相关æ“作 ==================== + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_POOL_SUB_TYPE, bizNo = "{{#id}}", + success = CRM_CUSTOMER_POOL_SUCCESS) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) + public void putCustomerPool(Long id) { + // 1. 校验存在 + CrmCustomerDO customer = customerMapper.selectById(id); + if (customer == null) { + throw exception(CUSTOMER_NOT_EXISTS); + } + // 1.2. 校验是å¦ä¸ºå…¬æµ·æ•°æ® + validateCustomerOwnerExists(customer, true); + // 1.3. 校验客户是å¦é”定 + validateCustomerIsLocked(customer, true); + + // 2. 客户放入公海 + putCustomerPool(customer); + + // 记录æ“作日志上下文 + LogRecordContext.putVariable("customerName", customer.getName()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void receiveCustomer(List ids, Long ownerUserId, Boolean isReceive) { + if (!isReceive && !CrmPermissionUtils.isCrmAdmin()) { // åªæœ‰ç®¡ç†å‘˜å¯ä»¥åˆ†é… + throw exception(CRM_PERMISSION_DENIED, CrmBizTypeEnum.CRM_CUSTOMER.getName()); + } + + // 1.1 校验存在 + List customers = customerMapper.selectBatchIds(ids); + if (customers.size() != ids.size()) { + throw exception(CUSTOMER_NOT_EXISTS); + } + // 1.2. 校验负责人是å¦å­˜åœ¨ + adminUserApi.validateUserList(singletonList(ownerUserId)); + // 1.3. æ ¡éªŒçŠ¶æ€ + customers.forEach(customer -> { + // 校验是å¦å·²æœ‰è´Ÿè´£äºº + validateCustomerOwnerExists(customer, false); + // 校验是å¦é”定 + validateCustomerIsLocked(customer, false); + // 校验æˆäº¤çŠ¶æ€ + validateCustomerDeal(customer); + }); + // 1.4 校验负责人是å¦åˆ°è¾¾ä¸Šé™ + validateCustomerExceedOwnerLimit(ownerUserId, customers.size()); + + // 2.1 领å–å…¬æµ·æ•°æ® + List updateCustomers = new ArrayList<>(); + List createPermissions = new ArrayList<>(); + customers.forEach(customer -> { + // 2.1. 设置负责人 + updateCustomers.add(new CrmCustomerDO().setId(customer.getId()).setOwnerUserId(ownerUserId)); + // 2.2. 创建负责人数æ®æƒé™ + createPermissions.add(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()) + .setBizId(customer.getId()).setUserId(ownerUserId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); + }); + // 2.2 更新客户负责人 + customerMapper.updateBatch(updateCustomers); + // 2.3 创建负责人数æ®æƒé™ + permissionService.createPermissionBatch(createPermissions); + // TODO @芋艿:è¦ä¸è¦å¤„ç†å…³è”çš„è”系人??? + + // 3. 记录æ“作日志 + AdminUserRespDTO user = null; + if (!isReceive) { + user = adminUserApi.getUser(ownerUserId).getCheckedData(); + } + for (CrmCustomerDO customer : customers) { + getSelf().receiveCustomerLog(customer, user == null ? null : user.getNickname()); + } + } + + @Override + public int autoPutCustomerPool() { + CrmCustomerPoolConfigDO poolConfig = customerPoolConfigService.getCustomerPoolConfig(); + if (poolConfig == null || !poolConfig.getEnabled()) { + return 0; + } + // 1.1 获å–没有é”定的ä¸åœ¨å…¬æµ·çš„客户 + List customerList = customerMapper.selectListByLockAndNotPool(Boolean.FALSE); + // TODO @puhui999:下é¢ä¹Ÿæžåˆ° sql 里去哈;写 or 查询,问题ä¸å¤§çš„;低 393 到 402;原因是,é¿å…无用的太多数æ®æŸ¥è¯¢åˆ° java 进程里; + List poolCustomerList = new ArrayList<>(); + poolCustomerList.addAll(filterList(customerList, customer -> + !customer.getDealStatus() && (poolConfig.getDealExpireDays() - LocalDateTimeUtils.between(customer.getCreateTime())) <= 0)); + poolCustomerList.addAll(filterList(customerList, customer -> { + if (!customer.getDealStatus()) { // 这里åªå¤„ç†æˆäº¤çš„ + return false; + } + LocalDateTime lastTime = ObjUtil.defaultIfNull(customer.getContactLastTime(), customer.getCreateTime()); + return (poolConfig.getContactExpireDays() - LocalDateTimeUtils.between(lastTime)) <= 0; + })); + + // 2. é€ä¸ªæ”¾å…¥å…¬æµ· + int count = 0; + for (CrmCustomerDO customer : poolCustomerList) { + try { + getSelf().putCustomerPool(customer); + count++; + } catch (Throwable e) { + log.error("[autoPutCustomerPool][Customer 客户({}) 放入公海异常]", customer.getId(), e); + } + } + return count; + } + + private void putCustomerPool(CrmCustomerDO customer) { + // 1. 设置负责人为 NULL + int updateOwnerUserIncr = customerMapper.updateOwnerUserIdById(customer.getId(), null); + if (updateOwnerUserIncr == 0) { + throw exception(CUSTOMER_UPDATE_OWNER_USER_FAIL); + } + // 2. 删除负责人数æ®æƒé™ + permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), customer.getId(), + CrmPermissionLevelEnum.OWNER.getLevel()); + + // 3. è”系人的负责人,也è¦è®¾ç½®ä¸º null。因为:因为领å–åŽï¼Œè´Ÿè´£äººä¹Ÿè¦å…³è”过æ¥ï¼Œè¿™å—å’Œ receiveCustomer 是对应的 + contactService.updateOwnerUserIdByCustomerId(customer.getId(), null); + } + + @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_RECEIVE_SUB_TYPE, bizNo = "{{#customer.id}}", + success = CRM_CUSTOMER_RECEIVE_SUCCESS) + public void receiveCustomerLog(CrmCustomerDO customer, String ownerUserName) { + // 记录æ“作日志上下文 + LogRecordContext.putVariable("customer", customer); + LogRecordContext.putVariable("ownerUserName", ownerUserName); + } + + //======================= 查询相关 ======================= + + @Override + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.READ) + public CrmCustomerDO getCustomer(Long id) { + return customerMapper.selectById(id); + } + + @Override + public List getCustomerList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + return customerMapper.selectBatchIds(ids); + } + + @Override + public PageResult getCustomerPage(CrmCustomerPageReqVO pageReqVO, Long userId) { + return customerMapper.selectPage(pageReqVO, userId); + } + + public PageResult getPutInPoolRemindCustomerPage(CrmCustomerPageReqVO pageReqVO, + CrmCustomerPoolConfigDO poolConfigDO, + Long userId) { + return customerMapper.selectPutInPoolRemindCustomerPage(pageReqVO, poolConfigDO, userId); + } + + // ======================= 校验相关 ======================= + + /** + * 校验客户是å¦å­˜åœ¨ + * + * @param customerId 客户 id + */ + @Override + public void validateCustomer(Long customerId) { + validateCustomerExists(customerId); + } + + private void validateCustomerOwnerExists(CrmCustomerDO customer, Boolean pool) { + if (customer == null) { // 防御一下 + throw exception(CUSTOMER_NOT_EXISTS); + } + // 校验是å¦ä¸ºå…¬æµ·æ•°æ® + if (pool && customer.getOwnerUserId() == null) { + throw exception(CUSTOMER_IN_POOL, customer.getName()); + } + // 负责人已存在 + if (!pool && customer.getOwnerUserId() != null) { + throw exception(CUSTOMER_OWNER_EXISTS, customer.getName()); + } + } + + private CrmCustomerDO validateCustomerExists(Long id) { + CrmCustomerDO customerDO = customerMapper.selectById(id); + if (customerDO == null) { + throw exception(CUSTOMER_NOT_EXISTS); + } + return customerDO; + } + + private void validateCustomerIsLocked(CrmCustomerDO customer, Boolean pool) { + if (customer.getLockStatus()) { + throw exception(pool ? CUSTOMER_LOCKED_PUT_POOL_FAIL : CUSTOMER_LOCKED, customer.getName()); + } + } + + private void validateCustomerDeal(CrmCustomerDO customer) { + if (customer.getDealStatus()) { + throw exception(CUSTOMER_ALREADY_DEAL); + } + } + + /** + * 校验用户拥有的客户数é‡ï¼Œæ˜¯å¦åˆ°è¾¾ä¸Šé™ + * + * @param userId ç”¨æˆ·ç¼–å· + * @param newCount é™„åŠ æ•°é‡ + */ + private void validateCustomerExceedOwnerLimit(Long userId, int newCount) { + List limitConfigs = customerLimitConfigService.getCustomerLimitConfigListByUserId( + CUSTOMER_OWNER_LIMIT.getType(), userId); + if (CollUtil.isEmpty(limitConfigs)) { + return; + } + Long ownerCount = customerMapper.selectCountByDealStatusAndOwnerUserId(null, userId); + Long dealOwnerCount = customerMapper.selectCountByDealStatusAndOwnerUserId(true, userId); + limitConfigs.forEach(limitConfig -> { + long nowCount = limitConfig.getDealCountEnabled() ? ownerCount : ownerCount - dealOwnerCount; + if (nowCount + newCount > limitConfig.getMaxCount()) { + throw exception(CUSTOMER_OWNER_EXCEED_LIMIT); + } + }); + } + + /** + * 校验用户é”定的客户数é‡ï¼Œæ˜¯å¦åˆ°è¾¾ä¸Šé™ + * + * @param userId ç”¨æˆ·ç¼–å· + */ + private void validateCustomerExceedLockLimit(Long userId) { + List limitConfigs = customerLimitConfigService.getCustomerLimitConfigListByUserId( + CUSTOMER_LOCK_LIMIT.getType(), userId); + if (CollUtil.isEmpty(limitConfigs)) { + return; + } + Long lockCount = customerMapper.selectCountByLockStatusAndOwnerUserId(true, userId); + Integer maxCount = CollectionUtils.getMaxValue(limitConfigs, CrmCustomerLimitConfigDO::getMaxCount); + assert maxCount != null; + if (lockCount >= maxCount) { + throw exception(CUSTOMER_LOCK_EXCEED_LIMIT); + } + } + + + /** + * 获得自身的代ç†å¯¹è±¡ï¼Œè§£å†³ AOP 生效问题 + * + * @return 自己 + */ + private CrmCustomerServiceImpl getSelf() { + return SpringUtil.getBean(getClass()); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/bo/CrmCustomerCreateReqBO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/bo/CrmCustomerCreateReqBO.java new file mode 100644 index 000000000..6d80b0e4c --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/bo/CrmCustomerCreateReqBO.java @@ -0,0 +1,125 @@ +package cn.iocoder.yudao.module.crm.service.customer.bo; + +import cn.iocoder.yudao.framework.common.validation.Mobile; +import cn.iocoder.yudao.framework.common.validation.Telephone; +import cn.iocoder.yudao.module.crm.enums.DictTypeConstants; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 客户创建 Create Req BO + * + * @author HUIHUI + */ +@Data +public class CrmCustomerCreateReqBO { + + /** + * 客户å称 + */ + @NotEmpty(message = "客户å称ä¸èƒ½ä¸ºç©º") + private String name; + /** + * è·Ÿè¿›çŠ¶æ€ + */ + private Boolean followUpStatus; + /** + * é”å®šçŠ¶æ€ + */ + private Boolean lockStatus; + /** + * æˆäº¤çŠ¶æ€ + */ + private Boolean dealStatus; + /** + * 所属行业 + * + * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_INDUSTRY} + */ + private Integer industryId; + /** + * 客户等级 + * + * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_LEVEL} + */ + private Integer level; + /** + * 客户æ¥æº + * + * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_SOURCE} + */ + private Integer source; + + /** + * 手机 + */ + @Mobile + private String mobile; + /** + * ç”µè¯ + */ + @Telephone + private String telephone; + /** + * ç½‘å€ + */ + private String website; + /** + * QQ + */ + private String qq; + /** + * wechat + */ + private String wechat; + + /** + * 邮箱 + */ + @Email(message = "邮箱格å¼ä¸æ­£ç¡®") + private String email; + + /** + * 客户æè¿° + */ + @Size(max = 4096, message = "客户æ述长度ä¸èƒ½è¶…过 4096 个字符") + private String description; + /** + * 备注 + */ + private String remark; + /** + * è´Ÿè´£äººçš„ç”¨æˆ·ç¼–å· + * + * å…³è” AdminUserDO çš„ id 字段 + */ + private Long ownerUserId; + /** + * 所在地 + * + * å…³è” {@link cn.iocoder.yudao.framework.ip.core.Area#getId()} 字段 + */ + private Integer areaId; + /** + * è¯¦ç»†åœ°å€ + */ + private String detailAddress; + + /** + * 最åŽè·Ÿè¿›æ—¶é—´ + */ + private LocalDateTime contactLastTime; + /** + * 最åŽè·Ÿè¿›å†…容 + */ + private String contactLastContent; + /** + * 下次è”系时间 + */ + private LocalDateTime contactNextTime; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordService.java new file mode 100644 index 000000000..d2a7cb8f9 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordService.java @@ -0,0 +1,76 @@ +package cn.iocoder.yudao.module.crm.service.followup; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordSaveReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmFollowUpCreateReqBO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; + +/** + * 跟进记录 Service æŽ¥å£ + * + * @author 芋é“æºç  + */ +public interface CrmFollowUpRecordService { + + /** + * 创建跟进记录 (æ•°æ®æƒé™åŸºäºŽ bizType〠bizId) + * + * @param createReqVO åˆ›å»ºä¿¡æ¯ + * @return ç¼–å· + */ + Long createFollowUpRecord(@Valid CrmFollowUpRecordSaveReqVO createReqVO); + + /** + * 创建更进 + * + * @param list 请求 + */ + void createFollowUpRecordBatch(List list); + + /** + * 删除跟进记录 (æ•°æ®æƒé™åŸºäºŽ bizType〠bizId) + * + * @param id ç¼–å· + * @param userId ç”¨æˆ·ç¼–å· + */ + void deleteFollowUpRecord(Long id, Long userId); + + /** + * 删除跟进 + * + * @param bizType 模å—类型 + * @param bizId 模å—æ•°æ®ç¼–å· + */ + void deleteFollowUpRecordByBiz(Integer bizType, Long bizId); + + /** + * 获得跟进记录 + * + * @param id ç¼–å· + * @return 跟进记录 + */ + CrmFollowUpRecordDO getFollowUpRecord(Long id); + + /** + * 获得跟进记录分页 (æ•°æ®æƒé™åŸºäºŽ bizType〠bizId) + * + * @param pageReqVO 分页查询 + * @return 跟进记录分页 + */ + PageResult getFollowUpRecordPage(CrmFollowUpRecordPageReqVO pageReqVO); + + /** + * 获å–跟进记录 + * + * @param bizType 模å—类型 + * @param bizIds 模å—æ•°æ®ç¼–å· + * @return 跟进列表 + */ + List getFollowUpRecordByBiz(Integer bizType, Collection bizIds); + +} \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordServiceImpl.java new file mode 100644 index 000000000..88f0b887c --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordServiceImpl.java @@ -0,0 +1,158 @@ +package cn.iocoder.yudao.module.crm.service.followup; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordSaveReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO; +import cn.iocoder.yudao.module.crm.dal.mysql.followup.CrmFollowUpRecordMapper; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; +import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; +import cn.iocoder.yudao.module.crm.service.clue.CrmClueService; +import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; +import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; +import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmFollowUpCreateReqBO; +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; +import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; +import jakarta.annotation.Resource; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.FOLLOW_UP_RECORD_DELETE_DENIED; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.FOLLOW_UP_RECORD_NOT_EXISTS; + +/** + * 跟进记录 Service 实现类 + * + * @author 芋é“æºç  + */ +@Service +@Validated +public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService { + + @Resource + private CrmFollowUpRecordMapper crmFollowUpRecordMapper; + + @Resource + @Lazy + private CrmPermissionService permissionService; + @Resource + @Lazy + private CrmBusinessService businessService; + @Resource + @Lazy + private CrmClueService clueService; + @Resource + @Lazy + private CrmContactService contactService; + @Resource + @Lazy + private CrmContractService contractService; + @Resource + @Lazy + private CrmCustomerService customerService; + + @Override + @CrmPermission(bizTypeValue = "#createReqVO.bizType", bizId = "#createReqVO.bizId", level = CrmPermissionLevelEnum.WRITE) + public Long createFollowUpRecord(CrmFollowUpRecordSaveReqVO createReqVO) { + // 创建更进记录 + CrmFollowUpRecordDO followUpRecord = BeanUtils.toBean(createReqVO, CrmFollowUpRecordDO.class); + crmFollowUpRecordMapper.insert(followUpRecord); + + LocalDateTime now = LocalDateTime.now(); + CrmUpdateFollowUpReqBO updateFollowUpReqBO = new CrmUpdateFollowUpReqBO().setBizId(followUpRecord.getBizId()) + .setContactLastTime(now).setContactNextTime(followUpRecord.getNextTime()).setContactLastContent(followUpRecord.getContent()); + // 2. æ›´æ–° bizId 对应的记录; + if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_BUSINESS.getType(), followUpRecord.getBizType())) { // æ›´æ–°å•†æœºè·Ÿè¿›ä¿¡æ¯ + businessService.updateBusinessFollowUpBatch(Collections.singletonList(updateFollowUpReqBO)); + } + if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_LEADS.getType(), followUpRecord.getBizType())) { // æ›´æ–°çº¿ç´¢è·Ÿè¿›ä¿¡æ¯ + clueService.updateClueFollowUp(updateFollowUpReqBO); + } + if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_CONTACT.getType(), followUpRecord.getBizType())) { // æ›´æ–°è”ç³»äººè·Ÿè¿›ä¿¡æ¯ + contactService.updateContactFollowUpBatch(Collections.singletonList(updateFollowUpReqBO)); + } + if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_CONTRACT.getType(), followUpRecord.getBizType())) { // æ›´æ–°åˆåŒè·Ÿè¿›ä¿¡æ¯ + contractService.updateContractFollowUp(updateFollowUpReqBO); + } + if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_CUSTOMER.getType(), followUpRecord.getBizType())) { // æ›´æ–°å®¢æˆ·è·Ÿè¿›ä¿¡æ¯ + customerService.updateCustomerFollowUp(updateFollowUpReqBO); + } + + // 3.1 æ›´æ–° contactIds 对应的记录,ä¸æ›´æ–° lastTime å’Œ lastContent + if (CollUtil.isNotEmpty(createReqVO.getContactIds())) { + contactService.updateContactFollowUpBatch(convertList(createReqVO.getContactIds(), + contactId -> updateFollowUpReqBO.setBizId(contactId).setContactLastTime(null).setContactLastContent(null))); + } + // 3.2 需è¦æ›´æ–° businessIds 对应的记录,ä¸æ›´æ–° lastTime å’Œ lastContent + if (CollUtil.isNotEmpty(createReqVO.getBusinessIds())) { + businessService.updateBusinessFollowUpBatch(convertList(createReqVO.getBusinessIds(), + businessId -> updateFollowUpReqBO.setBizId(businessId).setContactLastTime(null).setContactLastContent(null))); + } + return followUpRecord.getId(); + } + + @Override + public void createFollowUpRecordBatch(List list) { + if (CollUtil.isEmpty(list)) { + return; + } + crmFollowUpRecordMapper.insertBatch(BeanUtils.toBean(list, CrmFollowUpRecordDO.class)); + } + + @Override + public void deleteFollowUpRecord(Long id, Long userId) { + // 校验存在 + CrmFollowUpRecordDO followUpRecord = validateFollowUpRecordExists(id); + // 校验æƒé™ + if (!permissionService.hasPermission(followUpRecord.getBizType(), followUpRecord.getBizId(), userId, CrmPermissionLevelEnum.OWNER)) { + throw exception(FOLLOW_UP_RECORD_DELETE_DENIED); + } + + // 删除 + crmFollowUpRecordMapper.deleteById(id); + } + + @Override + public void deleteFollowUpRecordByBiz(Integer bizType, Long bizId) { + crmFollowUpRecordMapper.deleteByBiz(bizType, bizId); + } + + private CrmFollowUpRecordDO validateFollowUpRecordExists(Long id) { + CrmFollowUpRecordDO followUpRecord = crmFollowUpRecordMapper.selectById(id); + if (followUpRecord == null) { + throw exception(FOLLOW_UP_RECORD_NOT_EXISTS); + } + return followUpRecord; + } + + @Override + public CrmFollowUpRecordDO getFollowUpRecord(Long id) { + return crmFollowUpRecordMapper.selectById(id); + } + + @Override + @CrmPermission(bizTypeValue = "#pageReqVO.bizType", bizId = "#pageReqVO.bizId", level = CrmPermissionLevelEnum.READ) + public PageResult getFollowUpRecordPage(CrmFollowUpRecordPageReqVO pageReqVO) { + return crmFollowUpRecordMapper.selectPage(pageReqVO); + } + + @Override + public List getFollowUpRecordByBiz(Integer bizType, Collection bizIds) { + return crmFollowUpRecordMapper.selectListByBiz(bizType, bizIds); + } + +} \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/bo/CrmFollowUpCreateReqBO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/bo/CrmFollowUpCreateReqBO.java new file mode 100644 index 000000000..dec219e29 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/bo/CrmFollowUpCreateReqBO.java @@ -0,0 +1,78 @@ +package cn.iocoder.yudao.module.crm.service.followup.bo; + +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; +import cn.iocoder.yudao.module.crm.enums.DictTypeConstants; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * è·Ÿè¿›ä¿¡æ¯ Create Req BO + * + * @author HUIHUI + */ +@Data +public class CrmFollowUpCreateReqBO { + + /** + * æ•°æ®ç±»åž‹ + * + * 枚举 {@link CrmBizTypeEnum} + */ + @NotNull(message = "æ•°æ®ç±»åž‹ä¸èƒ½ä¸ºç©º") + private Integer bizType; + /** + * æ•°æ®ç¼–å· + * + * å…³è” {@link CrmBizTypeEnum} å¯¹åº”æ¨¡å— DO çš„ id 字段 + */ + @NotNull(message = "æ•°æ®ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long bizId; + + /** + * 跟进类型 + * + * å…³è” {@link DictTypeConstants#CRM_FOLLOW_UP_TYPE} å­—å…¸ + */ + @NotNull(message = "跟进类型ä¸èƒ½ä¸ºç©º") + private Integer type; + /** + * 跟进内容 + */ + @NotEmpty(message = "跟进内容ä¸èƒ½ä¸ºç©º") + private String content; + /** + * 下次è”系时间 + */ + @NotNull(message = "下次è”系时间ä¸èƒ½ä¸ºç©º") + private LocalDateTime nextTime; + + /** + * 图片 + */ + private List picUrls; + /** + * 附件 + */ + private List fileUrls; + + /** + * å…³è”的商机编å·æ•°ç»„ + * + * å…³è” {@link CrmBusinessDO#getId()} + */ + private List businessIds; + + /** + * å…³è”çš„è”系人编å·æ•°ç»„ + * + * å…³è” {@link CrmContactDO#getId()} + */ + private List contactIds; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/bo/CrmUpdateFollowUpReqBO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/bo/CrmUpdateFollowUpReqBO.java new file mode 100644 index 000000000..57f1849eb --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/bo/CrmUpdateFollowUpReqBO.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.crm.service.followup.bo; + +import com.mzt.logapi.starter.annotation.DiffLogField; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * è·Ÿè¿›ä¿¡æ¯ Update Req BO + * + * @author HUIHUI + */ +@Data +public class CrmUpdateFollowUpReqBO { + + @Schema(description = "æ•°æ®ç¼–å·", example = "3167") + @NotNull(message = "æ•°æ®ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long bizId; + + @Schema(description = "最åŽè·Ÿè¿›æ—¶é—´") + @DiffLogField(name = "最åŽè·Ÿè¿›æ—¶é—´") + private LocalDateTime contactLastTime; + + @Schema(description = "下次è”系时间") + @DiffLogField(name = "下次è”系时间") + private LocalDateTime contactNextTime; + + @Schema(description = "最åŽæ›´è¿›å†…容") + @DiffLogField(name = "最åŽæ›´è¿›å†…容") + private String contactLastContent; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/message/CrmBacklogService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/message/CrmBacklogService.java new file mode 100644 index 000000000..f13514c25 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/message/CrmBacklogService.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.crm.service.message; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.crm.controller.admin.backlog.vo.CrmTodayCustomerPageReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import jakarta.validation.Valid; + +/** + * CRM å¾…åŠžæ¶ˆæ¯ Service æŽ¥å£ + * + * @author dhb52 + */ +public interface CrmBacklogService { + + /** + * æ ¹æ®ã€è”系状æ€ã€‘ã€ã€åœºæ™¯ç±»åž‹ã€‘筛选客户分页 + * + * @param pageReqVO 分页查询 + * @return åˆ†é¡µæ•°æ® + */ + PageResult getTodayCustomerPage(@Valid CrmTodayCustomerPageReqVO pageReqVO, Long userId); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/message/CrmBacklogServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/message/CrmBacklogServiceImpl.java new file mode 100644 index 000000000..5c2417266 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/message/CrmBacklogServiceImpl.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.crm.service.message; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.crm.controller.admin.backlog.vo.CrmTodayCustomerPageReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; +import org.springframework.validation.annotation.Validated; + +/** + * å¾…åŠžæ¶ˆæ¯ Service 实现类 + * + * @author dhb52 + */ +@Component +@Validated +public class CrmBacklogServiceImpl implements CrmBacklogService { + + @Resource + private CrmCustomerMapper customerMapper; + + @Override + public PageResult getTodayCustomerPage(CrmTodayCustomerPageReqVO pageReqVO, Long userId) { + return customerMapper.selectTodayCustomerPage(pageReqVO, userId); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionService.java new file mode 100644 index 000000000..39b101323 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionService.java @@ -0,0 +1,122 @@ +package cn.iocoder.yudao.module.crm.service.permission; + + +import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; + +/** + * crm æ•°æ®æƒé™ Service æŽ¥å£ + * + * @author HUIHUI + */ +public interface CrmPermissionService { + + /** + * 创建数æ®æƒé™ + * + * @param createReqBO åˆ›å»ºä¿¡æ¯ + * @return ç¼–å· + */ + Long createPermission(@Valid CrmPermissionCreateReqBO createReqBO); + + /** + * 创建数æ®æƒé™ + * + * @param createReqBOs åˆ›å»ºä¿¡æ¯ + */ + void createPermissionBatch(@Valid List createReqBOs); + + /** + * æ›´æ–°æ•°æ®æƒé™ + * + * @param updateReqVO æ›´æ–°ä¿¡æ¯ + */ + void updatePermission(CrmPermissionUpdateReqVO updateReqVO); + + /** + * æ•°æ®æƒé™è½¬ç§» + * + * @param crmPermissionTransferReqBO æ•°æ®æƒé™è½¬ç§»è¯·æ±‚ + */ + void transferPermission(@Valid CrmPermissionTransferReqBO crmPermissionTransferReqBO); + + /** + * 删除数æ®æƒé™ + * + * @param bizType æ•°æ®ç±»åž‹ï¼Œå…³è” {@link CrmBizTypeEnum} + * @param bizId æ•°æ®ç¼–å·ï¼Œå…³è” {@link CrmBizTypeEnum} å¯¹åº”æ¨¡å— DO#getId() + * @param level æ•°æ®æƒé™çº§åˆ«ï¼Œå…³è” {@link CrmPermissionLevelEnum} + */ + void deletePermission(Integer bizType, Long bizId, Integer level); + + /** + * 删除数æ®æƒé™ + * + * @param bizType æ•°æ®ç±»åž‹ï¼Œå…³è” {@link CrmBizTypeEnum} + * @param bizId æ•°æ®ç¼–å·ï¼Œå…³è” {@link CrmBizTypeEnum} å¯¹åº”æ¨¡å— DO#getId() + */ + void deletePermission(Integer bizType, Long bizId); + + /** + * 批é‡åˆ é™¤æ•°æ®æƒé™ + * + * @param ids æƒé™ç¼–å· + * @param userId ç”¨æˆ·ç¼–å· + */ + void deletePermissionBatch(Collection ids, Long userId); + + /** + * 删除指定用户数æ®æƒé™ + * + * @param id æƒé™ç¼–å· + * @param userId ç”¨æˆ·ç¼–å· + */ + void deleteSelfPermission(Long id, Long userId); + + /** + * 获å–æ•°æ®æƒé™åˆ—表,通过 æ•°æ®ç±»åž‹ x æŸä¸ªæ•°æ® + * + * @param bizType æ•°æ®ç±»åž‹ï¼Œå…³è” {@link CrmBizTypeEnum} + * @param bizId æ•°æ®ç¼–å·ï¼Œå…³è” {@link CrmBizTypeEnum} å¯¹åº”æ¨¡å— DO#getId() + * @return Crm æ•°æ®æƒé™åˆ—表 + */ + List getPermissionListByBiz(Integer bizType, Long bizId); + + /** + * 获å–æ•°æ®æƒé™åˆ—表,通过 æ•°æ®ç±»åž‹ x æŸä¸ªæ•°æ® + * + * @param bizType æ•°æ®ç±»åž‹ï¼Œå…³è” {@link CrmBizTypeEnum} + * @param bizIds æ•°æ®ç¼–å·ï¼Œå…³è” {@link CrmBizTypeEnum} å¯¹åº”æ¨¡å— DO#getId() + * @return Crm æ•°æ®æƒé™åˆ—表 + */ + List getPermissionListByBiz(Integer bizType, Collection bizIds); + + /** + * 获å–用户å‚与的模å—æ•°æ®åˆ—表 + * + * @param bizType 模å—类型 + * @param userId ç”¨æˆ·ç¼–å· + * @return 模å—æ•°æ®åˆ—表 + */ + List getPermissionListByBizTypeAndUserId(Integer bizType, Long userId); + + /** + * 校验是å¦æœ‰æŒ‡å®šæ•°æ®çš„æ“作æƒé™ + * + * @param bizType æ•°æ®ç±»åž‹ï¼Œå…³è” {@link CrmBizTypeEnum} + * @param bizId æ•°æ®ç¼–å·ï¼Œå…³è” {@link CrmBizTypeEnum} å¯¹åº”æ¨¡å— DO#getId() + * @param userId ç”¨æˆ·ç¼–å· + * @param level æƒé™çº§åˆ« + * @return 是å¦æœ‰æƒé™ + */ + boolean hasPermission(Integer bizType, Long bizId, Long userId, CrmPermissionLevelEnum level); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImpl.java new file mode 100644 index 000000000..ba3e50c6d --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImpl.java @@ -0,0 +1,222 @@ +package cn.iocoder.yudao.module.crm.service.permission; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO; +import cn.iocoder.yudao.module.crm.convert.permission.CrmPermissionConvert; +import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; +import cn.iocoder.yudao.module.crm.dal.mysql.permission.CrmPermissionMapper; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; +import cn.iocoder.yudao.module.crm.framework.permission.core.util.CrmPermissionUtils; +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.anyMatch; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum.isOwner; + +/** + * CRM æ•°æ®æƒé™ Service 接å£å®žçŽ°ç±» + * + * @author HUIHUI + */ +@Service +@Validated +public class CrmPermissionServiceImpl implements CrmPermissionService { + + @Resource + private CrmPermissionMapper permissionMapper; + + @Resource + private AdminUserApi adminUserApi; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createPermission(CrmPermissionCreateReqBO createReqBO) { + validatePermissionNotExists(Collections.singletonList(createReqBO)); + // 1. 校验用户是å¦å­˜åœ¨ + adminUserApi.validateUserList(Collections.singletonList(createReqBO.getUserId())); + + // 2. 创建 + CrmPermissionDO permission = BeanUtils.toBean(createReqBO, CrmPermissionDO.class); + permissionMapper.insert(permission); + return permission.getId(); + } + + @Override + public void createPermissionBatch(List createReqBOs) { + validatePermissionNotExists(createReqBOs); + // 1. 校验用户是å¦å­˜åœ¨ + adminUserApi.validateUserList(convertSet(createReqBOs, CrmPermissionCreateReqBO::getUserId)); + + // 2. 创建 + List permissions = BeanUtils.toBean(createReqBOs, CrmPermissionDO.class); + permissionMapper.insertBatch(permissions); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updatePermission(CrmPermissionUpdateReqVO updateReqVO) { + // 1. 校验存在 + validatePermissionExists(updateReqVO.getIds()); + // 2. æ›´æ–° + List updateDO = CrmPermissionConvert.INSTANCE.convertList(updateReqVO); + permissionMapper.updateBatch(updateDO); + } + + private void validatePermissionExists(Collection ids) { + List permissionList = permissionMapper.selectBatchIds(ids); + if (ObjUtil.notEqual(permissionList.size(), ids.size())) { + throw exception(CRM_PERMISSION_NOT_EXISTS); + } + } + + private void validatePermissionNotExists(Collection createReqBOs) { + Set bizTypes = convertSet(createReqBOs, CrmPermissionCreateReqBO::getBizType); + Set bizIds = convertSet(createReqBOs, CrmPermissionCreateReqBO::getBizId); + Set userIds = convertSet(createReqBOs, CrmPermissionCreateReqBO::getUserId); + Long count = permissionMapper.selectListByBiz(bizTypes, bizIds, userIds); + if (count > 0) { + throw exception(CRM_PERMISSION_CREATE_FAIL); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void transferPermission(CrmPermissionTransferReqBO transferReqBO) { + // 1. 校验数æ®æƒé™ï¼šæ˜¯å¦æ˜¯è´Ÿè´£äººï¼Œåªæœ‰è´Ÿè´£äººæ‰å¯ä»¥è½¬ç§» + CrmPermissionDO oldPermission = permissionMapper.selectByBizTypeAndBizIdByUserId( + transferReqBO.getBizType(), transferReqBO.getBizId(), transferReqBO.getUserId()); + String bizTypeName = CrmBizTypeEnum.getNameByType(transferReqBO.getBizType()); + if (oldPermission == null // ä¸æ˜¯æ‹¥æœ‰è€…,并且ä¸æ˜¯è¶…管 + || (!isOwner(oldPermission.getLevel()) && !CrmPermissionUtils.isCrmAdmin())) { + throw exception(CRM_PERMISSION_DENIED, bizTypeName); + } + // 1.1 校验转移对象是å¦å·²ç»æ˜¯è¯¥è´Ÿè´£äºº + if (ObjUtil.equal(transferReqBO.getNewOwnerUserId(), oldPermission.getUserId())) { + throw exception(CRM_PERMISSION_MODEL_TRANSFER_FAIL_OWNER_USER_EXISTS, bizTypeName); + } + // 1.2 校验新负责人是å¦å­˜åœ¨ + adminUserApi.validateUserList(Collections.singletonList(transferReqBO.getNewOwnerUserId())); + + // 2. 修改新负责人的æƒé™ + List permissions = permissionMapper.selectByBizTypeAndBizId( + transferReqBO.getBizType(), transferReqBO.getBizId()); // 获得所有数æ®æƒé™ + CrmPermissionDO permission = CollUtil.findOne(permissions, + item -> ObjUtil.equal(item.getUserId(), transferReqBO.getNewOwnerUserId())); + if (permission == null) { + permissionMapper.insert(new CrmPermissionDO().setBizType(transferReqBO.getBizType()) + .setBizId(transferReqBO.getBizId()).setUserId(transferReqBO.getNewOwnerUserId()) + .setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); + } else { + permissionMapper.updateById(new CrmPermissionDO().setId(permission.getId()) + .setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); + } + + // 3. 修改è€è´Ÿè´£äººçš„æƒé™ + if (transferReqBO.getOldOwnerPermissionLevel() != null) { + permissionMapper.updateById(new CrmPermissionDO().setId(oldPermission.getId()) + .setLevel(transferReqBO.getOldOwnerPermissionLevel())); + } else { + permissionMapper.deleteById(oldPermission.getId()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deletePermission(Integer bizType, Long bizId, Integer level) { + // 校验存在 + List permissions = permissionMapper.selectListByBizTypeAndBizIdAndLevel( + bizType, bizId, level); + if (CollUtil.isEmpty(permissions)) { + throw exception(CRM_PERMISSION_NOT_EXISTS); + } + + // 删除数æ®æƒé™ + permissionMapper.deleteBatchIds(convertSet(permissions, CrmPermissionDO::getId)); + } + + @Override + public void deletePermission(Integer bizType, Long bizId) { + int deletedCount = permissionMapper.deletePermission(bizType, bizId); + if (deletedCount == 0) { + throw exception(CRM_PERMISSION_NOT_EXISTS); + } + } + + @Override + public void deletePermissionBatch(Collection ids, Long userId) { + List permissions = permissionMapper.selectBatchIds(ids); + if (CollUtil.isEmpty(permissions)) { + throw exception(CRM_PERMISSION_NOT_EXISTS); + } + // 校验:数æ®æƒé™çš„模å—æ•°æ®ç¼–å·æ˜¯ä¸€è‡´çš„ä¸å¯èƒ½å­˜åœ¨ä¸¤ä¸ª + if (convertSet(permissions, CrmPermissionDO::getBizId).size() > 1) { + throw exception(CRM_PERMISSION_DELETE_FAIL); + } + // 校验æ“作人是å¦ä¸ºè´Ÿè´£äºº + CrmPermissionDO permission = permissionMapper.selectByBizIdAndUserId(permissions.getFirst().getBizId(), userId); + if (permission == null) { + throw exception(CRM_PERMISSION_DELETE_DENIED); + } + if (!CrmPermissionLevelEnum.isOwner(permission.getLevel())) { + throw exception(CRM_PERMISSION_DELETE_DENIED); + } + + // 删除数æ®æƒé™ + permissionMapper.deleteBatchIds(ids); + } + + @Override + public void deleteSelfPermission(Long id, Long userId) { + // 校验数æ®å­˜åœ¨ä¸”是自己 + CrmPermissionDO permission = permissionMapper.selectByIdAndUserId(id, userId); + if (permission == null) { + throw exception(CRM_PERMISSION_NOT_EXISTS); + } + // 校验是å¦æ˜¯è´Ÿè´£äºº + if (CrmPermissionLevelEnum.isOwner(permission.getLevel())) { + throw exception(CRM_PERMISSION_DELETE_SELF_PERMISSION_FAIL_EXIST_OWNER); + } + + // 删除 + permissionMapper.deleteById(id); + } + + @Override + public List getPermissionListByBiz(Integer bizType, Long bizId) { + return permissionMapper.selectByBizTypeAndBizId(bizType, bizId); + } + + @Override + public List getPermissionListByBiz(Integer bizType, Collection bizIds) { + return permissionMapper.selectByBizTypeAndBizIds(bizType, bizIds); + } + + @Override + public List getPermissionListByBizTypeAndUserId(Integer bizType, Long userId) { + return permissionMapper.selectListByBizTypeAndUserId(bizType, userId); + } + + @Override + public boolean hasPermission(Integer bizType, Long bizId, Long userId, CrmPermissionLevelEnum level) { + List permissionList = permissionMapper.selectByBizTypeAndBizId(bizType, bizId); + return anyMatch(permissionList, permission -> + ObjUtil.equal(permission.getUserId(), userId) && ObjUtil.equal(permission.getLevel(), level.getLevel())); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/bo/CrmPermissionCreateReqBO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/bo/CrmPermissionCreateReqBO.java new file mode 100644 index 000000000..f4b4bb364 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/bo/CrmPermissionCreateReqBO.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.crm.service.permission.bo; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; +import lombok.Data; + +import jakarta.validation.constraints.NotNull; + +/** + * crm æ•°æ®æƒé™ Create Req BO + * + * @author HUIHUI + */ +@Data +public class CrmPermissionCreateReqBO { + + /** + * 当å‰ç™»å½•ç”¨æˆ·ç¼–å· + */ + @NotNull(message = "用户编å·ä¸èƒ½ä¸ºç©º") + private Long userId; + + /** + * Crm 类型 + */ + @NotNull(message = "Crm 类型ä¸èƒ½ä¸ºç©º") + @InEnum(CrmBizTypeEnum.class) + private Integer bizType; + /** + * æ•°æ®ç¼–å· + */ + @NotNull(message = "Crm æ•°æ®ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long bizId; + + /** + * æƒé™çº§åˆ« + */ + @NotNull(message = "æƒé™çº§åˆ«ä¸èƒ½ä¸ºç©º") + @InEnum(CrmPermissionLevelEnum.class) + private Integer level; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/bo/CrmPermissionTransferReqBO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/bo/CrmPermissionTransferReqBO.java new file mode 100644 index 000000000..0e5933c0c --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/bo/CrmPermissionTransferReqBO.java @@ -0,0 +1,49 @@ +package cn.iocoder.yudao.module.crm.service.permission.bo; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; +import lombok.Data; + +import jakarta.validation.constraints.NotNull; + +/** + * æ•°æ®æƒé™è½¬ç§» Request BO + * + * @author HUIHUI + */ +@Data +public class CrmPermissionTransferReqBO { + + /** + * 当å‰ç™»å½•ç”¨æˆ·ç¼–å· + */ + @NotNull(message = "用户编å·ä¸èƒ½ä¸ºç©º") + private Long userId; + + /** + * CRM 类型 + */ + @NotNull(message = "Crm 类型ä¸èƒ½ä¸ºç©º") + @InEnum(CrmBizTypeEnum.class) + private Integer bizType; + /** + * æ•°æ®ç¼–å· + */ + @NotNull(message = "CRM æ•°æ®ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long bizId; + + /** + * æ–°è´Ÿè´£äººçš„ç”¨æˆ·ç¼–å· + */ + @NotNull(message = "新负责人的用户编å·ä¸èƒ½ä¸ºç©º") + private Long newOwnerUserId; + + /** + * è€è´Ÿè´£äººåŠ å…¥å›¢é˜ŸåŽçš„æƒé™çº§åˆ«ã€‚如果 null 说明移除 + * + * å…³è” {@link CrmPermissionLevelEnum} + */ + private Integer oldOwnerPermissionLevel; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductCategoryService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductCategoryService.java new file mode 100644 index 000000000..56974da11 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductCategoryService.java @@ -0,0 +1,64 @@ +package cn.iocoder.yudao.module.crm.service.product; + +import cn.iocoder.yudao.module.crm.controller.admin.product.vo.category.CrmProductCategoryCreateReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.product.vo.category.CrmProductCategoryListReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO; + +import jakarta.validation.Valid; +import java.util.Collection; +import java.util.List; + +/** + * CRM 产å“分类 Service æŽ¥å£ + * + * @author ZanGe丶 + */ +public interface CrmProductCategoryService { + + /** + * 创建产å“分类 + * + * @param createReqVO åˆ›å»ºä¿¡æ¯ + * @return ç¼–å· + */ + Long createProductCategory(@Valid CrmProductCategoryCreateReqVO createReqVO); + + /** + * 更新产å“分类 + * + * @param updateReqVO æ›´æ–°ä¿¡æ¯ + */ + void updateProductCategory(@Valid CrmProductCategoryCreateReqVO updateReqVO); + + /** + * 删除产å“分类 + * + * @param id ç¼–å· + */ + void deleteProductCategory(Long id); + + /** + * 获得产å“分类 + * + * @param id ç¼–å· + * @return 产å“分类 + */ + CrmProductCategoryDO getProductCategory(Long id); + + /** + * 获得产å“分类列表 + * + * @param listReqVO 列表请求 + * @return 产å“分类列表 + */ + List getProductCategoryList(CrmProductCategoryListReqVO listReqVO); + + /** + * 获得产å“分类列表 + * + * @param ids ç¼–å·æ•°ç»„ + * @return 产å“分类列表 + */ + List getProductCategoryList(Collection ids); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductCategoryServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductCategoryServiceImpl.java new file mode 100644 index 000000000..6399039ba --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductCategoryServiceImpl.java @@ -0,0 +1,138 @@ +package cn.iocoder.yudao.module.crm.service.product; + +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.crm.controller.admin.product.vo.category.CrmProductCategoryCreateReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.product.vo.category.CrmProductCategoryListReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO; +import cn.iocoder.yudao.module.crm.dal.mysql.product.CrmProductCategoryMapper; +import com.mzt.logapi.context.LogRecordContext; +import com.mzt.logapi.starter.annotation.LogRecord; +import jakarta.annotation.Resource; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO.PARENT_ID_NULL; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; + +/** + * CRM 产å“分类 Service 实现类 + * + * @author ZanGe丶 + */ +@Service +@Validated +public class CrmProductCategoryServiceImpl implements CrmProductCategoryService { + + @Resource(name = "crmProductCategoryMapper") + private CrmProductCategoryMapper productCategoryMapper; + + @Resource + @Lazy // 延迟加载,解决循环ä¾èµ–问题 + private CrmProductService crmProductService; + + @Override + @LogRecord(type = CRM_PRODUCT_CATEGORY_TYPE, subType = CRM_PRODUCT_CATEGORY_CREATE_SUB_TYPE, bizNo = "{{#productCategoryId}}", + success = CRM_PRODUCT_CATEGORY_CREATE_SUCCESS) + public Long createProductCategory(CrmProductCategoryCreateReqVO createReqVO) { + // 1.1 校验父分类存在 + validateParentProductCategory(createReqVO.getParentId()); + // 1.2 分类å称是å¦å­˜åœ¨ + validateProductNameExists(null, createReqVO.getParentId(), createReqVO.getName()); + + // 2. æ’入分类 + CrmProductCategoryDO category = BeanUtils.toBean(createReqVO, CrmProductCategoryDO.class); + productCategoryMapper.insert(category); + + // 3. 记录æ“作日志上下文 + LogRecordContext.putVariable("productCategoryId", category.getId()); + return category.getId(); + } + + @Override + @LogRecord(type = CRM_PRODUCT_CATEGORY_TYPE, subType = CRM_PRODUCT_CATEGORY_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", + success = CRM_PRODUCT_CATEGORY_UPDATE_SUCCESS) + public void updateProductCategory(CrmProductCategoryCreateReqVO updateReqVO) { + // 1.1 校验存在 + validateProductCategoryExists(updateReqVO.getId()); + // 1.2 校验父分类存在 + validateParentProductCategory(updateReqVO.getParentId()); + // 1.3 分类å称是å¦å­˜åœ¨ + validateProductNameExists(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName()); + + // 2. 更新分类 + CrmProductCategoryDO updateObj = BeanUtils.toBean(updateReqVO, CrmProductCategoryDO.class); + productCategoryMapper.updateById(updateObj); + } + + private void validateProductCategoryExists(Long id) { + if (productCategoryMapper.selectById(id) == null) { + throw exception(PRODUCT_CATEGORY_NOT_EXISTS); + } + } + + private void validateParentProductCategory(Long id) { + // å¦‚æžœæ˜¯æ ¹åˆ†ç±»ï¼Œæ— éœ€éªŒè¯ + if (Objects.equals(id, PARENT_ID_NULL)) { + return; + } + // 父分类ä¸å­˜åœ¨ + CrmProductCategoryDO category = productCategoryMapper.selectById(id); + if (category == null) { + throw exception(PRODUCT_CATEGORY_PARENT_NOT_EXISTS); + } + // 父分类ä¸èƒ½æ˜¯äºŒçº§åˆ†ç±» + if (!Objects.equals(category.getParentId(), PARENT_ID_NULL)) { + throw exception(PRODUCT_CATEGORY_PARENT_NOT_FIRST_LEVEL); + } + } + + private void validateProductNameExists(Long id, Long parentId, String name) { + CrmProductCategoryDO category = productCategoryMapper.selectByParentIdAndName(parentId, name); + if (category == null + || category.getId().equals(id)) { + return; + } + throw exception(PRODUCT_CATEGORY_EXISTS); + } + + @Override + @LogRecord(type = CRM_PRODUCT_CATEGORY_TYPE, subType = CRM_PRODUCT_CATEGORY_DELETE_SUB_TYPE, bizNo = "{{#id}}", + success = CRM_PRODUCT_CATEGORY_DELETE_SUCCESS) + public void deleteProductCategory(Long id) { + // 1.1 校验存在 + validateProductCategoryExists(id); + // 1.2 校验是å¦è¿˜æœ‰å­åˆ†ç±» + if (productCategoryMapper.selectCountByParentId(id) > 0) { + throw exception(product_CATEGORY_EXISTS_CHILDREN); + } + // 1.3 校验是å¦è¢«äº§å“使用 + if (crmProductService.getProductByCategoryId(id) !=null) { + throw exception(PRODUCT_CATEGORY_USED); + } + // 2. 删除 + productCategoryMapper.deleteById(id); + } + + @Override + public CrmProductCategoryDO getProductCategory(Long id) { + return productCategoryMapper.selectById(id); + } + + @Override + public List getProductCategoryList(CrmProductCategoryListReqVO listReqVO) { + return productCategoryMapper.selectList(listReqVO); + } + + @Override + public List getProductCategoryList(Collection ids) { + return productCategoryMapper.selectBatchIds(ids); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductService.java new file mode 100644 index 000000000..6d2dd4943 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductService.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.crm.service.product; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductSaveReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; + +/** + * CRM äº§å“ Service æŽ¥å£ + * + * @author ZanGe丶 + */ +public interface CrmProductService { + + /** + * åˆ›å»ºäº§å“ + * + * @param createReqVO åˆ›å»ºä¿¡æ¯ + * @return ç¼–å· + */ + Long createProduct(@Valid CrmProductSaveReqVO createReqVO); + + /** + * æ›´æ–°äº§å“ + * + * @param updateReqVO æ›´æ–°ä¿¡æ¯ + */ + void updateProduct(@Valid CrmProductSaveReqVO updateReqVO); + + /** + * åˆ é™¤äº§å“ + * + * @param id ç¼–å· + */ + void deleteProduct(Long id); + + /** + * èŽ·å¾—äº§å“ + * + * @param id ç¼–å· + * @return äº§å“ + */ + CrmProductDO getProduct(Long id); + + /** + * 获得产å“列表 + * + * @param ids ç¼–å· + * @return 产å“列表 + */ + List getProductList(Collection ids); + + /** + * 获得产å“分页 + * + * @param pageReqVO 分页查询 + * @return 产å“分页 + */ + PageResult getProductPage(CrmProductPageReqVO pageReqVO, Long userId); + + /** + * èŽ·å¾—äº§å“ + * + * @param categoryId åˆ†ç±»ç¼–å· + * @return äº§å“ + */ + CrmProductDO getProductByCategoryId(Long categoryId); + + /** + * 获得产å“列表 + * + * @param ids 产å“ç¼–å· + * @return 产å“列表 + */ + List getProductListByIds(Collection ids); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductServiceImpl.java new file mode 100644 index 000000000..95205524e --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductServiceImpl.java @@ -0,0 +1,166 @@ +package cn.iocoder.yudao.module.crm.service.product; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductSaveReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; +import cn.iocoder.yudao.module.crm.dal.mysql.product.CrmProductMapper; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; +import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; +import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.mzt.logapi.context.LogRecordContext; +import com.mzt.logapi.service.impl.DiffParseFunction; +import com.mzt.logapi.starter.annotation.LogRecord; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; + + +/** + * CRM äº§å“ Service 实现类 + * + * @author ZanGe丶 + */ +@Service +@Validated +public class CrmProductServiceImpl implements CrmProductService { + + @Resource(name = "crmProductMapper") + private CrmProductMapper productMapper; + + @Resource + private CrmProductCategoryService productCategoryService; + @Resource + private CrmPermissionService permissionService; + + @Resource + private AdminUserApi adminUserApi; + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_PRODUCT_TYPE, subType = CRM_PRODUCT_CREATE_SUB_TYPE, bizNo = "{{#productId}}", + success = CRM_PRODUCT_CREATE_SUCCESS) + public Long createProduct(CrmProductSaveReqVO createReqVO) { + // 1. æ ¡éªŒäº§å“ + adminUserApi.validateUserList(Collections.singleton(createReqVO.getOwnerUserId())); + validateProductNoDuplicate(null, createReqVO.getNo()); + validateProductCategoryExists(createReqVO.getCategoryId()); + + // 2. æ’å…¥äº§å“ + CrmProductDO product = BeanUtils.toBean(createReqVO, CrmProductDO.class); + productMapper.insert(product); + + // 3. æ’入数æ®æƒé™ + permissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(product.getOwnerUserId()) + .setBizType(CrmBizTypeEnum.CRM_PRODUCT.getType()).setBizId(product.getId()) + .setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); + + // 4. 记录æ“作日志上下文 + LogRecordContext.putVariable("productId", product.getId()); + return product.getId(); + } + + @Override + @LogRecord(type = CRM_PRODUCT_TYPE, subType = CRM_PRODUCT_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", + success = CRM_PRODUCT_UPDATE_SUCCESS) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_PRODUCT, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE) + public void updateProduct(CrmProductSaveReqVO updateReqVO) { + // 1. æ ¡éªŒäº§å“ + updateReqVO.setOwnerUserId(null); // ä¸ä¿®æ”¹è´Ÿè´£äºº + CrmProductDO crmProductDO = validateProductExists(updateReqVO.getId()); + validateProductNoDuplicate(updateReqVO.getId(), updateReqVO.getNo()); + validateProductCategoryExists(updateReqVO.getCategoryId()); + + // 2. æ›´æ–°äº§å“ + CrmProductDO updateObj = BeanUtils.toBean(updateReqVO, CrmProductDO.class); + productMapper.updateById(updateObj); + + // 3. 记录æ“作日志上下文 + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(crmProductDO, CrmProductSaveReqVO.class)); + } + + private CrmProductDO validateProductExists(Long id) { + CrmProductDO product = productMapper.selectById(id); + if (product == null) { + throw exception(PRODUCT_NOT_EXISTS); + } + return product; + } + + private void validateProductNoDuplicate(Long id, String no) { + CrmProductDO product = productMapper.selectByNo(no); + if (product == null + || product.getId().equals(id)) { + return; + } + throw exception(PRODUCT_NO_EXISTS); + } + + private void validateProductCategoryExists(Long categoryId) { + CrmProductCategoryDO category = productCategoryService.getProductCategory(categoryId); + if (category == null) { + throw exception(PRODUCT_CATEGORY_NOT_EXISTS); + } + } + + @Override + @LogRecord(type = CRM_PRODUCT_TYPE, subType = CRM_PRODUCT_DELETE_SUB_TYPE, bizNo = "{{#id}}", + success = CRM_PRODUCT_DELETE_SUCCESS) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_PRODUCT, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) + public void deleteProduct(Long id) { + // 校验存在 + validateProductExists(id); + // 删除 + productMapper.deleteById(id); + } + + @Override + @CrmPermission(bizType = CrmBizTypeEnum.CRM_PRODUCT, bizId = "#id", level = CrmPermissionLevelEnum.READ) + public CrmProductDO getProduct(Long id) { + return productMapper.selectById(id); + } + + @Override + public List getProductList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return ListUtil.empty(); + } + return productMapper.selectBatchIds(ids); + } + + @Override + public PageResult getProductPage(CrmProductPageReqVO pageReqVO, Long userId) { + return productMapper.selectPage(pageReqVO, userId); + } + + @Override + public CrmProductDO getProductByCategoryId(Long categoryId) { + return productMapper.selectOne(new LambdaQueryWrapper().eq(CrmProductDO::getCategoryId, categoryId)); + } + + @Override + public List getProductListByIds(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + return productMapper.selectBatchIds(ids); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanService.java new file mode 100644 index 000000000..ded059b28 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanService.java @@ -0,0 +1,80 @@ +package cn.iocoder.yudao.module.crm.service.receivable; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanCreateReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanUpdateReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; + +/** + * CRM 回款计划 Service æŽ¥å£ + * + * @author 芋é“æºç  + */ +public interface CrmReceivablePlanService { + + /** + * 创建回款计划 + * + * @param createReqVO åˆ›å»ºä¿¡æ¯ + * @return ç¼–å· + */ + Long createReceivablePlan(@Valid CrmReceivablePlanCreateReqVO createReqVO, Long userId); + + /** + * 更新回款计划 + * + * @param updateReqVO æ›´æ–°ä¿¡æ¯ + */ + void updateReceivablePlan(@Valid CrmReceivablePlanUpdateReqVO updateReqVO); + + /** + * 删除回款计划 + * + * @param id ç¼–å· + */ + void deleteReceivablePlan(Long id); + + /** + * 获得回款计划 + * + * @param id ç¼–å· + * @return 回款计划 + */ + CrmReceivablePlanDO getReceivablePlan(Long id); + + /** + * 获得回款计划列表 + * + * @param ids ç¼–å· + * @return 回款计划列表 + */ + List getReceivablePlanList(Collection ids); + + /** + * 获得回款计划分页 + * + * æ•°æ®æƒé™ï¼šåŸºäºŽ {@link CrmReceivablePlanDO} è¯»å– + * + * @param pageReqVO 分页查询 + * @param userId ç”¨æˆ·ç¼–å· + * @return 回款计划分页 + */ + PageResult getReceivablePlanPage(CrmReceivablePlanPageReqVO pageReqVO, Long userId); + + /** + * 获得回款计划分页,基于指定客户 + * + * æ•°æ®æƒé™ï¼šåŸºäºŽ {@link CrmCustomerDO} è¯»å– + * + * @param pageReqVO 分页查询 + * @return 回款计划分页 + */ + PageResult getReceivablePlanPageByCustomerId(CrmReceivablePlanPageReqVO pageReqVO); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java new file mode 100644 index 000000000..9ac2e7f43 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java @@ -0,0 +1,166 @@ +package cn.iocoder.yudao.module.crm.service.receivable; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanCreateReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanUpdateReqVO; +import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivablePlanConvert; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO; +import cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivablePlanMapper; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; +import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; +import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; +import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; +import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; +import com.mzt.logapi.context.LogRecordContext; +import com.mzt.logapi.service.impl.DiffParseFunction; +import com.mzt.logapi.starter.annotation.LogRecord; +import jakarta.annotation.Resource; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; + +// TODO @liuhongfeng:å‚考 CrmReceivableServiceImpl 写的 todo 哈; + +/** + * 回款计划 Service 实现类 + * + * @author 芋é“æºç  + */ +@Service +@Validated +public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService { + + @Resource + private CrmReceivablePlanMapper receivablePlanMapper; + + @Resource + private CrmContractService contractService; + @Resource + private CrmCustomerService customerService; + @Resource + private CrmPermissionService permissionService; + + @Override + @LogRecord(type = CRM_RECEIVABLE_PLAN_TYPE, subType = CRM_RECEIVABLE_PLAN_CREATE_SUB_TYPE, bizNo = "{{#receivablePlan.id}}", + success = CRM_RECEIVABLE_PLAN_CREATE_SUCCESS) + public Long createReceivablePlan(CrmReceivablePlanCreateReqVO createReqVO, Long userId) { + // TODO @liuhongfeng:第几期的计算;基于是 contractId + contractDO 的第几个还款 + // TODO @liuhongfeng contractId:校验åˆåŒæ˜¯å¦å­˜åœ¨ + // æ’å…¥ + CrmReceivablePlanDO receivablePlan = CrmReceivablePlanConvert.INSTANCE.convert(createReqVO); + receivablePlan.setFinishStatus(false); + + checkReceivablePlan(receivablePlan); + + receivablePlanMapper.insert(receivablePlan); + // 创建数æ®æƒé™ + permissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(userId) + .setBizType(CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType()).setBizId(receivablePlan.getId()) + .setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); + + // 4. 记录æ“作日志上下文 + LogRecordContext.putVariable("receivablePlan", receivablePlan); + return receivablePlan.getId(); + } + + private void checkReceivablePlan(CrmReceivablePlanDO receivablePlan) { + + if (ObjectUtil.isNull(receivablePlan.getContractId())) { + throw exception(CONTRACT_NOT_EXISTS); + } + + CrmContractDO contract = contractService.getContract(receivablePlan.getContractId()); + if (ObjectUtil.isNull(contract)) { + throw exception(CONTRACT_NOT_EXISTS); + } + + CrmCustomerDO customer = customerService.getCustomer(receivablePlan.getCustomerId()); + if (ObjectUtil.isNull(customer)) { + throw exception(CUSTOMER_NOT_EXISTS); + } + + } + + @Override + @LogRecord(type = CRM_RECEIVABLE_PLAN_TYPE, subType = CRM_RECEIVABLE_PLAN_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", + success = CRM_RECEIVABLE_PLAN_UPDATE_SUCCESS) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE) + public void updateReceivablePlan(CrmReceivablePlanUpdateReqVO updateReqVO) { + // TODO @liuhongfeng:如果已ç»æœ‰å¯¹åº”的还款,则ä¸å…许编辑; + // 校验存在 + CrmReceivablePlanDO oldReceivablePlan = validateReceivablePlanExists(updateReqVO.getId()); + + // æ›´æ–° + CrmReceivablePlanDO updateObj = CrmReceivablePlanConvert.INSTANCE.convert(updateReqVO); + receivablePlanMapper.updateById(updateObj); + + // 3. 记录æ“作日志上下文 + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldReceivablePlan, CrmReceivablePlanUpdateReqVO.class)); + LogRecordContext.putVariable("receivablePlan", oldReceivablePlan); + } + + @Override + @LogRecord(type = CRM_RECEIVABLE_PLAN_TYPE, subType = CRM_RECEIVABLE_PLAN_DELETE_SUB_TYPE, bizNo = "{{#id}}", + success = CRM_RECEIVABLE_PLAN_DELETE_SUCCESS) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) + public void deleteReceivablePlan(Long id) { + // 校验存在 + CrmReceivablePlanDO receivablePlan = validateReceivablePlanExists(id); + // 删除 + receivablePlanMapper.deleteById(id); + // 删除数æ®æƒé™ + permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), id); + // 记录æ“作日志上下文 + LogRecordContext.putVariable("receivablePlan", receivablePlan); + } + + private CrmReceivablePlanDO validateReceivablePlanExists(Long id) { + CrmReceivablePlanDO receivablePlan = receivablePlanMapper.selectById(id); + if (receivablePlan == null) { + throw exception(RECEIVABLE_PLAN_NOT_EXISTS); + } + return receivablePlan; + } + + @Override + @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#id", level = CrmPermissionLevelEnum.READ) + public CrmReceivablePlanDO getReceivablePlan(Long id) { + return receivablePlanMapper.selectById(id); + } + + @Override + public List getReceivablePlanList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return ListUtil.empty(); + } + return receivablePlanMapper.selectBatchIds(ids); + } + + @Override + public PageResult getReceivablePlanPage(CrmReceivablePlanPageReqVO pageReqVO, Long userId) { + return receivablePlanMapper.selectPage(pageReqVO, userId); + } + + @Override + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#pageReqVO.customerId", level = CrmPermissionLevelEnum.READ) + public PageResult getReceivablePlanPageByCustomerId(CrmReceivablePlanPageReqVO pageReqVO) { + return receivablePlanMapper.selectPageByCustomerId(pageReqVO); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java new file mode 100644 index 000000000..8e9cfa0ea --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.crm.service.receivable; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableCreateReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableUpdateReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; + +/** + * CRM 回款 Service æŽ¥å£ + * + * @author 赤焰 + */ +public interface CrmReceivableService { + + /** + * 创建回款 + * + * @param createReqVO åˆ›å»ºä¿¡æ¯ + * @param userId ç”¨æˆ·ç¼–å· + * @return ç¼–å· + */ + Long createReceivable(@Valid CrmReceivableCreateReqVO createReqVO, Long userId); + + /** + * 更新回款 + * + * @param updateReqVO æ›´æ–°ä¿¡æ¯ + */ + void updateReceivable(@Valid CrmReceivableUpdateReqVO updateReqVO); + + /** + * 删除回款 + * + * @param id ç¼–å· + */ + void deleteReceivable(Long id); + + /** + * 获得回款 + * + * @param id ç¼–å· + * @return 回款 + */ + CrmReceivableDO getReceivable(Long id); + + /** + * 获得回款列表 + * + * @param ids ç¼–å· + * @return 回款列表 + */ + List getReceivableList(Collection ids); + + /** + * 获得回款分页 + * + * æ•°æ®æƒé™ï¼šåŸºäºŽ {@link CrmReceivableDO} è¯»å– + * + * @param pageReqVO 分页查询 + * @param userId ç”¨æˆ·ç¼–å· + * @return 回款分页 + */ + PageResult getReceivablePage(CrmReceivablePageReqVO pageReqVO, Long userId); + + /** + * 获得回款分页,基于指定客户 + * + * æ•°æ®æƒé™ï¼šåŸºäºŽ {@link CrmCustomerDO} è¯»å– + * + * @param pageReqVO 分页查询 + * @return 回款分页 + */ + PageResult getReceivablePageByCustomerId(CrmReceivablePageReqVO pageReqVO); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java new file mode 100644 index 000000000..effe0d720 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java @@ -0,0 +1,186 @@ +package cn.iocoder.yudao.module.crm.service.receivable; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableCreateReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableUpdateReqVO; +import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivableConvert; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO; +import cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivableMapper; +import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; +import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; +import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; +import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; +import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; +import com.mzt.logapi.context.LogRecordContext; +import com.mzt.logapi.service.impl.DiffParseFunction; +import com.mzt.logapi.starter.annotation.LogRecord; +import jakarta.annotation.Resource; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; + +/** + * CRM 回款 Service 实现类 + * + * @author 赤焰 + */ +@Service +@Validated +public class CrmReceivableServiceImpl implements CrmReceivableService { + + @Resource + private CrmReceivableMapper receivableMapper; + + @Resource + private CrmContractService contractService; + @Resource + private CrmCustomerService customerService; + @Resource + private CrmReceivablePlanService receivablePlanService; + @Resource + private CrmPermissionService permissionService; + + @Override + @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_CREATE_SUB_TYPE, bizNo = "{{#receivable.id}}", + success = CRM_RECEIVABLE_CREATE_SUCCESS) + public Long createReceivable(CrmReceivableCreateReqVO createReqVO, Long userId) { + // æ’入还款 + CrmReceivableDO receivable = CrmReceivableConvert.INSTANCE.convert(createReqVO); + if (ObjectUtil.isNull(receivable.getAuditStatus())) { + receivable.setAuditStatus(CommonStatusEnum.ENABLE.getStatus()); + } + receivable.setAuditStatus(CrmAuditStatusEnum.DRAFT.getStatus()); + + // TODO @liuhongfeng:一般æ¥è¯´ï¼Œé€»è¾‘的写法,是è¦å…ˆæ£€æŸ¥ï¼ŒåŽæ“作 db;所以,你这个 check 应该放到 CrmReceivableDO receivable 之å‰ï¼› + checkReceivable(receivable); + + receivableMapper.insert(receivable); + // 3. 创建数æ®æƒé™ + permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_RECEIVABLE.getType()) + .setBizId(receivable.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当å‰æ“作的人为负责人 + // TODO @liuhongfeng:需è¦æ›´æ–°å…³è”çš„ plan + + // 4. 记录æ“作日志上下文 + LogRecordContext.putVariable("receivable", receivable); + return receivable.getId(); + } + + // TODO @liuhongfeng:这里的括å·è¦æ³¨æ„排版; + private void checkReceivable(CrmReceivableDO receivable) { + // TODO @liuhongfeng:校验 no 的唯一性 + // TODO @liuhongfeng:这个放在å‚数校验åˆé€‚ + if (ObjectUtil.isNull(receivable.getContractId())) { + throw exception(CONTRACT_NOT_EXISTS); + } + + CrmContractDO contract = contractService.getContract(receivable.getContractId()); + if (ObjectUtil.isNull(contract)) { + throw exception(CONTRACT_NOT_EXISTS); + } + + CrmCustomerDO customer = customerService.getCustomer(receivable.getCustomerId()); + if (ObjectUtil.isNull(customer)) { + throw exception(CUSTOMER_NOT_EXISTS); + } + + CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(receivable.getPlanId()); + if (ObjectUtil.isNull(receivablePlan)) { + throw exception(RECEIVABLE_PLAN_NOT_EXISTS); + } + + } + + @Override + @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", + success = CRM_RECEIVABLE_UPDATE_SUCCESS) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE) + public void updateReceivable(CrmReceivableUpdateReqVO updateReqVO) { + // 校验存在 + CrmReceivableDO oldReceivable = validateReceivableExists(updateReqVO.getId()); + // TODO @liuhongfeng:åªæœ‰åœ¨è‰ç¨¿ã€å®¡æ ¸ä¸­ï¼Œå¯ä»¥æ交修改 + + // 更新还款 + CrmReceivableDO updateObj = CrmReceivableConvert.INSTANCE.convert(updateReqVO); + receivableMapper.updateById(updateObj); + + // TODO @liuhongfeng:需è¦æ›´æ–°å…³è”çš„ plan + // 3. 记录æ“作日志上下文 + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldReceivable, CrmReceivableUpdateReqVO.class)); + LogRecordContext.putVariable("receivable", oldReceivable); + } + + // TODO @liuhongfeng:缺一个å–消åˆåŒçš„接å£ï¼›åªæœ‰è‰ç¨¿ã€å®¡æ‰¹ä¸­å¯ä»¥å–消;CrmAuditStatusEnum + + // TODO @liuhongfeng:缺一个å‘起审批的接å£ï¼›åªæœ‰è‰ç¨¿å¯ä»¥å‘起审批;CrmAuditStatusEnum + + @Override + @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_DELETE_SUB_TYPE, bizNo = "{{#id}}", + success = CRM_RECEIVABLE_DELETE_SUCCESS) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) + public void deleteReceivable(Long id) { + // TODO @liuhongfeng:如果被 CrmReceivablePlanDO 所使用,则ä¸å…许删除 + // 校验存在 + CrmReceivableDO receivable = validateReceivableExists(id); + // 删除 + receivableMapper.deleteById(id); + + // 删除数æ®æƒé™ + permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), id); + + // 记录æ“作日志上下文 + LogRecordContext.putVariable("receivable", receivable); + } + + private CrmReceivableDO validateReceivableExists(Long id) { + CrmReceivableDO receivable = receivableMapper.selectById(id); + if (receivable == null) { + throw exception(RECEIVABLE_NOT_EXISTS); + } + return receivable; + } + + @Override + @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = "#id", level = CrmPermissionLevelEnum.READ) + public CrmReceivableDO getReceivable(Long id) { + return receivableMapper.selectById(id); + } + + @Override + public List getReceivableList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return ListUtil.empty(); + } + return receivableMapper.selectBatchIds(ids); + } + + @Override + public PageResult getReceivablePage(CrmReceivablePageReqVO pageReqVO, Long userId) { + return receivableMapper.selectPage(pageReqVO, userId); + } + + @Override + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#pageReqVO.customerId", level = CrmPermissionLevelEnum.READ) + public PageResult getReceivablePageByCustomerId(CrmReceivablePageReqVO pageReqVO) { + return receivableMapper.selectPageByCustomerId(pageReqVO); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmQueryWrapperUtils.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmQueryWrapperUtils.java new file mode 100644 index 000000000..5e7700902 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmQueryWrapperUtils.java @@ -0,0 +1,113 @@ +package cn.iocoder.yudao.module.crm.util; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.extra.spring.SpringUtil; +import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum; +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; +import cn.iocoder.yudao.module.crm.framework.permission.core.util.CrmPermissionUtils; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import com.baomidou.mybatisplus.core.toolkit.support.SFunction; +import com.github.yulichang.autoconfigure.MybatisPlusJoinProperties; +import com.github.yulichang.wrapper.MPJLambdaWrapper; + +import java.util.Collection; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; + +/** + * CRM 查询工具类 + * + * @author HUIHUI + */ +public class CrmQueryWrapperUtils { + + /** + * 构造 CRM æ•°æ®ç±»åž‹æ•°æ®åˆ†é¡µæŸ¥è¯¢æ¡ä»¶ + * + * @param query 连表查询对象 + * @param bizType æ•°æ®ç±»åž‹ {@link CrmBizTypeEnum} + * @param bizId æ•°æ®ç¼–å· + * @param userId ç”¨æˆ·ç¼–å· + * @param sceneType 场景类型 + * @param pool 公海 + */ + public static , S> void appendPermissionCondition(T query, Integer bizType, SFunction bizId, + Long userId, Integer sceneType, Boolean pool) { + final String ownerUserIdField = SingletonManager.getMybatisPlusJoinProperties().getTableAlias() + ".owner_user_id"; + // 1. 构建数æ®æƒé™è¿žè¡¨æ¡ä»¶ + if (!CrmPermissionUtils.isCrmAdmin() && ObjUtil.notEqual(pool, Boolean.TRUE)) { // 管ç†å‘˜ï¼Œå…¬æµ·ä¸éœ€è¦æ•°æ®æƒé™ + query.innerJoin(CrmPermissionDO.class, on -> on.eq(CrmPermissionDO::getBizType, bizType) + .eq(CrmPermissionDO::getBizId, bizId) // åªèƒ½ä½¿ç”¨ SFunction 如果传 id 解æžå‡ºæ¥çš„ sql ä¸å¯¹ + .eq(CrmPermissionDO::getUserId, userId)); + } + // 2.1 åœºæ™¯ä¸€ï¼šæˆ‘è´Ÿè´£çš„æ•°æ® + if (CrmSceneTypeEnum.isOwner(sceneType)) { + query.eq(ownerUserIdField, userId); + } + // 2.2 场景二:我å‚ä¸Žçš„æ•°æ® + if (CrmSceneTypeEnum.isInvolved(sceneType)) { + query.ne(ownerUserIdField, userId) + .in(CrmPermissionDO::getLevel, CrmPermissionLevelEnum.READ.getLevel(), CrmPermissionLevelEnum.WRITE.getLevel()); + } + // 2.3 åœºæ™¯ä¸‰ï¼šä¸‹å±žè´Ÿè´£çš„æ•°æ® + if (CrmSceneTypeEnum.isSubordinate(sceneType)) { + List subordinateUsers = SingletonManager.getAdminUserApi().getUserListBySubordinate(userId).getCheckedData(); + if (CollUtil.isEmpty(subordinateUsers)) { + query.eq(ownerUserIdField, -1); // ä¸è¿”回任何结果 + } else { + query.in(ownerUserIdField, convertSet(subordinateUsers, AdminUserRespDTO::getId)); + } + } + + // 3. 拼接公海的查询æ¡ä»¶ + if (ObjUtil.equal(pool, Boolean.TRUE)) { // 情况一:公海 + query.isNull(ownerUserIdField); + } else { // 情况二:ä¸æ˜¯å…¬æµ· + query.isNotNull(ownerUserIdField); + } + } + + /** + * 构造 CRM æ•°æ®ç±»åž‹æ‰¹é‡æ•°æ®æŸ¥è¯¢æ¡ä»¶ + * + * @param query 连表查询对象 + * @param bizType æ•°æ®ç±»åž‹ {@link CrmBizTypeEnum} + * @param bizIds æ•°æ®ç¼–å· + * @param userId ç”¨æˆ·ç¼–å· + */ + public static > void appendPermissionCondition(T query, Integer bizType, Collection bizIds, Long userId) { + if (CrmPermissionUtils.isCrmAdmin()) {// 管ç†å‘˜ä¸éœ€è¦æ•°æ®æƒé™ + return; + } + query.innerJoin(CrmPermissionDO.class, on -> + on.eq(CrmPermissionDO::getBizType, bizType).in(CrmPermissionDO::getBizId, bizIds) + .eq(CollUtil.isNotEmpty(bizIds), CrmPermissionDO::getUserId, userId)); + } + + /** + * é™æ€å†…部类实现å•ä¾‹èŽ·å– + * + * @author HUIHUI + */ + private static class SingletonManager { + + private static final AdminUserApi ADMIN_USER_API = SpringUtil.getBean(AdminUserApi.class); + + private static final MybatisPlusJoinProperties MYBATIS_PLUS_JOIN_PROPERTIES = SpringUtil.getBean(MybatisPlusJoinProperties.class); + + public static AdminUserApi getAdminUserApi() { + return ADMIN_USER_API; + } + + public static MybatisPlusJoinProperties getMybatisPlusJoinProperties() { + return MYBATIS_PLUS_JOIN_PROPERTIES; + } + + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/application-dev.yaml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/application-dev.yaml new file mode 100644 index 000000000..affbaf3c2 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/application-dev.yaml @@ -0,0 +1,104 @@ +--- #################### æ•°æ®åº“相关é…ç½® #################### +spring: + # æ•°æ®æºé…置项 + autoconfigure: + exclude: + datasource: + druid: # Druid ã€ç›‘控】相关的全局é…ç½® + web-stat-filter: + enabled: true + stat-view-servlet: + enabled: true + allow: # 设置白åå•ï¼Œä¸å¡«åˆ™å…许所有访问 + url-pattern: /druid/* + login-username: # 控制å°ç®¡ç†ç”¨æˆ·åå’Œå¯†ç  + login-password: + filter: + stat: + enabled: true + log-slow-sql: true # æ…¢ SQL 记录 + slow-sql-millis: 100 + merge-sql: true + wall: + config: + multi-statement-allow: true + dynamic: # 多数æ®æºé…ç½® + druid: # Druid ã€è¿žæŽ¥æ± ã€‘相关的全局é…ç½® + initial-size: 5 # åˆå§‹è¿žæŽ¥æ•° + min-idle: 10 # 最å°è¿žæŽ¥æ± æ•°é‡ + max-active: 20 # æœ€å¤§è¿žæŽ¥æ± æ•°é‡ + max-wait: 600000 # é…置获å–连接等待超时的时间,å•ä½ï¼šæ¯«ç§’ + time-between-eviction-runs-millis: 60000 # é…置间隔多久æ‰è¿›è¡Œä¸€æ¬¡æ£€æµ‹ï¼Œæ£€æµ‹éœ€è¦å…³é—­çš„空闲连接,å•ä½ï¼šæ¯«ç§’ + min-evictable-idle-time-millis: 300000 # é…置一个连接在池中最å°ç”Ÿå­˜çš„时间,å•ä½ï¼šæ¯«ç§’ + max-evictable-idle-time-millis: 900000 # é…置一个连接在池中最大生存的时间,å•ä½ï¼šæ¯«ç§’ + validation-query: SELECT 1 FROM DUAL # é…置检测连接是å¦æœ‰æ•ˆ + test-while-idle: true + test-on-borrow: false + test-on-return: false + primary: master + datasource: + master: + name: ruoyi-vue-pro + url: jdbc:mysql://400-infra.server.iocoder.cn:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT&nullCatalogMeansCurrent=true + driver-class-name: com.mysql.jdbc.Driver + username: root + password: 3WLiVUBEwTbvAfsh + slave: # 模拟从库,å¯æ ¹æ®è‡ªå·±éœ€è¦ä¿®æ”¹ # 模拟从库,å¯æ ¹æ®è‡ªå·±éœ€è¦ä¿®æ”¹ + name: ruoyi-vue-pro + url: jdbc:mysql://400-infra.server.iocoder.cn:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT&nullCatalogMeansCurrent=true + driver-class-name: com.mysql.jdbc.Driver + username: root + password: 3WLiVUBEwTbvAfsh + + # Redis é…置。Redisson 默认的é…置足够使用,一般ä¸éœ€è¦è¿›è¡Œè°ƒä¼˜ + data: + redis: + host: 400-infra.server.iocoder.cn # åœ°å€ + port: 6379 # ç«¯å£ + database: 1 # æ•°æ®åº“索引 +# password: 123456 # 密ç ï¼Œå»ºè®®ç”Ÿäº§çŽ¯å¢ƒå¼€å¯ + +--- #################### MQ 消æ¯é˜Ÿåˆ—相关é…ç½® #################### + +--- #################### 定时任务相关é…ç½® #################### + +--- #################### æœåŠ¡ä¿éšœç›¸å…³é…ç½® #################### + +# Lock4j é…置项 +lock4j: + acquire-timeout: 3000 # 获å–分布å¼é”超时时间,默认为 3000 毫秒 + expire: 30000 # 分布å¼é”的超时时间,默认为 30 毫秒 + +--- #################### 监控相关é…ç½® #################### + +# Actuator 监控端点的é…置项 +management: + endpoints: + web: + base-path: /actuator # Actuator æ供的 API 接å£çš„根目录。默认为 /actuator + exposure: + include: '*' # 需è¦å¼€æ”¾çš„端点。默认值åªæ‰“å¼€ health å’Œ info 两个端点。通过设置 * ,å¯ä»¥å¼€æ”¾æ‰€æœ‰ç«¯ç‚¹ã€‚ + +# Spring Boot Admin é…置项 +spring: + boot: + admin: + # Spring Boot Admin Client 客户端的相关é…ç½® + client: + instance: + service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME] + +--- #################### 芋é“相关é…ç½® #################### + +# 芋é“é…置项,设置当å‰é¡¹ç›®æ‰€æœ‰è‡ªå®šä¹‰çš„é…ç½® +yudao: + xss: + enable: false + exclude-urls: # 如下两个 url,仅仅是为了演示,去掉é…置也没关系 + - ${spring.boot.admin.context-path}/** # ä¸å¤„ç† Spring Boot Admin 的请求 + - ${management.endpoints.web.base-path}/** # ä¸å¤„ç† Actuator 的请求 + access-log: # 访问日志的é…置项 + enable: false + error-code: # 错误ç ç›¸å…³é…置项 + enable: false + demo: false # å…³é—­æ¼”ç¤ºæ¨¡å¼ diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/application-local.yaml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/application-local.yaml new file mode 100644 index 000000000..7d5f030fb --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/application-local.yaml @@ -0,0 +1,130 @@ +--- #################### æ•°æ®åº“相关é…ç½® #################### +spring: + # æ•°æ®æºé…置项 + autoconfigure: + exclude: + - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # ç¦ç”¨ Spring Boot Admin çš„ Client 的自动é…ç½® + datasource: + druid: # Druid ã€ç›‘控】相关的全局é…ç½® + web-stat-filter: + enabled: true + stat-view-servlet: + enabled: true + allow: # 设置白åå•ï¼Œä¸å¡«åˆ™å…许所有访问 + url-pattern: /druid/* + login-username: # 控制å°ç®¡ç†ç”¨æˆ·åå’Œå¯†ç  + login-password: + filter: + stat: + enabled: true + log-slow-sql: true # æ…¢ SQL 记录 + slow-sql-millis: 100 + merge-sql: true + wall: + config: + multi-statement-allow: true + dynamic: # 多数æ®æºé…ç½® + druid: # Druid ã€è¿žæŽ¥æ± ã€‘相关的全局é…ç½® + initial-size: 1 # åˆå§‹è¿žæŽ¥æ•° + min-idle: 1 # 最å°è¿žæŽ¥æ± æ•°é‡ + max-active: 20 # æœ€å¤§è¿žæŽ¥æ± æ•°é‡ + max-wait: 600000 # é…置获å–连接等待超时的时间,å•ä½ï¼šæ¯«ç§’ + time-between-eviction-runs-millis: 60000 # é…置间隔多久æ‰è¿›è¡Œä¸€æ¬¡æ£€æµ‹ï¼Œæ£€æµ‹éœ€è¦å…³é—­çš„空闲连接,å•ä½ï¼šæ¯«ç§’ + min-evictable-idle-time-millis: 300000 # é…置一个连接在池中最å°ç”Ÿå­˜çš„时间,å•ä½ï¼šæ¯«ç§’ + max-evictable-idle-time-millis: 900000 # é…置一个连接在池中最大生存的时间,å•ä½ï¼šæ¯«ç§’ + validation-query: SELECT 1 FROM DUAL # é…置检测连接是å¦æœ‰æ•ˆ + test-while-idle: true + test-on-borrow: false + test-on-return: false + primary: master + datasource: + master: + name: ruoyi-vue-pro + url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?allowMultiQueries=true&useUnicode=true&useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 + # url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL Connector/J 5.X 连接的示例 + # url: jdbc:postgresql://127.0.0.1:5432/${spring.datasource.dynamic.datasource.master.name} # PostgreSQL 连接的示例 + # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 + # url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=${spring.datasource.dynamic.datasource.master.name} # SQLServer 连接的示例 + username: root + password: 123456 + # username: sa + # password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W + slave: # 模拟从库,å¯æ ¹æ®è‡ªå·±éœ€è¦ä¿®æ”¹ + name: ruoyi-vue-pro + url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.slave.name}?allowMultiQueries=true&useUnicode=true&useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 + # url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL Connector/J 5.X 连接的示例 + # url: jdbc:postgresql://127.0.0.1:5432/${spring.datasource.dynamic.datasource.slave.name} # PostgreSQL 连接的示例 + # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 + # url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=${spring.datasource.dynamic.datasource.slave.name} # SQLServer 连接的示例 + username: root + password: 123456 + # username: sa + # password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W + + # Redis é…置。Redisson 默认的é…置足够使用,一般ä¸éœ€è¦è¿›è¡Œè°ƒä¼˜ + data: + redis: + host: 127.0.0.1 # åœ°å€ + port: 6379 # ç«¯å£ + database: 0 # æ•°æ®åº“索引 +# password: 123456 # 密ç ï¼Œå»ºè®®ç”Ÿäº§çŽ¯å¢ƒå¼€å¯ + +--- #################### MQ 消æ¯é˜Ÿåˆ—相关é…ç½® #################### + +--- #################### 定时任务相关é…ç½® #################### +xxl: + job: + enabled: false # 是å¦å¼€å¯è°ƒåº¦ä¸­å¿ƒï¼Œé»˜è®¤ä¸º true å¼€å¯ + admin: + addresses: http://127.0.0.1:8000/xxl-job-admin # è°ƒåº¦ä¸­å¿ƒéƒ¨ç½²è·Ÿåœ°å€ + +--- #################### æœåŠ¡ä¿éšœç›¸å…³é…ç½® #################### + +# Lock4j é…置项 +lock4j: + acquire-timeout: 3000 # 获å–分布å¼é”超时时间,默认为 3000 毫秒 + expire: 30000 # 分布å¼é”的超时时间,默认为 30 毫秒 + +--- #################### 监控相关é…ç½® #################### + +# Actuator 监控端点的é…置项 +management: + endpoints: + web: + base-path: /actuator # Actuator æ供的 API 接å£çš„根目录。默认为 /actuator + exposure: + include: '*' # 需è¦å¼€æ”¾çš„端点。默认值åªæ‰“å¼€ health å’Œ info 两个端点。通过设置 * ,å¯ä»¥å¼€æ”¾æ‰€æœ‰ç«¯ç‚¹ã€‚ + +# Spring Boot Admin é…置项 +spring: + boot: + admin: + # Spring Boot Admin Client 客户端的相关é…ç½® + client: + instance: + service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME] + +# 日志文件é…ç½® +logging: + level: + # é…置自己写的 MyBatis Mapper 打å°æ—¥å¿— + cn.iocoder.yudao.module.crm.dal.mysql: debug + +--- #################### 芋é“相关é…ç½® #################### + +# 芋é“é…置项,设置当å‰é¡¹ç›®æ‰€æœ‰è‡ªå®šä¹‰çš„é…ç½® +yudao: + env: # 多环境的é…置项 + tag: ${HOSTNAME} + security: + mock-enable: true + xss: + enable: false + exclude-urls: # 如下两个 url,仅仅是为了演示,去掉é…置也没关系 + - ${spring.boot.admin.context-path}/** # ä¸å¤„ç† Spring Boot Admin 的请求 + - ${management.endpoints.web.base-path}/** # ä¸å¤„ç† Actuator 的请求 + access-log: # 访问日志的é…置项 + enable: false + error-code: # 错误ç ç›¸å…³é…置项 + enable: false + demo: false # å…³é—­æ¼”ç¤ºæ¨¡å¼ diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/application.yaml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/application.yaml new file mode 100644 index 000000000..7860f1328 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/application.yaml @@ -0,0 +1,105 @@ +spring: + main: + allow-circular-references: true # å…许循环ä¾èµ–,因为项目是三层架构,无法é¿å…这个情况。 + allow-bean-definition-overriding: true # å…许 Bean 覆盖,例如说 Feign 等会存在é‡å¤å®šä¹‰çš„æœåŠ¡ + + # Servlet é…ç½® + servlet: + # 文件上传相关é…置项 + multipart: + max-file-size: 16MB # å•ä¸ªæ–‡ä»¶å¤§å° + max-request-size: 32MB # è®¾ç½®æ€»ä¸Šä¼ çš„æ–‡ä»¶å¤§å° + mvc: + pathmatch: + matching-strategy: ANT_PATH_MATCHER # 解决 SpringFox 与 SpringBoot 2.6.x ä¸å…¼å®¹çš„问题,å‚è§ SpringFoxHandlerProviderBeanPostProcessor ç±» + + # Jackson é…置项 + jackson: + serialization: + write-dates-as-timestamps: true # 设置 LocalDateTime çš„æ ¼å¼ï¼Œä½¿ç”¨æ—¶é—´æˆ³ + write-date-timestamps-as-nanoseconds: false # 设置ä¸ä½¿ç”¨ nanoseconds çš„æ ¼å¼ã€‚例如说 1611460870.401,而是直接 1611460870401 + write-durations-as-timestamps: true # 设置 Duration çš„æ ¼å¼ï¼Œä½¿ç”¨æ—¶é—´æˆ³ + fail-on-empty-beans: false # å…许åºåˆ—化无属性的 Bean + + # Cache é…置项 + cache: + type: REDIS + redis: + time-to-live: 1h # 设置过期时间为 1 å°æ—¶ + +--- #################### 接å£æ–‡æ¡£é…ç½® #################### + +springdoc: + api-docs: + enabled: true # 1. 是å¦å¼€å¯ Swagger æŽ¥æ–‡æ¡£çš„å…ƒæ•°æ® + path: /v3/api-docs + swagger-ui: + enabled: true # 2.1 是å¦å¼€å¯ Swagger 文档的官方 UI ç•Œé¢ + path: /swagger-ui.html + default-flat-param-object: true # å‚è§ https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档 + +knife4j: + enable: true # 2.2 是å¦å¼€å¯ Swagger 文档的 Knife4j UI ç•Œé¢ + setting: + language: zh_cn + +# MyBatis Plus çš„é…置项 +mybatis-plus: + configuration: + map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。 + global-config: + db-config: + id-type: NONE # “智能â€æ¨¡å¼ï¼ŒåŸºäºŽ IdTypeEnvironmentPostProcessor + æ•°æ®æºçš„类型,自动适é…æˆ AUTOã€INPUT 模å¼ã€‚ + # id-type: AUTO # 自增 IDï¼Œé€‚åˆ MySQL 等直接自增的数æ®åº“ + # id-type: INPUT # 用户输入 IDï¼Œé€‚åˆ Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“ + # id-type: ASSIGN_ID # åˆ†é… ID,默认使用雪花算法。注æ„,Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“时,需è¦åŽ»é™¤å®žä½“类上的 @KeySequence 注解 + logic-delete-value: 1 # 逻辑已删除值(默认为 1) + logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) + banner: false # 关闭控制å°çš„ Banner æ‰“å° + type-aliases-package: ${yudao.info.base-package}.dal.dataobject + encryptor: + password: XDV71a+xqStEA3WH # 加解密的秘钥,å¯ä½¿ç”¨ https://www.imaegoo.com/2020/aes-key-generator/ ç½‘ç«™ç”Ÿæˆ + +mybatis-plus-join: + banner: false # 关闭控制å°çš„ Banner æ‰“å° + +# Spring Data Redis é…ç½® +spring: + data: + redis: + repositories: + enabled: false # 项目未使用到 Spring Data Redis çš„ Repository,所以直接ç¦ç”¨ï¼Œä¿è¯å¯åŠ¨é€Ÿåº¦ + +--- #################### MQ 消æ¯é˜Ÿåˆ—相关é…ç½® #################### + +--- #################### 定时任务相关é…ç½® #################### + +xxl: + job: + executor: + appname: ${spring.application.name} # 执行器 AppName + logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器è¿è¡Œæ—¥å¿—文件存储ç£ç›˜è·¯å¾„ + accessToken: default_token # 执行器通讯TOKEN + +--- #################### 芋é“相关é…ç½® #################### + +yudao: + info: + version: 1.0.0 + base-package: cn.iocoder.yudao.module.crm + web: + admin-ui: + url: http://dashboard.yudao.iocoder.cn # Admin 管ç†åŽå° UI çš„åœ°å€ + swagger: + title: 管ç†åŽå° + description: æ供管ç†å‘˜ç®¡ç†çš„所有功能 + version: ${yudao.info.version} + base-package: ${yudao.info.base-package} + error-code: # 错误ç ç›¸å…³é…置项 + constants-class-list: + - cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants + tenant: # 多租户相关é…置项 + enable: true + ignore-urls: + +debug: false diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/bootstrap-local.yaml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/bootstrap-local.yaml new file mode 100644 index 000000000..2de0efbf7 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/bootstrap-local.yaml @@ -0,0 +1,23 @@ +--- #################### 注册中心相关é…ç½® #################### + +spring: + cloud: + nacos: + server-addr: 127.0.0.1:8848 + discovery: + namespace: dev # 命å空间。这里使用 dev å¼€å‘环境 + metadata: + version: 1.0.0 # æœåŠ¡å®žä¾‹çš„版本å·ï¼Œå¯ç”¨äºŽç°åº¦å‘布 + +--- #################### é…置中心相关é…ç½® #################### + +spring: + cloud: + nacos: + # Nacos Config é…置项,对应 NacosConfigProperties é…置属性类 + config: + server-addr: 127.0.0.1:8848 # Nacos æœåŠ¡å™¨åœ°å€ + namespace: dev # 命å空间 dev çš„ID,ä¸èƒ½ç›´æŽ¥ä½¿ç”¨ dev å称。创建命å空间的时候需è¦æŒ‡å®šID为 dev,这里使用 dev å¼€å‘环境 + group: DEFAULT_GROUP # 使用的 Nacos é…置分组,默认为 DEFAULT_GROUP + name: ${spring.application.name} # 使用的 Nacos é…置集的 dataId,默认为 spring.application.name + file-extension: yaml # 使用的 Nacos é…置集的 dataId 的文件拓展å,åŒæ—¶ä¹Ÿæ˜¯ Nacos é…置集的é…置格å¼ï¼Œé»˜è®¤ä¸º properties diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/bootstrap.yaml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/bootstrap.yaml new file mode 100644 index 000000000..f87220ec2 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/bootstrap.yaml @@ -0,0 +1,14 @@ +spring: + application: + name: crm-server + + profiles: + active: local + +server: + port: 48089 + +# 日志文件é…置。注æ„,如果 logging.file.name ä¸æ”¾åœ¨ bootstrap.yaml é…置文件,而是放在 application.yaml 中,会导致出现 LOG_FILE_IS_UNDEFINED 文件 +logging: + file: + name: ${user.home}/logs/${spring.application.name}.log # 日志文件å,全路径 diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/logback-spring.xml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/logback-spring.xml new file mode 100644 index 000000000..b1b9f3faf --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/logback-spring.xml @@ -0,0 +1,76 @@ + + + + + + + + + +       + + + ${PATTERN_DEFAULT} + + + + + + + + + + ${PATTERN_DEFAULT} + + + + ${LOG_FILE} + + + ${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz} + + ${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false} + + ${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB} + + ${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0} + + ${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-30} + + + + + + 0 + + 256 + + + + + + + + ${PATTERN_DEFAULT} + + + + + + + + + + + + + + + + + + + + + + diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/bi/CrmBiRankingMapper.xml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/bi/CrmBiRankingMapper.xml new file mode 100644 index 000000000..10030e0a7 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/bi/CrmBiRankingMapper.xml @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/business/BusinessStatusTypeServiceImplTest.java b/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/business/BusinessStatusTypeServiceImplTest.java new file mode 100644 index 000000000..3c039f644 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/business/BusinessStatusTypeServiceImplTest.java @@ -0,0 +1,119 @@ +package cn.iocoder.yudao.module.crm.service.business; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypePageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeSaveReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO; +import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessStatusTypeMapper; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import jakarta.annotation.Resource; + +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_STATUS_TYPE_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.*; + +// TODO 芋艿:åŽç»­å† review +/** + * {@link CrmBusinessStatusTypeServiceImpl} çš„å•å…ƒæµ‹è¯•ç±» + * + * @author ljlleo + */ +@Disabled // TODO 芋艿:åŽç»­ fix 补充的å•æµ‹ +@Import(CrmBusinessStatusTypeServiceImpl.class) +public class BusinessStatusTypeServiceImplTest extends BaseDbUnitTest { + + @Resource + private CrmBusinessStatusTypeServiceImpl businessStatusTypeService; + + @Resource + private CrmBusinessStatusTypeMapper businessStatusTypeMapper; + + @Test + public void testCreateBusinessStatusType_success() { + // 准备å‚æ•° + CrmBusinessStatusTypeSaveReqVO createReqVO = randomPojo(CrmBusinessStatusTypeSaveReqVO.class).setId(null); + + // 调用 + Long businessStatusTypeId = businessStatusTypeService.createBusinessStatusType(createReqVO); + // 断言 + assertNotNull(businessStatusTypeId); + // 校验记录的属性是å¦æ­£ç¡® + CrmBusinessStatusTypeDO businessStatusType = businessStatusTypeMapper.selectById(businessStatusTypeId); + assertPojoEquals(createReqVO, businessStatusType, "id"); + } + + @Test + public void testUpdateBusinessStatusType_success() { + // mock æ•°æ® + CrmBusinessStatusTypeDO dbBusinessStatusType = randomPojo(CrmBusinessStatusTypeDO.class); + businessStatusTypeMapper.insert(dbBusinessStatusType);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备å‚æ•° + CrmBusinessStatusTypeSaveReqVO updateReqVO = randomPojo(CrmBusinessStatusTypeSaveReqVO.class, o -> { + o.setId(dbBusinessStatusType.getId()); // 设置更新的 ID + }); + + // 调用 + businessStatusTypeService.updateBusinessStatusType(updateReqVO); + // 校验是å¦æ›´æ–°æ­£ç¡® + CrmBusinessStatusTypeDO businessStatusType = businessStatusTypeMapper.selectById(updateReqVO.getId()); // 获å–最新的 + assertPojoEquals(updateReqVO, businessStatusType); + } + + @Test + public void testUpdateBusinessStatusType_notExists() { + // 准备å‚æ•° + CrmBusinessStatusTypeSaveReqVO updateReqVO = randomPojo(CrmBusinessStatusTypeSaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> businessStatusTypeService.updateBusinessStatusType(updateReqVO), BUSINESS_STATUS_TYPE_NOT_EXISTS); + } + + @Test + public void testDeleteBusinessStatusType_success() { + // mock æ•°æ® + CrmBusinessStatusTypeDO dbBusinessStatusType = randomPojo(CrmBusinessStatusTypeDO.class); + businessStatusTypeMapper.insert(dbBusinessStatusType);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备å‚æ•° + Long id = dbBusinessStatusType.getId(); + + // 调用 + businessStatusTypeService.deleteBusinessStatusType(id); + // 校验数æ®ä¸å­˜åœ¨äº† + assertNull(businessStatusTypeMapper.selectById(id)); + } + + @Test + public void testDeleteBusinessStatusType_notExists() { + // 准备å‚æ•° + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> businessStatusTypeService.deleteBusinessStatusType(id), BUSINESS_STATUS_TYPE_NOT_EXISTS); + } + + @Test + @Disabled // TODO 请修改 null 为需è¦çš„值,然åŽåˆ é™¤ @Disabled 注解 + public void testGetBusinessStatusTypePage() { + // mock æ•°æ® + CrmBusinessStatusTypeDO dbBusinessStatusType = randomPojo(CrmBusinessStatusTypeDO.class, o -> { // 等会查询到 + }); + businessStatusTypeMapper.insert(dbBusinessStatusType); + // 准备å‚æ•° + CrmBusinessStatusTypePageReqVO reqVO = new CrmBusinessStatusTypePageReqVO(); + + // 调用 + PageResult pageResult = businessStatusTypeService.getBusinessStatusTypePage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbBusinessStatusType, pageResult.getList().get(0)); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImplTest.java b/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImplTest.java new file mode 100644 index 000000000..0fd3c447f --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImplTest.java @@ -0,0 +1,181 @@ +package cn.iocoder.yudao.module.crm.service.business; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessSaveReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; +import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessMapper; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +/** + * {@link CrmBusinessServiceImpl} çš„å•å…ƒæµ‹è¯•ç±» + * + * @author ljlleo + */ +@Disabled // TODO 芋艿:åŽç»­ fix 补充的å•æµ‹ +@Import(CrmBusinessServiceImpl.class) +public class CrmBusinessServiceImplTest extends BaseDbUnitTest { + + @Resource + private CrmBusinessServiceImpl businessService; + + @Resource + private CrmBusinessMapper businessMapper; + + @Test + public void testCreateBusiness_success() { + // 准备å‚æ•° + CrmBusinessSaveReqVO reqVO = randomPojo(CrmBusinessSaveReqVO.class); + + // 调用 + Long businessId = businessService.createBusiness(reqVO, getLoginUserId()); + // 断言 + assertNotNull(businessId); + // 校验记录的属性是å¦æ­£ç¡® + CrmBusinessDO business = businessMapper.selectById(businessId); + assertPojoEquals(reqVO, business); + } + + @Test + public void testUpdateBusiness_success() { + // mock æ•°æ® + CrmBusinessDO dbBusiness = randomPojo(CrmBusinessDO.class); + businessMapper.insert(dbBusiness);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备å‚æ•° + CrmBusinessSaveReqVO reqVO = randomPojo(CrmBusinessSaveReqVO.class, o -> { + o.setId(dbBusiness.getId()); // 设置更新的 ID + }); + + // 调用 + businessService.updateBusiness(reqVO); + // 校验是å¦æ›´æ–°æ­£ç¡® + CrmBusinessDO business = businessMapper.selectById(reqVO.getId()); // 获å–最新的 + assertPojoEquals(reqVO, business); + } + + @Test + public void testUpdateBusiness_notExists() { + // 准备å‚æ•° + CrmBusinessSaveReqVO reqVO = randomPojo(CrmBusinessSaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> businessService.updateBusiness(reqVO), BUSINESS_NOT_EXISTS); + } + + @Test + public void testDeleteBusiness_success() { + // mock æ•°æ® + CrmBusinessDO dbBusiness = randomPojo(CrmBusinessDO.class); + businessMapper.insert(dbBusiness);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备å‚æ•° + Long id = dbBusiness.getId(); + + // 调用 + businessService.deleteBusiness(id); + // 校验数æ®ä¸å­˜åœ¨äº† + assertNull(businessMapper.selectById(id)); + } + + @Test + public void testDeleteBusiness_notExists() { + // 准备å‚æ•° + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> businessService.deleteBusiness(id), BUSINESS_NOT_EXISTS); + } + + @Test + @Disabled // TODO 请修改 null 为需è¦çš„值,然åŽåˆ é™¤ @Disabled 注解 + public void testGetBusinessPage() { + // mock æ•°æ® + CrmBusinessDO dbBusiness = randomPojo(CrmBusinessDO.class, o -> { // 等会查询到 + o.setName(null); + o.setStatusTypeId(null); + o.setStatusId(null); + o.setContactNextTime(null); + o.setCustomerId(null); + o.setDealTime(null); + o.setPrice(null); + o.setDiscountPercent(null); + o.setProductPrice(null); + o.setRemark(null); + o.setCreateTime(null); + o.setEndStatus(null); + o.setEndRemark(null); + o.setContactLastTime(null); + o.setFollowUpStatus(null); + }); + businessMapper.insert(dbBusiness); + // 测试 name ä¸åŒ¹é… + businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setName(null))); + // 测试 statusTypeId ä¸åŒ¹é… + businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setStatusTypeId(null))); + // 测试 statusId ä¸åŒ¹é… + businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setStatusId(null))); + // 测试 contactNextTime ä¸åŒ¹é… + businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setContactNextTime(null))); + // 测试 customerId ä¸åŒ¹é… + businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setCustomerId(null))); + // 测试 dealTime ä¸åŒ¹é… + businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setDealTime(null))); + // 测试 price ä¸åŒ¹é… + businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setPrice(null))); + // 测试 discountPercent ä¸åŒ¹é… + businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setDiscountPercent(null))); + // 测试 productPrice ä¸åŒ¹é… + businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setProductPrice(null))); + // 测试 remark ä¸åŒ¹é… + businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setRemark(null))); + // 测试 createTime ä¸åŒ¹é… + businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setCreateTime(null))); + // 测试 endStatus ä¸åŒ¹é… + businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setEndStatus(null))); + // 测试 endRemark ä¸åŒ¹é… + businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setEndRemark(null))); + // 测试 contactLastTime ä¸åŒ¹é… + businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setContactLastTime(null))); + // 测试 followUpStatus ä¸åŒ¹é… + businessMapper.insert(cloneIgnoreId(dbBusiness, o -> o.setFollowUpStatus(null))); + //// 准备å‚æ•° + //CrmBusinessPageReqVO reqVO = new CrmBusinessPageReqVO(); + //reqVO.setName(null); + //reqVO.setStatusTypeId(null); + //reqVO.setStatusId(null); + //reqVO.setContactNextTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); + //reqVO.setCustomerId(null); + //reqVO.setDealTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); + //reqVO.setPrice(null); + //reqVO.setDiscountPercent(null); + //reqVO.setProductPrice(null); + //reqVO.setRemark(null); + //reqVO.setOwnerUserId(null); + //reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); + //reqVO.setRoUserIds(null); + //reqVO.setRwUserIds(null); + //reqVO.setEndStatus(null); + //reqVO.setEndRemark(null); + //reqVO.setContactLastTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); + //reqVO.setFollowUpStatus(null); + // + //// 调用 + //PageResult pageResult = businessService.getBusinessPage(reqVO); + //// 断言 + //assertEquals(1, pageResult.getTotal()); + //assertEquals(1, pageResult.getList().size()); + //assertPojoEquals(dbBusiness, pageResult.getList().get(0)); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImplTest.java b/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImplTest.java new file mode 100644 index 000000000..d73921f3d --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImplTest.java @@ -0,0 +1,207 @@ +package cn.iocoder.yudao.module.crm.service.clue; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueSaveReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO; +import cn.iocoder.yudao.module.crm.dal.mysql.clue.CrmClueMapper; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CLUE_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.*; + +// TODO 芋艿:å•æµ‹åŽç»­è¡¥ï¼› + +/** + * {@link CrmClueServiceImpl} çš„å•å…ƒæµ‹è¯•ç±» + * + * @author Wanwan + */ +@Disabled // TODO 芋艿:åŽç»­ fix 补充的å•æµ‹ +@Import(CrmClueServiceImpl.class) +public class CrmClueServiceImplTest extends BaseDbUnitTest { + + @Resource + private CrmClueServiceImpl clueService; + + @Resource + private CrmClueMapper clueMapper; + + @Test + public void testCreateClue_success() { + // 准备å‚æ•° + CrmClueSaveReqVO reqVO = randomPojo(CrmClueSaveReqVO.class); + + // 调用 + Long clueId = clueService.createClue(reqVO, getLoginUserId()); + // 断言 + assertNotNull(clueId); + // 校验记录的属性是å¦æ­£ç¡® + CrmClueDO clue = clueMapper.selectById(clueId); + assertPojoEquals(reqVO, clue); + } + + @Test + public void testUpdateClue_success() { + // mock æ•°æ® + CrmClueDO dbClue = randomPojo(CrmClueDO.class); + clueMapper.insert(dbClue);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备å‚æ•° + CrmClueSaveReqVO reqVO = randomPojo(CrmClueSaveReqVO.class, o -> { + o.setId(dbClue.getId()); // 设置更新的 ID + }); + + // 调用 + clueService.updateClue(reqVO); + // 校验是å¦æ›´æ–°æ­£ç¡® + CrmClueDO clue = clueMapper.selectById(reqVO.getId()); // 获å–最新的 + assertPojoEquals(reqVO, clue); + } + + @Test + public void testUpdateClue_notExists() { + // 准备å‚æ•° + CrmClueSaveReqVO reqVO = randomPojo(CrmClueSaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> clueService.updateClue(reqVO), CLUE_NOT_EXISTS); + } + + @Test + public void testDeleteClue_success() { + // mock æ•°æ® + CrmClueDO dbClue = randomPojo(CrmClueDO.class); + clueMapper.insert(dbClue);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备å‚æ•° + Long id = dbClue.getId(); + + // 调用 + clueService.deleteClue(id); + // 校验数æ®ä¸å­˜åœ¨äº† + assertNull(clueMapper.selectById(id)); + } + + @Test + public void testDeleteClue_notExists() { + // 准备å‚æ•° + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> clueService.deleteClue(id), CLUE_NOT_EXISTS); + } + + @Test + @Disabled // TODO 请修改 null 为需è¦çš„值,然åŽåˆ é™¤ @Disabled 注解 + public void testGetCluePage() { + // mock æ•°æ® + CrmClueDO dbClue = randomPojo(CrmClueDO.class, o -> { // 等会查询到 + o.setTransformStatus(null); + o.setFollowUpStatus(null); + o.setName(null); + o.setCustomerId(null); + o.setContactNextTime(null); + o.setTelephone(null); + o.setMobile(null); + o.setAddress(null); + o.setContactLastTime(null); + o.setCreateTime(null); + }); + clueMapper.insert(dbClue); + // 测试 transformStatus ä¸åŒ¹é… + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setTransformStatus(null))); + // 测试 followUpStatus ä¸åŒ¹é… + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setFollowUpStatus(null))); + // 测试 name ä¸åŒ¹é… + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setName(null))); + // 测试 customerId ä¸åŒ¹é… + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setCustomerId(null))); + // 测试 contactNextTime ä¸åŒ¹é… + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setContactNextTime(null))); + // 测试 telephone ä¸åŒ¹é… + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setTelephone(null))); + // 测试 mobile ä¸åŒ¹é… + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setMobile(null))); + // 测试 address ä¸åŒ¹é… + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setAddress(null))); + // 测试 contactLastTime ä¸åŒ¹é… + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setContactLastTime(null))); + // 测试 createTime ä¸åŒ¹é… + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setCreateTime(null))); + // 准备å‚æ•° + CrmCluePageReqVO reqVO = new CrmCluePageReqVO(); + reqVO.setName(null); + reqVO.setTelephone(null); + reqVO.setMobile(null); + + // 调用 + PageResult pageResult = clueService.getCluePage(reqVO, 1L); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbClue, pageResult.getList().get(0)); + } + + @Test + @Disabled // TODO 请修改 null 为需è¦çš„值,然åŽåˆ é™¤ @Disabled 注解 + public void testGetClueList() { + // mock æ•°æ® + CrmClueDO dbClue = randomPojo(CrmClueDO.class, o -> { // 等会查询到 + o.setTransformStatus(null); + o.setFollowUpStatus(null); + o.setName(null); + o.setCustomerId(null); + o.setContactNextTime(null); + o.setTelephone(null); + o.setMobile(null); + o.setAddress(null); + o.setContactLastTime(null); + o.setCreateTime(null); + }); + clueMapper.insert(dbClue); + // 测试 transformStatus ä¸åŒ¹é… + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setTransformStatus(null))); + // 测试 followUpStatus ä¸åŒ¹é… + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setFollowUpStatus(null))); + // 测试 name ä¸åŒ¹é… + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setName(null))); + // 测试 customerId ä¸åŒ¹é… + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setCustomerId(null))); + // 测试 contactNextTime ä¸åŒ¹é… + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setContactNextTime(null))); + // 测试 telephone ä¸åŒ¹é… + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setTelephone(null))); + // 测试 mobile ä¸åŒ¹é… + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setMobile(null))); + // 测试 address ä¸åŒ¹é… + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setAddress(null))); + // 测试 contactLastTime ä¸åŒ¹é… + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setContactLastTime(null))); + // 测试 createTime ä¸åŒ¹é… + clueMapper.insert(cloneIgnoreId(dbClue, o -> o.setCreateTime(null))); + // 准备å‚æ•° + CrmCluePageReqVO reqVO = new CrmCluePageReqVO(); + reqVO.setName(null); + reqVO.setTelephone(null); + reqVO.setMobile(null); + reqVO.setPageSize(PAGE_SIZE_NONE); + // 调用 + List list = clueService.getCluePage(reqVO, 1L).getList(); + // 断言 + assertEquals(1, list.size()); + assertPojoEquals(dbClue, list.get(0)); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/contract/ContractServiceImplTest.java b/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/contract/ContractServiceImplTest.java new file mode 100644 index 000000000..a511e7c1b --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/contract/ContractServiceImplTest.java @@ -0,0 +1,135 @@ +package cn.iocoder.yudao.module.crm.service.contract; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; +import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractMapper; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CONTRACT_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link CrmContractServiceImpl} çš„å•å…ƒæµ‹è¯•ç±» + * + * @author dhb52 + */ +@Import(CrmContractServiceImpl.class) +public class ContractServiceImplTest extends BaseDbUnitTest { + + @Resource + private CrmContractServiceImpl contractService; + + @Resource + private CrmContractMapper contractMapper; + + @Test + public void testCreateContract_success() { + // 准备å‚æ•° + CrmContractSaveReqVO reqVO = randomPojo(CrmContractSaveReqVO.class); + + // 调用 + Long contractId = contractService.createContract(reqVO, getLoginUserId()); + // 断言 + assertNotNull(contractId); + // 校验记录的属性是å¦æ­£ç¡® + CrmContractDO contract = contractMapper.selectById(contractId); + assertPojoEquals(reqVO, contract); + } + + @Test + public void testUpdateContract_success() { + // mock æ•°æ® + CrmContractDO dbContract = randomPojo(CrmContractDO.class); + contractMapper.insert(dbContract);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备å‚æ•° + CrmContractSaveReqVO reqVO = randomPojo(CrmContractSaveReqVO.class, o -> { + o.setId(dbContract.getId()); // 设置更新的 ID + }); + + // 调用 + contractService.updateContract(reqVO); + // 校验是å¦æ›´æ–°æ­£ç¡® + CrmContractDO contract = contractMapper.selectById(reqVO.getId()); // 获å–最新的 + assertPojoEquals(reqVO, contract); + } + + @Test + public void testUpdateContract_notExists() { + // 准备å‚æ•° + CrmContractSaveReqVO reqVO = randomPojo(CrmContractSaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> contractService.updateContract(reqVO), CONTRACT_NOT_EXISTS); + } + + @Test + public void testDeleteContract_success() { + // mock æ•°æ® + CrmContractDO dbContract = randomPojo(CrmContractDO.class); + contractMapper.insert(dbContract);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备å‚æ•° + Long id = dbContract.getId(); + + // 调用 + contractService.deleteContract(id); + // 校验数æ®ä¸å­˜åœ¨äº† + assertNull(contractMapper.selectById(id)); + } + + @Test + public void testDeleteContract_notExists() { + // 准备å‚æ•° + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> contractService.deleteContract(id), CONTRACT_NOT_EXISTS); + } + + @Test + @Disabled // TODO 请修改 null 为需è¦çš„值,然åŽåˆ é™¤ @Disabled 注解 + public void testGetContractPage() { + // mock æ•°æ® + CrmContractDO dbContract = randomPojo(CrmContractDO.class, o -> { // 等会查询到 + o.setName(null); + o.setCustomerId(null); + o.setBusinessId(null); + o.setOrderDate(null); + o.setNo(null); + o.setDiscountPercent(null); + o.setProductPrice(null); + }); + contractMapper.insert(dbContract); + // 测试 name ä¸åŒ¹é… + contractMapper.insert(cloneIgnoreId(dbContract, o -> o.setName(null))); + // 测试 customerId ä¸åŒ¹é… + contractMapper.insert(cloneIgnoreId(dbContract, o -> o.setCustomerId(null))); + // 测试 no ä¸åŒ¹é… + contractMapper.insert(cloneIgnoreId(dbContract, o -> o.setNo(null))); + // 准备å‚æ•° + CrmContractPageReqVO reqVO = new CrmContractPageReqVO(); + reqVO.setName(null); + reqVO.setCustomerId(null); + reqVO.setBusinessId(null); + reqVO.setNo(null); + + // 调用 + PageResult pageResult = contractService.getContractPage(reqVO, getLoginUserId()); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbContract, pageResult.getList().get(0)); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImplTest.java b/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImplTest.java new file mode 100644 index 000000000..8f701d987 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImplTest.java @@ -0,0 +1,141 @@ +package cn.iocoder.yudao.module.crm.service.customer; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerSaveReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper; +import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CUSTOMER_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.*; + +// TODO 芋艿:å•æµ‹åŽç»­è¡¥ + +/** + * {@link CrmCustomerServiceImpl} çš„å•å…ƒæµ‹è¯•ç±» + * + * @author Wanwan + */ +@Disabled // TODO 芋艿:åŽç»­ fix 补充的å•æµ‹ +@Import(CrmCustomerServiceImpl.class) +public class CrmCustomerServiceImplTest extends BaseDbUnitTest { + + @Resource + private CrmCustomerServiceImpl customerService; + + @Resource + private CrmCustomerMapper customerMapper; + @MockBean + private CrmPermissionService permissionService; + + @Test + public void testCreateCustomer_success() { + // 准备å‚æ•° + CrmCustomerSaveReqVO reqVO = randomPojo(CrmCustomerSaveReqVO.class); + + // 调用 + Long customerId = customerService.createCustomer(reqVO, getLoginUserId()); + // 断言 + assertNotNull(customerId); + // 校验记录的属性是å¦æ­£ç¡® + CrmCustomerDO customer = customerMapper.selectById(customerId); + assertPojoEquals(reqVO, customer); + } + + @Test + public void testUpdateCustomer_success() { + // mock æ•°æ® + CrmCustomerDO dbCustomer = randomPojo(CrmCustomerDO.class); + customerMapper.insert(dbCustomer);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备å‚æ•° + CrmCustomerSaveReqVO reqVO = randomPojo(CrmCustomerSaveReqVO.class, o -> { + o.setId(dbCustomer.getId()); // 设置更新的 ID + }); + + // 调用 + customerService.updateCustomer(reqVO); + // 校验是å¦æ›´æ–°æ­£ç¡® + CrmCustomerDO customer = customerMapper.selectById(reqVO.getId()); // 获å–最新的 + assertPojoEquals(reqVO, customer); + } + + @Test + public void testUpdateCustomer_notExists() { + // 准备å‚æ•° + CrmCustomerSaveReqVO reqVO = randomPojo(CrmCustomerSaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> customerService.updateCustomer(reqVO), CUSTOMER_NOT_EXISTS); + } + + @Test + public void testDeleteCustomer_success() { + // mock æ•°æ® + CrmCustomerDO dbCustomer = randomPojo(CrmCustomerDO.class); + customerMapper.insert(dbCustomer);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备å‚æ•° + Long id = dbCustomer.getId(); + + // 调用 + customerService.deleteCustomer(id); + // 校验数æ®ä¸å­˜åœ¨äº† + assertNull(customerMapper.selectById(id)); + } + + @Test + public void testDeleteCustomer_notExists() { + // 准备å‚æ•° + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> customerService.deleteCustomer(id), CUSTOMER_NOT_EXISTS); + } + + @Test + public void testGetCustomerPage() { + // mock æ•°æ® + CrmCustomerDO dbCustomer = randomPojo(CrmCustomerDO.class, o -> { // 等会查询到 + o.setName("张三"); + o.setMobile("13888888888"); + o.setTelephone("13888888888"); + o.setWebsite("https://yudao.com"); + }); + //customerMapper.insert(dbCustomer); + // 测试 name ä¸åŒ¹é… + //customerMapper.insert(cloneIgnoreId(dbCustomer, o -> o.setName(""))); + // 测试 mobile ä¸åŒ¹é… + //customerMapper.insert(cloneIgnoreId(dbCustomer, o -> o.setMobile(null))); + // 测试 telephone ä¸åŒ¹é… + //customerMapper.insert(cloneIgnoreId(dbCustomer, o -> o.setTelephone(null))); + // 测试 website ä¸åŒ¹é… + //customerMapper.insert(cloneIgnoreId(dbCustomer, o -> o.setWebsite(null))); + // 准备å‚æ•° + CrmCustomerPageReqVO reqVO = new CrmCustomerPageReqVO(); + reqVO.setName("张三"); + reqVO.setMobile("13888888888"); + //reqVO.setTelephone(null); + //reqVO.setWebsite(null); + + // 调用 + PageResult pageResult = customerService.getCustomerPage(reqVO, 1L); + // 断言 + //assertEquals(1, pageResult.getTotal()); + //assertEquals(1, pageResult.getList().size()); + //assertPojoEquals(dbCustomer, pageResult.getList().get(0)); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/customerlimitconfig/CrmCustomerLimitConfigServiceImplTest.java b/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/customerlimitconfig/CrmCustomerLimitConfigServiceImplTest.java new file mode 100644 index 000000000..ecda14822 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/customerlimitconfig/CrmCustomerLimitConfigServiceImplTest.java @@ -0,0 +1,119 @@ +package cn.iocoder.yudao.module.crm.service.customerlimitconfig; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigSaveReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO; +import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerLimitConfigMapper; +import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerLimitConfigServiceImpl; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CUSTOMER_LIMIT_CONFIG_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.*; + +// TODO 芋艿:å•æµ‹åŽé¢æž +/** + * {@link CrmCustomerLimitConfigServiceImpl} çš„å•å…ƒæµ‹è¯•ç±» + * + * @author Wanwan + */ +@Disabled // TODO 芋艿:åŽç»­ fix 补充的å•æµ‹ +@Import(CrmCustomerLimitConfigServiceImpl.class) +public class CrmCustomerLimitConfigServiceImplTest extends BaseDbUnitTest { + + @Resource + private CrmCustomerLimitConfigServiceImpl customerLimitConfigService; + + @Resource + private CrmCustomerLimitConfigMapper customerLimitConfigMapper; + + @Test + public void testCreateCustomerLimitConfig_success() { + // 准备å‚æ•° + CrmCustomerLimitConfigSaveReqVO reqVO = randomPojo(CrmCustomerLimitConfigSaveReqVO.class); + + // 调用 + Long customerLimitConfigId = customerLimitConfigService.createCustomerLimitConfig(reqVO); + // 断言 + assertNotNull(customerLimitConfigId); + // 校验记录的属性是å¦æ­£ç¡® + CrmCustomerLimitConfigDO customerLimitConfig = customerLimitConfigMapper.selectById(customerLimitConfigId); + assertPojoEquals(reqVO, customerLimitConfig); + } + + @Test + public void testUpdateCustomerLimitConfig_success() { + // mock æ•°æ® + CrmCustomerLimitConfigDO dbCustomerLimitConfig = randomPojo(CrmCustomerLimitConfigDO.class); + customerLimitConfigMapper.insert(dbCustomerLimitConfig);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备å‚æ•° + CrmCustomerLimitConfigSaveReqVO reqVO = randomPojo(CrmCustomerLimitConfigSaveReqVO.class, o -> { + o.setId(dbCustomerLimitConfig.getId()); // 设置更新的 ID + }); + + // 调用 + customerLimitConfigService.updateCustomerLimitConfig(reqVO); + // 校验是å¦æ›´æ–°æ­£ç¡® + CrmCustomerLimitConfigDO customerLimitConfig = customerLimitConfigMapper.selectById(reqVO.getId()); // 获å–最新的 + assertPojoEquals(reqVO, customerLimitConfig); + } + + @Test + public void testUpdateCustomerLimitConfig_notExists() { + // 准备å‚æ•° + CrmCustomerLimitConfigSaveReqVO reqVO = randomPojo(CrmCustomerLimitConfigSaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> customerLimitConfigService.updateCustomerLimitConfig(reqVO), CUSTOMER_LIMIT_CONFIG_NOT_EXISTS); + } + + @Test + public void testDeleteCustomerLimitConfig_success() { + // mock æ•°æ® + CrmCustomerLimitConfigDO dbCustomerLimitConfig = randomPojo(CrmCustomerLimitConfigDO.class); + customerLimitConfigMapper.insert(dbCustomerLimitConfig);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备å‚æ•° + Long id = dbCustomerLimitConfig.getId(); + + // 调用 + customerLimitConfigService.deleteCustomerLimitConfig(id); + // 校验数æ®ä¸å­˜åœ¨äº† + assertNull(customerLimitConfigMapper.selectById(id)); + } + + @Test + public void testDeleteCustomerLimitConfig_notExists() { + // 准备å‚æ•° + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> customerLimitConfigService.deleteCustomerLimitConfig(id), CUSTOMER_LIMIT_CONFIG_NOT_EXISTS); + } + + @Test + @Disabled // TODO 请修改 null 为需è¦çš„值,然åŽåˆ é™¤ @Disabled 注解 + public void testGetCustomerLimitConfigPage() { + // mock æ•°æ® + CrmCustomerLimitConfigDO dbCustomerLimitConfig = randomPojo(CrmCustomerLimitConfigDO.class, o -> { // 等会查询到 + }); + customerLimitConfigMapper.insert(dbCustomerLimitConfig); + // 准备å‚æ•° + CrmCustomerLimitConfigPageReqVO reqVO = new CrmCustomerLimitConfigPageReqVO(); + + // 调用 + PageResult pageResult = customerLimitConfigService.getCustomerLimitConfigPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbCustomerLimitConfig, pageResult.getList().get(0)); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/receivable/CrmCrmReceivablePlanServiceImplTest.java b/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/receivable/CrmCrmReceivablePlanServiceImplTest.java new file mode 100644 index 000000000..4f17abcba --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/receivable/CrmCrmReceivablePlanServiceImplTest.java @@ -0,0 +1,137 @@ +package cn.iocoder.yudao.module.crm.service.receivable; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanCreateReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanUpdateReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO; +import cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivablePlanMapper; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.RECEIVABLE_PLAN_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.*; + +// TODO 芋艿:åŽç»­ï¼Œéœ€è¦è¡¥å……测试用例 + +/** + * {@link CrmReceivablePlanServiceImpl} çš„å•å…ƒæµ‹è¯•ç±» + * + * @author 芋é“æºç  + */ +@Disabled // TODO 芋艿:åŽç»­ fix 补充的å•æµ‹ +@Import(CrmReceivablePlanServiceImpl.class) +public class CrmCrmReceivablePlanServiceImplTest extends BaseDbUnitTest { + + @Resource + private CrmReceivablePlanServiceImpl receivablePlanService; + + @Resource + private CrmReceivablePlanMapper crmReceivablePlanMapper; + + @Test + public void testCreateReceivablePlan_success() { + // 准备å‚æ•° + CrmReceivablePlanCreateReqVO reqVO = randomPojo(CrmReceivablePlanCreateReqVO.class); + + // 调用 + Long receivablePlanId = receivablePlanService.createReceivablePlan(reqVO, 1L); + // 断言 + assertNotNull(receivablePlanId); + // 校验记录的属性是å¦æ­£ç¡® + CrmReceivablePlanDO receivablePlan = crmReceivablePlanMapper.selectById(receivablePlanId); + assertPojoEquals(reqVO, receivablePlan); + } + + @Test + public void testUpdateReceivablePlan_success() { + // mock æ•°æ® + CrmReceivablePlanDO dbReceivablePlan = randomPojo(CrmReceivablePlanDO.class); + crmReceivablePlanMapper.insert(dbReceivablePlan);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备å‚æ•° + CrmReceivablePlanUpdateReqVO reqVO = randomPojo(CrmReceivablePlanUpdateReqVO.class, o -> { + o.setId(dbReceivablePlan.getId()); // 设置更新的 ID + }); + + // 调用 + receivablePlanService.updateReceivablePlan(reqVO); + // 校验是å¦æ›´æ–°æ­£ç¡® + CrmReceivablePlanDO receivablePlan = crmReceivablePlanMapper.selectById(reqVO.getId()); // 获å–最新的 + assertPojoEquals(reqVO, receivablePlan); + } + + @Test + public void testUpdateReceivablePlan_notExists() { + // 准备å‚æ•° + CrmReceivablePlanUpdateReqVO reqVO = randomPojo(CrmReceivablePlanUpdateReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> receivablePlanService.updateReceivablePlan(reqVO), RECEIVABLE_PLAN_NOT_EXISTS); + } + + @Test + public void testDeleteReceivablePlan_success() { + // mock æ•°æ® + CrmReceivablePlanDO dbReceivablePlan = randomPojo(CrmReceivablePlanDO.class); + crmReceivablePlanMapper.insert(dbReceivablePlan);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备å‚æ•° + Long id = dbReceivablePlan.getId(); + + // 调用 + receivablePlanService.deleteReceivablePlan(id); + // 校验数æ®ä¸å­˜åœ¨äº† + assertNull(crmReceivablePlanMapper.selectById(id)); + } + + @Test + public void testDeleteReceivablePlan_notExists() { + // 准备å‚æ•° + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> receivablePlanService.deleteReceivablePlan(id), RECEIVABLE_PLAN_NOT_EXISTS); + } + + @Test + @Disabled // TODO 请修改 null 为需è¦çš„值,然åŽåˆ é™¤ @Disabled 注解 + public void testGetReceivablePlanPage() { + // mock æ•°æ® + CrmReceivablePlanDO dbReceivablePlan = randomPojo(CrmReceivablePlanDO.class, o -> { // 等会查询到 + o.setPeriod(null); + o.setReturnTime(null); + o.setRemindDays(null); + o.setRemindTime(null); + o.setCustomerId(null); + o.setContractId(null); + o.setOwnerUserId(null); + o.setRemark(null); + o.setCreateTime(null); + }); + crmReceivablePlanMapper.insert(dbReceivablePlan); + // 测试 customerId ä¸åŒ¹é… + crmReceivablePlanMapper.insert(cloneIgnoreId(dbReceivablePlan, o -> o.setCustomerId(null))); + // 测试 contractId ä¸åŒ¹é… + crmReceivablePlanMapper.insert(cloneIgnoreId(dbReceivablePlan, o -> o.setContractId(null))); + // 准备å‚æ•° + CrmReceivablePlanPageReqVO reqVO = new CrmReceivablePlanPageReqVO(); + reqVO.setCustomerId(null); + reqVO.setContractId(null); + reqVO.setPageSize(PAGE_SIZE_NONE); + // 调用 + PageResult pageResult = receivablePlanService.getReceivablePlanPage(reqVO, 1L); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbReceivablePlan, pageResult.getList().get(0)); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/receivable/CrmCrmReceivableServiceImplTest.java b/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/receivable/CrmCrmReceivableServiceImplTest.java new file mode 100644 index 000000000..04c466924 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/receivable/CrmCrmReceivableServiceImplTest.java @@ -0,0 +1,143 @@ +package cn.iocoder.yudao.module.crm.service.receivable; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableCreateReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableUpdateReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO; +import cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivableMapper; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.RECEIVABLE_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.*; + +// TODO 芋艿:等实现完,在校验下; +/** + * {@link CrmReceivableServiceImpl} çš„å•å…ƒæµ‹è¯•ç±» + * + * @author 赤焰 + */ +@Disabled // TODO 芋艿:åŽç»­ fix 补充的å•æµ‹ +@Import(CrmReceivableServiceImpl.class) +public class CrmCrmReceivableServiceImplTest extends BaseDbUnitTest { + + @Resource + private CrmReceivableServiceImpl receivableService; + + @Resource + private CrmReceivableMapper crmReceivableMapper; + + @Test + public void testCreateReceivable_success() { + // 准备å‚æ•° + CrmReceivableCreateReqVO reqVO = randomPojo(CrmReceivableCreateReqVO.class); + + // 调用 + Long receivableId = receivableService.createReceivable(reqVO, getLoginUserId()); + // 断言 + assertNotNull(receivableId); + // 校验记录的属性是å¦æ­£ç¡® + CrmReceivableDO receivable = crmReceivableMapper.selectById(receivableId); + assertPojoEquals(reqVO, receivable); + } + + @Test + public void testUpdateReceivable_success() { + // mock æ•°æ® + CrmReceivableDO dbReceivable = randomPojo(CrmReceivableDO.class); + crmReceivableMapper.insert(dbReceivable);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备å‚æ•° + CrmReceivableUpdateReqVO reqVO = randomPojo(CrmReceivableUpdateReqVO.class, o -> { + o.setId(dbReceivable.getId()); // 设置更新的 ID + }); + + // 调用 + receivableService.updateReceivable(reqVO); + // 校验是å¦æ›´æ–°æ­£ç¡® + CrmReceivableDO receivable = crmReceivableMapper.selectById(reqVO.getId()); // 获å–最新的 + assertPojoEquals(reqVO, receivable); + } + + @Test + public void testUpdateReceivable_notExists() { + // 准备å‚æ•° + CrmReceivableUpdateReqVO reqVO = randomPojo(CrmReceivableUpdateReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> receivableService.updateReceivable(reqVO), RECEIVABLE_NOT_EXISTS); + } + + @Test + public void testDeleteReceivable_success() { + // mock æ•°æ® + CrmReceivableDO dbReceivable = randomPojo(CrmReceivableDO.class); + crmReceivableMapper.insert(dbReceivable);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备å‚æ•° + Long id = dbReceivable.getId(); + + // 调用 + receivableService.deleteReceivable(id); + // 校验数æ®ä¸å­˜åœ¨äº† + assertNull(crmReceivableMapper.selectById(id)); + } + + @Test + public void testDeleteReceivable_notExists() { + // 准备å‚æ•° + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> receivableService.deleteReceivable(id), RECEIVABLE_NOT_EXISTS); + } + + @Test + @Disabled // TODO 请修改 null 为需è¦çš„值,然åŽåˆ é™¤ @Disabled 注解 + public void testGetReceivablePage() { + // mock æ•°æ® + CrmReceivableDO dbReceivable = randomPojo(CrmReceivableDO.class, o -> { // 等会查询到 + o.setNo(null); + o.setPlanId(null); + o.setCustomerId(null); + o.setContractId(null); + o.setProcessInstanceId(null); + o.setReturnTime(null); + o.setReturnType(null); + o.setPrice(null); + o.setOwnerUserId(null); + o.setSort(null); + o.setAuditStatus(null); + o.setRemark(null); + o.setCreateTime(null); + }); + crmReceivableMapper.insert(dbReceivable); + // 测试 no ä¸åŒ¹é… + crmReceivableMapper.insert(cloneIgnoreId(dbReceivable, o -> o.setNo(null))); + // 测试 planId ä¸åŒ¹é… + crmReceivableMapper.insert(cloneIgnoreId(dbReceivable, o -> o.setPlanId(null))); + // 测试 customerId ä¸åŒ¹é… + crmReceivableMapper.insert(cloneIgnoreId(dbReceivable, o -> o.setCustomerId(null))); + // 准备å‚æ•° + CrmReceivablePageReqVO reqVO = new CrmReceivablePageReqVO(); + reqVO.setNo(null); + reqVO.setPlanId(null); + reqVO.setCustomerId(null); + + // 调用 + PageResult pageResult = receivableService.getReceivablePage(reqVO, 1L); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbReceivable, pageResult.getList().get(0)); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/test/resources/application-unit-test.yaml b/yudao-module-crm/yudao-module-crm-biz/src/test/resources/application-unit-test.yaml new file mode 100644 index 000000000..0f28838da --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/test/resources/application-unit-test.yaml @@ -0,0 +1,51 @@ +spring: + main: + lazy-initialization: true # å¼€å¯æ‡’加载,加快速度 + banner-mode: off # å•å…ƒæµ‹è¯•ï¼Œç¦ç”¨ Banner + +--- #################### æ•°æ®åº“相关é…ç½® #################### + +spring: + # æ•°æ®æºé…置项 + datasource: + name: ruoyi-vue-pro + url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模å¼ï¼›DATABASE_TO_UPPER é…置表和字段使用å°å†™ + driver-class-name: org.h2.Driver + username: sa + password: + druid: + async-init: true # å•å…ƒæµ‹è¯•ï¼Œå¼‚æ­¥åˆå§‹åŒ– Druid 连接池,æå‡å¯åŠ¨é€Ÿåº¦ + initial-size: 1 # å•å…ƒæµ‹è¯•ï¼Œé…置为 1,æå‡å¯åŠ¨é€Ÿåº¦ + sql: + init: + schema-locations: classpath:/sql/create_tables.sql + + # Redis é…置。Redisson 默认的é…置足够使用,一般ä¸éœ€è¦è¿›è¡Œè°ƒä¼˜ + data: + redis: + host: 127.0.0.1 # åœ°å€ + port: 16379 # 端å£ï¼ˆå•å…ƒæµ‹è¯•ï¼Œä½¿ç”¨ 16379 端å£ï¼‰ + database: 0 # æ•°æ®åº“索引 + +mybatis-plus: + lazy-initialization: true # å•å…ƒæµ‹è¯•ï¼Œè®¾ç½® MyBatis Mapper 延迟加载,加速æ¯ä¸ªå•å…ƒæµ‹è¯• + type-aliases-package: ${yudao.info.base-package}.module.*.dal.dataobject + +--- #################### 定时任务相关é…ç½® #################### + +--- #################### é…置中心相关é…ç½® #################### + +--- #################### æœåŠ¡ä¿éšœç›¸å…³é…ç½® #################### + +# Lock4j é…置项(å•å…ƒæµ‹è¯•ï¼Œç¦ç”¨ Lock4j) + +# Resilience4j é…置项 + +--- #################### 监控相关é…ç½® #################### + +--- #################### 芋é“相关é…ç½® #################### + +# 芋é“é…置项,设置当å‰é¡¹ç›®æ‰€æœ‰è‡ªå®šä¹‰çš„é…ç½® +yudao: + info: + base-package: cn.iocoder.yudao diff --git a/yudao-module-crm/yudao-module-crm-biz/src/test/resources/logback.xml b/yudao-module-crm/yudao-module-crm-biz/src/test/resources/logback.xml new file mode 100644 index 000000000..1d071e479 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/test/resources/logback.xml @@ -0,0 +1,4 @@ + + + + diff --git a/yudao-module-crm/yudao-module-crm-biz/src/test/resources/sql/clean.sql b/yudao-module-crm/yudao-module-crm-biz/src/test/resources/sql/clean.sql new file mode 100644 index 000000000..138780eed --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/test/resources/sql/clean.sql @@ -0,0 +1,11 @@ +DELETE FROM "crm_contract"; + +DELETE FROM "crm_clue"; + +DELETE FROM "crm_receivable"; + +DELETE FROM "crm_receivable_plan"; + +DELETE FROM "crm_customer"; + +DELETE FROM "crm_customer_limit_config"; \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/test/resources/sql/create_tables.sql b/yudao-module-crm/yudao-module-crm-biz/src/test/resources/sql/create_tables.sql new file mode 100644 index 000000000..9528f3dde --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/test/resources/sql/create_tables.sql @@ -0,0 +1,162 @@ +CREATE TABLE IF NOT EXISTS "crm_contract" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar NOT NULL, + "customer_id" bigint, + "business_id" bigint, + "process_instance_id" bigint, + "order_date" varchar, + "owner_user_id" bigint, + "no" varchar, + "start_time" varchar, + "end_time" varchar, + "price" int, + "discount_percent" int, + "product_price" int, + "ro_user_ids" varchar, + "rw_user_ids" varchar, + "contact_id" bigint, + "sign_user_id" bigint, + "contact_last_time" varchar, + "remark" varchar, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT 'åˆåŒè¡¨'; + +CREATE TABLE IF NOT EXISTS "crm_clue" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "transform_status" bit NOT NULL, + "follow_up_status" bit NOT NULL, + "name" varchar NOT NULL, + "customer_id" bigint NOT NULL, + "contact_next_time" varchar, + "telephone" varchar, + "mobile" varchar, + "address" varchar, + "owner_user_id" bigint, + "contact_last_time" varchar, + "remark" varchar, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL, + PRIMARY KEY ("id") +) COMMENT '线索表'; + +CREATE TABLE IF NOT EXISTS "crm_receivable" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "no" varchar, + "plan_id" bigint, + "customer_id" bigint, + "contract_id" bigint, + "check_status" int, + "process_instance_id" bigint, + "return_time" varchar, + "return_type" varchar, + "price" varchar, + "owner_user_id" bigint, + "batch_id" bigint, + "sort" int, + "data_scope" int, + "data_scope_dept_ids" varchar, + "status" int NOT NULL, + "remark" varchar, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '回款'; + +CREATE TABLE IF NOT EXISTS "crm_receivable_plan" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "index_no" bigint, + "receivable_id" bigint, + "status" int NOT NULL, + "check_status" varchar, + "process_instance_id" bigint, + "price" varchar, + "return_time" varchar, + "remind_days" bigint, + "remind_time" varchar, + "customer_id" bigint, + "contract_id" bigint, + "owner_user_id" bigint, + "sort" int, + "remark" varchar, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '回款计划'; + +CREATE TABLE IF NOT EXISTS "crm_customer" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar(255), + "follow_up_status" int NOT NULL, + "lock_status" int NOT NULL, + "deal_status" int NOT NULL, + "industry_id" int, + "level" int, + "source" int, + "mobile" varchar(255), + "telephone" varchar(255), + "website" varchar(255), + "qq" varchar(255), + "wechat" varchar(255), + "email" varchar(255), + "description" varchar(255), + "remark" varchar(255), + "owner_user_id" bigint, + "area_id" int, + "detail_address" varchar(255), + "contact_last_time" datetime, + "contact_next_time" datetime, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL, + PRIMARY KEY ("id") +) COMMENT '客户表'; + +CREATE TABLE IF NOT EXISTS "crm_customer_limit_config" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "type" int NOT NULL, + "user_ids" varchar, + "dept_ids" varchar, + "max_count" int NOT NULL, + "deal_count_enabled" varchar, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL, + PRIMARY KEY ("id") +) COMMENT '客户é™åˆ¶é…置表'; + +CREATE TABLE IF NOT EXISTS "crm_permission" +( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "biz_id" bigint NOT NULL, + "biz_type" int NOT NULL, + "user_id" bigint NOT NULL, + "level" int NOT NULL, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL, + PRIMARY KEY ("id") +) COMMENT '客户é™åˆ¶é…置表'; \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/framework/security/config/SecurityConfiguration.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/framework/security/config/SecurityConfiguration.java index e54e16ca5..4478bfe96 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/framework/security/config/SecurityConfiguration.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/framework/security/config/SecurityConfiguration.java @@ -8,12 +8,12 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer; /** - * Pay 模å—çš„ Security é…ç½® + * Erp 模å—çš„ Security é…ç½® */ -@Configuration("paySecurityConfiguration") +@Configuration("erpSecurityConfiguration") public class SecurityConfiguration { - @Bean("payAuthorizeRequestsCustomizer") + @Bean("erpAuthorizeRequestsCustomizer") public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() { return new AuthorizeRequestsCustomizer() { diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApi.java index 7f558286f..179840971 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApi.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApi.java @@ -43,9 +43,14 @@ public interface DeptApi { * @param ids 部门编å·æ•°ç»„ * @return 部门 Map */ - default Map getDeptMap(Set ids) { + default Map getDeptMap(Collection ids) { List list = getDeptList(ids).getCheckedData(); return CollectionUtils.convertMap(list, DeptRespDTO::getId); } + @GetMapping(PREFIX + "/list-child") + @Operation(summary = "获得指定部门的所有å­éƒ¨é—¨") + @Parameter(name = "id", description = "部门编å·", example = "1024", required = true) + CommonResult> getChildDeptList(@RequestParam("id") Long id); + } diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/PostApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/PostApi.java index 73d3d38a9..bec33087c 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/PostApi.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/PostApi.java @@ -1,6 +1,10 @@ package cn.iocoder.yudao.module.system.api.dept; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.module.system.api.dept.dto.PostRespDTO; import cn.iocoder.yudao.module.system.enums.ApiConstants; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -10,6 +14,8 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import java.util.Collection; +import java.util.List; +import java.util.Map; @FeignClient(name = ApiConstants.NAME) // TODO 芋艿:fallbackFactory = @Tag(name = "RPC æœåŠ¡ - å²—ä½") @@ -22,4 +28,18 @@ public interface PostApi { @Parameter(name = "ids", description = "å²—ä½ç¼–å·æ•°ç»„", example = "1,2", required = true) CommonResult validPostList(@RequestParam("ids") Collection ids); + @GetMapping(PREFIX + "/list") + @Operation(summary = "获得岗ä½åˆ—表") + @Parameter(name = "ids", description = "å²—ä½ç¼–å·æ•°ç»„", example = "1,2", required = true) + CommonResult> getPostList(@RequestParam("ids") Collection ids); + + default Map getPostMap(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return MapUtil.empty(); + } + + List list = getPostList(ids).getData(); + return CollectionUtils.convertMap(list, PostRespDTO::getId); + } + } diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/dto/PostRespDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/dto/PostRespDTO.java new file mode 100644 index 000000000..c2c8cf393 --- /dev/null +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/dto/PostRespDTO.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.system.api.dept.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * å²—ä½ Response DTO + * + * @author 芋é“æºç  + */ +@Schema(description = "RPC æœåŠ¡ - å²—ä½ Response DTO") +@Data +public class PostRespDTO { + + @Schema(description = "å²—ä½ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "å²—ä½å称", requiredMode = Schema.RequiredMode.REQUIRED, example = "å°åœŸè±†") + private String name; + + @Schema(description = "å²—ä½ç¼–ç ", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") + private String code; + + @Schema(description = "å²—ä½æŽ’åº", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer sort; + + @Schema(description = "状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; // å‚è§ CommonStatusEnum 枚举 + +} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java index 113b482e4..13342ec7e 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java @@ -24,6 +24,11 @@ public interface AdminUserApi { @Parameter(name = "id", description = "用户编å·", example = "1", required = true) CommonResult getUser(@RequestParam("id") Long id); + @GetMapping(PREFIX + "/list-by-subordinate") + @Operation(summary = "通过用户 ID 查询用户下属") + @Parameter(name = "id", description = "用户编å·", example = "1", required = true) + CommonResult> getUserListBySubordinate(@RequestParam("id") Long id); + @GetMapping(PREFIX + "/list") @Operation(summary = "通过用户 ID 查询用户们") @Parameter(name = "ids", description = "部门编å·æ•°ç»„", example = "1,2", required = true) @@ -64,6 +69,6 @@ public interface AdminUserApi { @GetMapping(PREFIX + "/valid") @Operation(summary = "校验用户们是å¦æœ‰æ•ˆ") @Parameter(name = "ids", description = "用户编å·æ•°ç»„", example = "3,5", required = true) - CommonResult validateUserList(@RequestParam("ids") Set ids); + CommonResult validateUserList(@RequestParam("ids") Collection ids); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApiImpl.java index e4a4187eb..fa628224d 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApiImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApiImpl.java @@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; import cn.iocoder.yudao.module.system.service.dept.DeptService; +import org.springframework.context.annotation.Bean; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RestController; @@ -39,4 +40,10 @@ public class DeptApiImpl implements DeptApi { return success(true); } + @Override + public CommonResult> getChildDeptList(Long id) { + List depts = deptService.getChildDeptList(id); + return success(BeanUtils.toBean(depts, DeptRespDTO.class)); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dept/PostApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dept/PostApiImpl.java index da68d8f79..a56c56984 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dept/PostApiImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dept/PostApiImpl.java @@ -1,12 +1,16 @@ package cn.iocoder.yudao.module.system.api.dept; import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.api.dept.dto.PostRespDTO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; import cn.iocoder.yudao.module.system.service.dept.PostService; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RestController; import jakarta.annotation.Resource; import java.util.Collection; +import java.util.List; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @@ -23,4 +27,10 @@ public class PostApiImpl implements PostApi { return success(true); } + @Override + public CommonResult> getPostList(Collection ids) { + List list = postService.getPostList(ids); + return success(BeanUtils.toBean(list, PostRespDTO.class)); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java index 4cbcba894..229b7b1d6 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java @@ -1,19 +1,23 @@ package cn.iocoder.yudao.module.system.api.user; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import cn.iocoder.yudao.module.system.service.dept.DeptService; import cn.iocoder.yudao.module.system.service.user.AdminUserService; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RestController; import jakarta.annotation.Resource; -import java.util.Collection; -import java.util.List; -import java.util.Set; + +import java.util.*; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; @RestController // æä¾› RESTful API 接å£ï¼Œç»™ Feign 调用 @Validated @@ -21,6 +25,8 @@ public class AdminUserApiImpl implements AdminUserApi { @Resource private AdminUserService userService; + @Resource + private DeptService deptService; @Override public CommonResult getUser(Long id) { @@ -28,6 +34,34 @@ public class AdminUserApiImpl implements AdminUserApi { return success(BeanUtils.toBean(user, AdminUserRespDTO.class)); } + @Override + public CommonResult> getUserListBySubordinate(Long id) { + // 1.1 获å–用户负责的部门 + AdminUserDO user = userService.getUser(id); + if (user == null) { + return success(Collections.emptyList()); + } + ArrayList deptIds = new ArrayList<>(); + DeptDO dept = deptService.getDept(user.getDeptId()); + if (dept == null) { + return success(Collections.emptyList()); + } + if (ObjUtil.notEqual(dept.getLeaderUserId(), id)) { // 校验为负责人 + return success(Collections.emptyList()); + } + deptIds.add(dept.getId()); + // 1.2 获å–所有å­éƒ¨é—¨ + List childDeptList = deptService.getChildDeptList(dept.getId()); + if (CollUtil.isNotEmpty(childDeptList)) { + deptIds.addAll(convertSet(childDeptList, DeptDO::getId)); + } + + // 2. 获å–éƒ¨é—¨å¯¹åº”çš„ç”¨æˆ·ä¿¡æ¯ + List users = userService.getUserListByDeptIds(deptIds); + users.removeIf(item -> ObjUtil.equal(item.getId(), id)); // 排除自己 + return success(BeanUtils.toBean(users, AdminUserRespDTO.class)); + } + @Override public CommonResult> getUserList(Collection ids) { List users = userService.getUserList(ids); @@ -47,7 +81,7 @@ public class AdminUserApiImpl implements AdminUserApi { } @Override - public CommonResult validateUserList(Set ids) { + public CommonResult validateUserList(Collection ids) { userService.validateUserList(ids); return success(true); }