Commit d80406acbb41fa8e14af172cac772e24e013b028

Authored by YunaiV
2 parents bf966cd6 6b426fcd

Merge branch 'develop' of https://gitee.com/puhui999/ruoyi-vue-pro into develop

# Conflicts:
#	yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/category/ProductCategoryServiceImplTest.java
Showing 56 changed files with 954 additions and 312 deletions
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java
... ... @@ -122,7 +122,12 @@ public class LocalDateTimeUtils {
122 122 return date.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
123 123 }
124 124  
125   - // TODO @puhui999:加下注释哈;
  125 + /**
  126 + * 获取指定日期到现在过了几天,如果指定日期在当前日期之后,获取结果为负。
  127 + *
  128 + * @param dateTime 日期
  129 + * @return 相差天数
  130 + */
126 131 public static Long between(LocalDateTime dateTime) {
127 132 return LocalDateTimeUtil.between(dateTime, LocalDateTime.now(), ChronoUnit.DAYS);
128 133 }
... ...
yudao-module-crm/yudao-module-crm-api/pom.xml
... ... @@ -28,6 +28,12 @@
28 28 <artifactId>spring-boot-starter-validation</artifactId>
29 29 <optional>true</optional>
30 30 </dependency>
  31 + <dependency>
  32 + <groupId>cn.iocoder.boot</groupId>
  33 + <artifactId>yudao-module-system-api</artifactId>
  34 + <version>${revision}</version>
  35 + <optional>true</optional>
  36 + </dependency>
31 37 </dependencies>
32 38  
33 39 </project>
... ...
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
... ... @@ -44,6 +44,7 @@ public interface ErrorCodeConstants {
44 44 ErrorCode CUSTOMER_UNLOCK_FAIL_IS_UNLOCK = new ErrorCode(1_020_006_008, "解锁客户失败,它已经处于未锁定状态");
45 45 ErrorCode CUSTOMER_LOCK_EXCEED_LIMIT = new ErrorCode(1_020_006_009, "锁定客户失败,超出锁定规则上限");
46 46 ErrorCode CUSTOMER_OWNER_EXCEED_LIMIT = new ErrorCode(1_020_006_010, "操作失败,超出客户数拥有上限");
  47 + ErrorCode CUSTOMER_DELETE_FAIL_HAVE_REFERENCE = new ErrorCode(1_020_006_011, "删除客户失败,有关联{}");
47 48  
48 49 // ========== 权限管理 1_020_007_000 ==========
49 50 ErrorCode CRM_PERMISSION_NOT_EXISTS = new ErrorCode(1_020_007_000, "数据权限不存在");
... ... @@ -54,6 +55,7 @@ public interface ErrorCodeConstants {
54 55 ErrorCode CRM_PERMISSION_DELETE_FAIL_EXIST_OWNER = new ErrorCode(1_020_007_005, "删除数据权限失败,原因:存在负责人");
55 56 ErrorCode CRM_PERMISSION_DELETE_DENIED = new ErrorCode(1_020_007_006, "删除数据权限失败,原因:没有权限");
56 57 ErrorCode CRM_PERMISSION_DELETE_SELF_PERMISSION_FAIL_EXIST_OWNER = new ErrorCode(1_020_007_007, "删除数据权限失败,原因:不能删除负责人");
  58 + ErrorCode CRM_PERMISSION_CREATE_FAIL = new ErrorCode(1_020_007_008, "创建数据权限失败,原因:所加用户已有权限");
57 59  
58 60 // ========== 产品 1_020_008_000 ==========
59 61 ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1_020_008_000, "产品不存在");
... ... @@ -77,4 +79,7 @@ public interface ErrorCodeConstants {
77 79 // ========== 客户公海规则设置 1_020_012_000 ==========
78 80 ErrorCode CUSTOMER_LIMIT_CONFIG_NOT_EXISTS = new ErrorCode(1_020_012_000, "客户限制配置不存在");
79 81  
  82 + // ========== 跟进记录 1_020_013_000 ==========
  83 + ErrorCode FOLLOW_UP_RECORD_NOT_EXISTS = new ErrorCode(1_020_013_000, "跟进记录不存在");
  84 +
80 85 }
... ...
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java
1 1 package cn.iocoder.yudao.module.crm.enums;
2 2  
  3 +import static cn.iocoder.yudao.module.crm.enums.operatelog.CrmParseFunctionNameConstants.GET_CONTRACT_BY_ID;
  4 +import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNameConstants.GET_ADMIN_USER_BY_ID;
  5 +
3 6 /**
4 7 * CRM 操作日志枚举
5 8 * 目的:统一管理,也减少 Service 里各种“复杂”字符串
... ... @@ -22,9 +25,9 @@ public interface LogRecordConstants {
22 25 String CRM_CUSTOMER_DELETE_SUB_TYPE = "删除客户";
23 26 String CRM_CUSTOMER_DELETE_SUCCESS = "删除了客户【{{#customerName}}】";
24 27 String CRM_CUSTOMER_TRANSFER_SUB_TYPE = "转移客户";
25   - String CRM_CUSTOMER_TRANSFER_SUCCESS = "将客户【{{#crmCustomer.name}}】的负责人从【{getAdminUserById{#crmCustomer.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】";
26   - String CRM_CUSTOMER_LOCK_SUB_TYPE = "{{#crmCustomer.lockStatus ? '解锁客户' : '锁定客户'}}";
27   - String CRM_CUSTOMER_LOCK_SUCCESS = "{{#crmCustomer.lockStatus ? '将客户【' + #crmCustomer.name + '】解锁' : '将客户【' + #crmCustomer.name + '】锁定'}}";
  28 + String CRM_CUSTOMER_TRANSFER_SUCCESS = "将客户【{{#customer.name}}】的负责人从【{" + GET_ADMIN_USER_BY_ID + "{#customer.ownerUserId}}】变更为了【{" + GET_ADMIN_USER_BY_ID + "{#reqVO.newOwnerUserId}}】";
  29 + String CRM_CUSTOMER_LOCK_SUB_TYPE = "{{#customer.lockStatus ? '解锁客户' : '锁定客户'}}";
  30 + String CRM_CUSTOMER_LOCK_SUCCESS = "{{#customer.lockStatus ? '将客户【' + #customer.name + '】解锁' : '将客户【' + #customer.name + '】锁定'}}";
28 31 String CRM_CUSTOMER_POOL_SUB_TYPE = "客户放入公海";
29 32 String CRM_CUSTOMER_POOL_SUCCESS = "将客户【{{#customerName}}】放入了公海";
30 33 String CRM_CUSTOMER_RECEIVE_SUB_TYPE = "{{#ownerUserName != null ? '分配客户' : '领取客户'}}";
... ... @@ -49,14 +52,38 @@ public interface LogRecordConstants {
49 52 // ======================= CRM_CONTACT 联系人 =======================
50 53  
51 54 String CRM_CONTACT_TYPE = "CRM 联系人";
  55 + String CRM_CONTACT_CREATE_SUB_TYPE = "创建联系人";
  56 + String CRM_CONTACT_CREATE_SUCCESS = "创建了联系人{{#contact.name}}";
  57 + String CRM_CONTACT_UPDATE_SUB_TYPE = "更新联系人";
  58 + String CRM_CONTACT_UPDATE_SUCCESS = "更新了联系人【{{#contactName}}】: {_DIFF{#updateReqVO}}";
  59 + String CRM_CONTACT_DELETE_SUB_TYPE = "删除联系人";
  60 + String CRM_CONTACT_DELETE_SUCCESS = "删除了联系人【{{#contactName}}】";
  61 + String CRM_CONTACT_TRANSFER_SUB_TYPE = "转移联系人";
  62 + String CRM_CONTACT_TRANSFER_SUCCESS = "将联系人【{{#contact.name}}】的负责人从【{" + GET_ADMIN_USER_BY_ID + "{#contact.ownerUserId}}】变更为了【{" + GET_ADMIN_USER_BY_ID + "{#reqVO.newOwnerUserId}}】";
52 63  
53 64 // ======================= CRM_BUSINESS 商机 =======================
54 65  
55 66 String CRM_BUSINESS_TYPE = "CRM 商机";
  67 + String CRM_BUSINESS_CREATE_SUB_TYPE = "创建商机";
  68 + String CRM_BUSINESS_CREATE_SUCCESS = "创建了商机{{#business.name}}";
  69 + String CRM_BUSINESS_UPDATE_SUB_TYPE = "更新商机";
  70 + String CRM_BUSINESS_UPDATE_SUCCESS = "更新了商机【{{#businessName}}】: {_DIFF{#updateReqVO}}";
  71 + String CRM_BUSINESS_DELETE_SUB_TYPE = "删除商机";
  72 + String CRM_BUSINESS_DELETE_SUCCESS = "删除了商机【{{#businessName}}】";
  73 + String CRM_BUSINESS_TRANSFER_SUB_TYPE = "转移商机";
  74 + String CRM_BUSINESS_TRANSFER_SUCCESS = "将商机【{{#business.name}}】的负责人从【{" + GET_ADMIN_USER_BY_ID + "{#business.ownerUserId}}】变更为了【{" + GET_ADMIN_USER_BY_ID + "{#reqVO.newOwnerUserId}}】";
56 75  
57 76 // ======================= CRM_CONTRACT 合同 =======================
58 77  
59 78 String CRM_CONTRACT_TYPE = "CRM 合同";
  79 + String CRM_CONTRACT_CREATE_SUB_TYPE = "创建合同";
  80 + String CRM_CONTRACT_CREATE_SUCCESS = "创建了合同{{#contract.name}}";
  81 + String CRM_CONTRACT_UPDATE_SUB_TYPE = "更新合同";
  82 + String CRM_CONTRACT_UPDATE_SUCCESS = "更新了合同【{{#contractName}}】: {_DIFF{#updateReqVO}}";
  83 + String CRM_CONTRACT_DELETE_SUB_TYPE = "删除合同";
  84 + String CRM_CONTRACT_DELETE_SUCCESS = "删除了合同【{{#contractName}}】";
  85 + String CRM_CONTRACT_TRANSFER_SUB_TYPE = "转移合同";
  86 + String CRM_CONTRACT_TRANSFER_SUCCESS = "将合同【{{#contract.name}}】的负责人从【{" + GET_ADMIN_USER_BY_ID + "{#contract.ownerUserId}}】变更为了【{" + GET_ADMIN_USER_BY_ID + "{#reqVO.newOwnerUserId}}】";
60 87  
61 88 // ======================= CRM_PRODUCT 产品 =======================
62 89  
... ... @@ -79,9 +106,21 @@ public interface LogRecordConstants {
79 106 // ======================= CRM_RECEIVABLE 回款 =======================
80 107  
81 108 String CRM_RECEIVABLE_TYPE = "CRM 回款";
  109 + String CRM_RECEIVABLE_CREATE_SUB_TYPE = "创建回款";
  110 + String CRM_RECEIVABLE_CREATE_SUCCESS = "创建了合同【{" + GET_CONTRACT_BY_ID + "{#receivable.contractId}}】的第【{{#receivable.period}}】期回款";
  111 + String CRM_RECEIVABLE_UPDATE_SUB_TYPE = "更新回款";
  112 + String CRM_RECEIVABLE_UPDATE_SUCCESS = "更新了合同【{" + GET_CONTRACT_BY_ID + "{#receivable.contractId}}】的第【{{#receivable.period}}】期回款: {_DIFF{#updateReqVO}}";
  113 + String CRM_RECEIVABLE_DELETE_SUB_TYPE = "删除回款";
  114 + String CRM_RECEIVABLE_DELETE_SUCCESS = "删除了合同【{" + GET_CONTRACT_BY_ID + "{#receivable.contractId}}】的第【{{#receivable.period}}】期回款";
82 115  
83 116 // ======================= CRM_RECEIVABLE_PLAN 回款计划 =======================
84 117  
85 118 String CRM_RECEIVABLE_PLAN_TYPE = "CRM 回款计划";
  119 + String CRM_RECEIVABLE_PLAN_CREATE_SUB_TYPE = "创建回款计划";
  120 + String CRM_RECEIVABLE_PLAN_CREATE_SUCCESS = "创建了合同【{" + GET_CONTRACT_BY_ID + "{#receivablePlan.contractId}}】的第【{{#receivablePlan.period}}】期回款计划";
  121 + String CRM_RECEIVABLE_PLAN_UPDATE_SUB_TYPE = "更新回款计划";
  122 + String CRM_RECEIVABLE_PLAN_UPDATE_SUCCESS = "更新了合同【{" + GET_CONTRACT_BY_ID + "{#receivablePlan.contractId}}】的第【{{#receivablePlan.period}}】期回款计划: {_DIFF{#updateReqVO}}";
  123 + String CRM_RECEIVABLE_PLAN_DELETE_SUB_TYPE = "删除回款计划";
  124 + String CRM_RECEIVABLE_PLAN_DELETE_SUCCESS = "删除了合同【{" + GET_CONTRACT_BY_ID + "{#receivablePlan.contractId}}】的第【{{#receivablePlan.period}}】期回款计划";
86 125  
87 126 }
... ...
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/operatelog/CrmParseFunctionNameConstants.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.enums.operatelog;
  2 +
  3 +/**
  4 + * functionName 常量枚举
  5 + * 方便别的模块调用
  6 + *
  7 + * @author HUIHUI
  8 + */
  9 +public interface CrmParseFunctionNameConstants {
  10 +
  11 + String GET_CONTACT_BY_ID = "getContactById"; // 获取联系人信息
  12 + String GET_CUSTOMER_BY_ID = "getCustomerById"; // 获取客户信息
  13 + String GET_CUSTOMER_INDUSTRY = "getCustomerIndustry"; // 获取客户行业信息
  14 + String GET_CUSTOMER_LEVEL = "getCustomerLevel"; // 获取客户级别
  15 + String GET_CUSTOMER_SOURCE = "getCustomerSource"; // 获取客户来源
  16 + String GET_CONTRACT_BY_ID = "getContractById"; // 获取合同信息
  17 +
  18 +}
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactSaveReqVO.java
... ... @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;
2 2  
3 3 import cn.iocoder.yudao.framework.common.validation.Mobile;
4 4 import cn.iocoder.yudao.framework.common.validation.Telephone;
5   -import cn.iocoder.yudao.module.crm.framework.operatelog.core.*;
6 5 import com.mzt.logapi.starter.annotation.DiffLogField;
7 6 import io.swagger.v3.oas.annotations.media.Schema;
8 7 import jakarta.validation.constraints.Email;
... ... @@ -14,10 +13,13 @@ import java.time.LocalDateTime;
14 13  
15 14 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
16 15 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  16 +import static cn.iocoder.yudao.module.crm.enums.operatelog.CrmParseFunctionNameConstants.GET_CONTACT_BY_ID;
  17 +import static cn.iocoder.yudao.module.crm.enums.operatelog.CrmParseFunctionNameConstants.GET_CUSTOMER_BY_ID;
  18 +import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNameConstants.*;
17 19  
18 20 @Schema(description = "管理后台 - CRM 联系人创建/更新 Request VO")
19 21 @Data
20   -public class CrmContactSaveReqVO {
  22 +public class CrmContactSaveReqVO {
21 23  
22 24 @Schema(description = "主键", example = "3167")
23 25 private Long id;
... ... @@ -28,11 +30,11 @@ public class CrmContactSaveReqVO {
28 30 private String name;
29 31  
30 32 @Schema(description = "客户编号", example = "10795")
31   - @DiffLogField(name = "姓名", function = CrmCustomerParseFunction.NAME)
  33 + @DiffLogField(name = "姓名", function = GET_CUSTOMER_BY_ID)
32 34 private Long customerId;
33 35  
34 36 @Schema(description = "性别")
35   - @DiffLogField(name = "性别", function = CrmSexParseFunction.NAME)
  37 + @DiffLogField(name = "性别", function = GET_SEX)
36 38 private Integer sex;
37 39  
38 40 @Schema(description = "职位")
... ... @@ -40,11 +42,11 @@ public class CrmContactSaveReqVO {
40 42 private String post;
41 43  
42 44 @Schema(description = "是否关键决策人")
43   - @DiffLogField(name = "关键决策人", function = CrmBooleanParseFunction.NAME)
  45 + @DiffLogField(name = "关键决策人", function = GET_BOOLEAN)
44 46 private Boolean master;
45 47  
46 48 @Schema(description = "直属上级", example = "23457")
47   - @DiffLogField(name = "直属上级", function = CrmContactParseFunction.NAME)
  49 + @DiffLogField(name = "直属上级", function = GET_CONTACT_BY_ID)
48 50 private Long parentId;
49 51  
50 52 @Schema(description = "手机号", example = "1387171766")
... ... @@ -71,7 +73,7 @@ public class CrmContactSaveReqVO {
71 73 private String email;
72 74  
73 75 @Schema(description = "地区编号", example = "20158")
74   - @DiffLogField(name = "所在地", function = "getAreaById")
  76 + @DiffLogField(name = "所在地", function = GET_AREA)
75 77 private Integer areaId;
76 78  
77 79 @Schema(description = "地址")
... ... @@ -84,7 +86,7 @@ public class CrmContactSaveReqVO {
84 86  
85 87 @Schema(description = "负责人用户编号", example = "14334")
86 88 @NotNull(message = "负责人不能为空")
87   - @DiffLogField(name = "负责人", function = CrmSysUserParseFunction.NAME)
  89 + @DiffLogField(name = "负责人", function = GET_ADMIN_USER_BY_ID)
88 90 private Long ownerUserId;
89 91  
90 92 @Schema(description = "最后跟进时间")
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java
1 1 package cn.iocoder.yudao.module.crm.controller.admin.customer;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.map.MapUtil;
  5 +import cn.hutool.core.util.ObjUtil;
4 6 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 8 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
... ... @@ -32,6 +34,7 @@ import org.springframework.validation.annotation.Validated;
32 34 import org.springframework.web.bind.annotation.*;
33 35  
34 36 import java.io.IOException;
  37 +import java.time.LocalDateTime;
35 38 import java.util.List;
36 39 import java.util.Map;
37 40 import java.util.stream.Stream;
... ... @@ -112,32 +115,40 @@ public class CrmCustomerController {
112 115 }
113 116  
114 117 // 2. 拼接数据
115   - Map<Long, Long> poolDayMap = getPoolDayMap(pageResult); // 距离进入公海的时间
  118 + Map<Long, Long> poolDayMap = null;
  119 + if (ObjUtil.notEqual(pageVO.getPool(), Boolean.TRUE)) {
  120 + poolDayMap = getPoolDayMap(pageResult.getList()); // 距离进入公海的时间
  121 + }
116 122 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
117 123 convertSetByFlatMap(pageResult.getList(), user -> Stream.of(Long.parseLong(user.getCreator()), user.getOwnerUserId())));
118 124 Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
119 125 return success(CrmCustomerConvert.INSTANCE.convertPage(pageResult, userMap, deptMap, poolDayMap));
120 126 }
121 127  
122   - // TODO @puhui999:加下注释哈;
123   - private Map<Long, Long> getPoolDayMap(PageResult<CrmCustomerDO> pageResult) {
124   - Map<Long, Long> poolDayMap = null;
  128 + /**
  129 + * 获取距离进入公海的时间
  130 + *
  131 + * @param customerList 客户列表
  132 + * @return Map<key 客户编号, value 距离进入公海的时间>
  133 + */
  134 + private Map<Long, Long> getPoolDayMap(List<CrmCustomerDO> customerList) {
125 135 CrmCustomerPoolConfigDO customerPoolConfig = customerPoolConfigService.getCustomerPoolConfig();
126   - // TODO @puhui999:if return 减少括号
127   - if (customerPoolConfig != null && customerPoolConfig.getEnabled()) { // 有公海配置的情况
128   - // TODO @puhui999:item 改成 customer 更好,容易理解;
129   - poolDayMap = convertMap(pageResult.getList(), CrmCustomerDO::getId, item -> {
130   - long dealExpireDay = 0;
131   - if (!item.getDealStatus()) { // 检查是否成交
132   - dealExpireDay = customerPoolConfig.getDealExpireDays() - LocalDateTimeUtils.between(item.getCreateTime());
133   - }
134   - // TODO @puhui999:需要考虑 contactLastTime 为空的情况哈;
135   - long contactExpireDay = customerPoolConfig.getContactExpireDays() - LocalDateTimeUtils.between(item.getContactLastTime());
136   - return dealExpireDay == 0 ? contactExpireDay : Math.min(dealExpireDay, contactExpireDay);
137   - });
138   - // TODO @puhui999:需要考虑 lock 的情况么?
  136 + if (customerPoolConfig == null || !customerPoolConfig.getEnabled()) {
  137 + return MapUtil.empty();
139 138 }
140   - return poolDayMap;
  139 + // TODO @puhui999:需要考虑 lock 的情况么? 回复:锁定正常显示距离进入公海的时间有个提示
  140 + return convertMap(customerList, CrmCustomerDO::getId, customer -> {
  141 + long dealExpireDay = 0;
  142 + if (!customer.getDealStatus()) { // 检查是否成交
  143 + dealExpireDay = customerPoolConfig.getDealExpireDays() - LocalDateTimeUtils.between(customer.getCreateTime());
  144 + }
  145 + LocalDateTime lastTime = customer.getContactLastTime() != null ? customer.getContactLastTime() : customer.getCreateTime();
  146 + long contactExpireDay = customerPoolConfig.getContactExpireDays() - LocalDateTimeUtils.between(lastTime);
  147 + if (contactExpireDay < 0) {
  148 + contactExpireDay = 0; // 如果为负的话重置为零
  149 + }
  150 + return Math.min(dealExpireDay, contactExpireDay);
  151 + });
141 152 }
142 153  
143 154 @GetMapping(value = "/list-all-simple")
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerPoolConfigController.java
1 1 package cn.iocoder.yudao.module.crm.controller.admin.customer;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
4 5 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.poolconfig.CrmCustomerPoolConfigRespVO;
5 6 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.poolconfig.CrmCustomerPoolConfigSaveReqVO;
6   -import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert;
7 7 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;
8 8 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerPoolConfigService;
9 9 import io.swagger.v3.oas.annotations.Operation;
... ... @@ -30,7 +30,7 @@ public class CrmCustomerPoolConfigController {
30 30 @PreAuthorize("@ss.hasPermission('crm:customer-pool-config:query')")
31 31 public CommonResult<CrmCustomerPoolConfigRespVO> getCustomerPoolConfig() {
32 32 CrmCustomerPoolConfigDO customerPoolConfig = customerPoolConfigService.getCustomerPoolConfig();
33   - return success(CrmCustomerConvert.INSTANCE.convert(customerPoolConfig));
  33 + return success(BeanUtils.toBean(customerPoolConfig, CrmCustomerPoolConfigRespVO.class));
34 34 }
35 35  
36 36 @PutMapping("/save")
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerDistributeReqVO.java
1 1 package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
2 2  
3 3 import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotEmpty;
4 5 import jakarta.validation.constraints.NotNull;
5 6 import lombok.Data;
6 7  
... ... @@ -11,7 +12,7 @@ import java.util.List;
11 12 public class CrmCustomerDistributeReqVO {
12 13  
13 14 @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1024]")
14   - @NotNull(message = "客户编号不能为空") // TODO @puhui999:list 是 @NotEmpty
  15 + @NotEmpty(message = "客户编号不能为空")
15 16 private List<Long> ids;
16 17  
17 18 @Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerSaveReqVO.java
... ... @@ -5,9 +5,6 @@ import cn.iocoder.yudao.framework.common.validation.Mobile;
5 5 import cn.iocoder.yudao.framework.common.validation.Telephone;
6 6 import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
7 7 import cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLevelEnum;
8   -import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmIndustryParseFunction;
9   -import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmLevelParseFunction;
10   -import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmSourceParseFunction;
11 8 import com.mzt.logapi.starter.annotation.DiffLogField;
12 9 import io.swagger.v3.oas.annotations.media.Schema;
13 10 import jakarta.validation.constraints.Email;
... ... @@ -20,6 +17,8 @@ import java.time.LocalDateTime;
20 17  
21 18 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
22 19 import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY;
  20 +import static cn.iocoder.yudao.module.crm.enums.operatelog.CrmParseFunctionNameConstants.*;
  21 +import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNameConstants.GET_AREA;
23 22  
24 23 @Schema(description = "管理后台 - CRM 客户新增/修改 Request VO")
25 24 @Data
... ... @@ -34,17 +33,17 @@ public class CrmCustomerSaveReqVO {
34 33 private String name;
35 34  
36 35 @Schema(description = "所属行业", example = "1")
37   - @DiffLogField(name = "所属行业", function = CrmIndustryParseFunction.NAME)
  36 + @DiffLogField(name = "所属行业", function = GET_CUSTOMER_INDUSTRY)
38 37 @DictFormat(CRM_CUSTOMER_INDUSTRY)
39 38 private Integer industryId;
40 39  
41 40 @Schema(description = "客户等级", example = "2")
42   - @DiffLogField(name = "客户等级", function = CrmLevelParseFunction.NAME)
  41 + @DiffLogField(name = "客户等级", function = GET_CUSTOMER_LEVEL)
43 42 @InEnum(CrmCustomerLevelEnum.class)
44 43 private Integer level;
45 44  
46 45 @Schema(description = "客户来源", example = "3")
47   - @DiffLogField(name = "客户来源", function = CrmSourceParseFunction.NAME)
  46 + @DiffLogField(name = "客户来源", function = GET_CUSTOMER_SOURCE)
48 47 private Integer source;
49 48  
50 49 @Schema(description = "手机", example = "18000000000")
... ... @@ -87,7 +86,7 @@ public class CrmCustomerSaveReqVO {
87 86 private String remark;
88 87  
89 88 @Schema(description = "地区编号", example = "20158")
90   - @DiffLogField(name = "地区编号", function = "getAreaById")
  89 + @DiffLogField(name = "地区编号", function = GET_AREA)
91 90 private Integer areaId;
92 91  
93 92 @Schema(description = "详细地址", example = "北京市海淀区")
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/limitconfig/CrmCustomerLimitConfigSaveReqVO.java
... ... @@ -7,6 +7,9 @@ import lombok.Data;
7 7  
8 8 import java.util.List;
9 9  
  10 +import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNameConstants.GET_ADMIN_USER_BY_ID;
  11 +import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNameConstants.GET_DEPT_BY_ID;
  12 +
10 13 @Schema(description = "管理后台 - 客户限制配置创建/更新 Request VO")
11 14 @Data
12 15 public class CrmCustomerLimitConfigSaveReqVO {
... ... @@ -19,13 +22,12 @@ public class CrmCustomerLimitConfigSaveReqVO {
19 22 @DiffLogField(name = "规则类型")
20 23 private Integer type;
21 24  
22   - // TODO @puhui999:可以把 Function 那的 functionName 搞成 NAME 枚举,这里直接引用。这样后续改动更方便哈。
23 25 @Schema(description = "规则适用人群")
24   - @DiffLogField(name = "规则适用人群", function = "getAdminUserById")
  26 + @DiffLogField(name = "规则适用人群", function = GET_ADMIN_USER_BY_ID)
25 27 private List<Long> userIds;
26 28  
27 29 @Schema(description = "规则适用部门")
28   - @DiffLogField(name = "规则适用部门", function = "getDeptById")
  30 + @DiffLogField(name = "规则适用部门", function = GET_DEPT_BY_ID)
29 31 private List<Long> deptIds;
30 32  
31 33 @Schema(description = "数量上限", requiredMode = Schema.RequiredMode.REQUIRED, example = "28384")
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/CrmFollowUpRecordController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.followup;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
  8 +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
  9 +import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordPageReqVO;
  10 +import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordRespVO;
  11 +import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordSaveReqVO;
  12 +import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;
  13 +import cn.iocoder.yudao.module.crm.service.followup.CrmFollowUpRecordService;
  14 +import io.swagger.v3.oas.annotations.Operation;
  15 +import io.swagger.v3.oas.annotations.Parameter;
  16 +import io.swagger.v3.oas.annotations.tags.Tag;
  17 +import jakarta.annotation.Resource;
  18 +import jakarta.servlet.http.HttpServletResponse;
  19 +import jakarta.validation.Valid;
  20 +import org.springframework.security.access.prepost.PreAuthorize;
  21 +import org.springframework.validation.annotation.Validated;
  22 +import org.springframework.web.bind.annotation.*;
  23 +
  24 +import java.io.IOException;
  25 +import java.util.List;
  26 +
  27 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  28 +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
  29 +
  30 +
  31 +@Tag(name = "管理后台 - 跟进记录")
  32 +@RestController
  33 +@RequestMapping("/crm/follow-up-record")
  34 +@Validated
  35 +public class CrmFollowUpRecordController {
  36 +
  37 + @Resource
  38 + private CrmFollowUpRecordService crmFollowUpRecordService;
  39 +
  40 + @PostMapping("/create")
  41 + @Operation(summary = "创建跟进记录")
  42 + @PreAuthorize("@ss.hasPermission('crm:follow-up-record:create')")
  43 + public CommonResult<Long> createFollowUpRecord(@Valid @RequestBody CrmFollowUpRecordSaveReqVO createReqVO) {
  44 + return success(crmFollowUpRecordService.createFollowUpRecord(createReqVO));
  45 + }
  46 +
  47 + @PutMapping("/update")
  48 + @Operation(summary = "更新跟进记录")
  49 + @PreAuthorize("@ss.hasPermission('crm:follow-up-record:update')")
  50 + public CommonResult<Boolean> updateFollowUpRecord(@Valid @RequestBody CrmFollowUpRecordSaveReqVO updateReqVO) {
  51 + crmFollowUpRecordService.updateFollowUpRecord(updateReqVO);
  52 + return success(true);
  53 + }
  54 +
  55 + @DeleteMapping("/delete")
  56 + @Operation(summary = "删除跟进记录")
  57 + @Parameter(name = "id", description = "编号", required = true)
  58 + @PreAuthorize("@ss.hasPermission('crm:follow-up-record:delete')")
  59 + public CommonResult<Boolean> deleteFollowUpRecord(@RequestParam("id") Long id) {
  60 + crmFollowUpRecordService.deleteFollowUpRecord(id);
  61 + return success(true);
  62 + }
  63 +
  64 + @GetMapping("/get")
  65 + @Operation(summary = "获得跟进记录")
  66 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  67 + @PreAuthorize("@ss.hasPermission('crm:follow-up-record:query')")
  68 + public CommonResult<CrmFollowUpRecordRespVO> getFollowUpRecord(@RequestParam("id") Long id) {
  69 + CrmFollowUpRecordDO followUpRecord = crmFollowUpRecordService.getFollowUpRecord(id);
  70 + return success(BeanUtils.toBean(followUpRecord, CrmFollowUpRecordRespVO.class));
  71 + }
  72 +
  73 + @GetMapping("/page")
  74 + @Operation(summary = "获得跟进记录分页")
  75 + @PreAuthorize("@ss.hasPermission('crm:follow-up-record:query')")
  76 + public CommonResult<PageResult<CrmFollowUpRecordRespVO>> getFollowUpRecordPage(@Valid CrmFollowUpRecordPageReqVO pageReqVO) {
  77 + PageResult<CrmFollowUpRecordDO> pageResult = crmFollowUpRecordService.getFollowUpRecordPage(pageReqVO);
  78 + return success(BeanUtils.toBean(pageResult, CrmFollowUpRecordRespVO.class));
  79 + }
  80 +
  81 + @GetMapping("/export-excel")
  82 + @Operation(summary = "导出跟进记录 Excel")
  83 + @PreAuthorize("@ss.hasPermission('crm:follow-up-record:export')")
  84 + @OperateLog(type = EXPORT)
  85 + public void exportFollowUpRecordExcel(@Valid CrmFollowUpRecordPageReqVO pageReqVO,
  86 + HttpServletResponse response) throws IOException {
  87 + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
  88 + List<CrmFollowUpRecordDO> list = crmFollowUpRecordService.getFollowUpRecordPage(pageReqVO).getList();
  89 + // 导出 Excel
  90 + ExcelUtils.write(response, "跟进记录.xls", "数据", CrmFollowUpRecordRespVO.class,
  91 + BeanUtils.toBean(list, CrmFollowUpRecordRespVO.class));
  92 + }
  93 +
  94 +}
0 95 \ No newline at end of file
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.followup.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +import org.springframework.format.annotation.DateTimeFormat;
  9 +
  10 +import java.time.LocalDateTime;
  11 +
  12 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  13 +
  14 +@Schema(description = "管理后台 - 跟进记录分页 Request VO")
  15 +@Data
  16 +@EqualsAndHashCode(callSuper = true)
  17 +@ToString(callSuper = true)
  18 +public class CrmFollowUpRecordPageReqVO extends PageParam {
  19 +
  20 + @Schema(description = "数据类型", example = "2")
  21 + private Integer bizType;
  22 +
  23 + @Schema(description = "数据编号", example = "5564")
  24 + private Long bizId;
  25 +
  26 + @Schema(description = "跟进类型", example = "2")
  27 + private Integer type;
  28 +
  29 + @Schema(description = "下次联系时间")
  30 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  31 + private LocalDateTime[] nextTime;
  32 +
  33 + @Schema(description = "创建时间")
  34 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  35 + private LocalDateTime[] createTime;
  36 +
  37 +}
0 38 \ No newline at end of file
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.followup.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
  4 +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
  5 +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
  6 +import com.alibaba.excel.annotation.ExcelProperty;
  7 +import io.swagger.v3.oas.annotations.media.Schema;
  8 +import lombok.Data;
  9 +
  10 +import java.time.LocalDateTime;
  11 +
  12 +@Schema(description = "管理后台 - 跟进记录 Response VO")
  13 +@Data
  14 +@ExcelIgnoreUnannotated
  15 +public class CrmFollowUpRecordRespVO {
  16 +
  17 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "28800")
  18 + @ExcelProperty("编号")
  19 + private Long id;
  20 +
  21 + @Schema(description = "数据类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  22 + @ExcelProperty("数据类型")
  23 + private Integer bizType;
  24 +
  25 + @Schema(description = "数据编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "5564")
  26 + @ExcelProperty("数据编号")
  27 + private Long bizId;
  28 +
  29 + @Schema(description = "跟进类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  30 + @ExcelProperty(value = "跟进类型", converter = DictConvert.class)
  31 + @DictFormat("crm_follow_up_type") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
  32 + private Integer type;
  33 +
  34 + @Schema(description = "跟进内容", requiredMode = Schema.RequiredMode.REQUIRED)
  35 + @ExcelProperty("跟进内容")
  36 + private String content;
  37 +
  38 + @Schema(description = "下次联系时间", requiredMode = Schema.RequiredMode.REQUIRED)
  39 + @ExcelProperty("下次联系时间")
  40 + private LocalDateTime nextTime;
  41 +
  42 + @Schema(description = "关联的商机编号数组")
  43 + @ExcelProperty("关联的商机编号数组")
  44 + private String businessIds;
  45 +
  46 + @Schema(description = "关联的联系人编号数组")
  47 + @ExcelProperty("关联的联系人编号数组")
  48 + private String contactIds;
  49 +
  50 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  51 + @ExcelProperty("创建时间")
  52 + private LocalDateTime createTime;
  53 +
  54 +}
0 55 \ No newline at end of file
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.followup.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotEmpty;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import lombok.Data;
  7 +
  8 +import java.time.LocalDateTime;
  9 +
  10 +@Schema(description = "管理后台 - 跟进记录新增/修改 Request VO")
  11 +@Data
  12 +public class CrmFollowUpRecordSaveReqVO {
  13 +
  14 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "28800")
  15 + private Long id;
  16 +
  17 + @Schema(description = "数据类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  18 + @NotNull(message = "数据类型不能为空")
  19 + private Integer bizType;
  20 +
  21 + @Schema(description = "数据编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "5564")
  22 + @NotNull(message = "数据编号不能为空")
  23 + private Long bizId;
  24 +
  25 + @Schema(description = "跟进类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  26 + @NotNull(message = "跟进类型不能为空")
  27 + private Integer type;
  28 +
  29 + @Schema(description = "跟进内容", requiredMode = Schema.RequiredMode.REQUIRED)
  30 + @NotEmpty(message = "跟进内容不能为空")
  31 + private String content;
  32 +
  33 + @Schema(description = "下次联系时间", requiredMode = Schema.RequiredMode.REQUIRED)
  34 + @NotNull(message = "下次联系时间不能为空")
  35 + private LocalDateTime nextTime;
  36 +
  37 + @Schema(description = "关联的商机编号数组")
  38 + private String businessIds;
  39 +
  40 + @Schema(description = "关联的联系人编号数组")
  41 + private String contactIds;
  42 +
  43 +}
0 44 \ No newline at end of file
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java
... ... @@ -7,7 +7,10 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 7 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
8 8 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
9 9 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
10   -import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.*;
  10 +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableCreateReqVO;
  11 +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO;
  12 +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO;
  13 +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableUpdateReqVO;
11 14 import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivableConvert;
12 15 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
13 16 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
... ... @@ -59,7 +62,7 @@ public class CrmReceivableController {
59 62 @Operation(summary = "创建回款")
60 63 @PreAuthorize("@ss.hasPermission('crm:receivable:create')")
61 64 public CommonResult<Long> createReceivable(@Valid @RequestBody CrmReceivableCreateReqVO createReqVO) {
62   - return success(receivableService.createReceivable(createReqVO));
  65 + return success(receivableService.createReceivable(createReqVO, getLoginUserId()));
63 66 }
64 67  
65 68 @PutMapping("/update")
... ... @@ -141,12 +144,4 @@ public class CrmReceivableController {
141 144 return CrmReceivableConvert.INSTANCE.convertPage(pageResult, userMap, customerList, contractList);
142 145 }
143 146  
144   - @PutMapping("/transfer")
145   - @Operation(summary = "回款转移")
146   - @PreAuthorize("@ss.hasPermission('crm:receivable:update')")
147   - public CommonResult<Boolean> transfer(@Valid @RequestBody CrmReceivableTransferReqVO reqVO) {
148   - receivableService.transferReceivable(reqVO, getLoginUserId());
149   - return success(true);
150   - }
151   -
152 147 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java
... ... @@ -7,7 +7,10 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 7 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
8 8 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
9 9 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
10   -import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.*;
  10 +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanCreateReqVO;
  11 +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO;
  12 +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanRespVO;
  13 +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanUpdateReqVO;
11 14 import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivablePlanConvert;
12 15 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
13 16 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
... ... @@ -148,12 +151,4 @@ public class CrmReceivablePlanController {
148 151 return CrmReceivablePlanConvert.INSTANCE.convertPage(pageResult, userMap, customerList, contractList, receivableList);
149 152 }
150 153  
151   - @PutMapping("/transfer")
152   - @Operation(summary = "回款计划转移")
153   - @PreAuthorize("@ss.hasPermission('crm:receivable-plan:update')")
154   - public CommonResult<Boolean> transfer(@Valid @RequestBody CrmReceivablePlanTransferReqVO reqVO) {
155   - receivablePlanService.transferReceivablePlan(reqVO, getLoginUserId());
156   - return success(true);
157   - }
158   -
159 154 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/customer/CrmCustomerConvert.java
... ... @@ -3,13 +3,8 @@ package cn.iocoder.yudao.module.crm.convert.customer;
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
5 5 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerRespVO;
6   -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerSaveReqVO;
7 6 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerTransferReqVO;
8   -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.poolconfig.CrmCustomerPoolConfigRespVO;
9   -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.poolconfig.CrmCustomerPoolConfigSaveReqVO;
10   -import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;
11 7 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
12   -import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;
13 8 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
14 9 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
15 10 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
... ... @@ -31,9 +26,6 @@ public interface CrmCustomerConvert {
31 26  
32 27 CrmCustomerConvert INSTANCE = Mappers.getMapper(CrmCustomerConvert.class);
33 28  
34   - // TODO @puhui999:可以清理掉可以用 BeanUtil 替代的方法哈
35   - CrmCustomerDO convert(CrmCustomerSaveReqVO bean);
36   -
37 29 CrmCustomerRespVO convert(CrmCustomerDO bean);
38 30  
39 31 /**
... ... @@ -74,12 +66,4 @@ public interface CrmCustomerConvert {
74 66 return result;
75 67 }
76 68  
77   - CrmCustomerPoolConfigRespVO convert(CrmCustomerPoolConfigDO customerPoolConfig);
78   -
79   - CrmCustomerPoolConfigDO convert(CrmCustomerPoolConfigSaveReqVO updateReqVO);
80   -
81   - // TODO @min:使用 BeanUtils 拷贝哈。我们慢慢简单的对象,不再直接基于 convert 做啦。
82   - @Mapping(ignore = true, target = "id")
83   - CrmCustomerSaveReqVO convert(CrmClueDO bean);
84   -
85 69 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/customer/CrmCustomerLimitConfigConvert.java
... ... @@ -3,14 +3,12 @@ package cn.iocoder.yudao.module.crm.convert.customer;
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
5 5 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigRespVO;
6   -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigSaveReqVO;
7 6 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO;
8 7 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
9 8 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
10 9 import org.mapstruct.Mapper;
11 10 import org.mapstruct.factory.Mappers;
12 11  
13   -import java.util.List;
14 12 import java.util.Map;
15 13  
16 14 /**
... ... @@ -23,13 +21,8 @@ public interface CrmCustomerLimitConfigConvert {
23 21  
24 22 CrmCustomerLimitConfigConvert INSTANCE = Mappers.getMapper(CrmCustomerLimitConfigConvert.class);
25 23  
26   - // TODO @puhui999:可以把 convert 改成 BeanUtils
27   - CrmCustomerLimitConfigDO convert(CrmCustomerLimitConfigSaveReqVO bean);
28   -
29 24 CrmCustomerLimitConfigRespVO convert(CrmCustomerLimitConfigDO bean);
30 25  
31   - List<CrmCustomerLimitConfigRespVO> convertList(List<CrmCustomerLimitConfigDO> list);
32   -
33 26 PageResult<CrmCustomerLimitConfigRespVO> convertPage(PageResult<CrmCustomerLimitConfigDO> page);
34 27  
35 28 default PageResult<CrmCustomerLimitConfigRespVO> convertPage(PageResult<CrmCustomerLimitConfigDO> pageResult,
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contact/CrmContactDO.java
... ... @@ -105,7 +105,10 @@ public class CrmContactDO extends BaseDO {
105 105 * 最后跟进时间
106 106 */
107 107 private LocalDateTime contactLastTime;
108   - // TODO @puhui999:增加一个字段 contactLastContent;最后跟进内容
  108 + /**
  109 + * 最后跟进内容
  110 + */
  111 + private String contactLastContent;
109 112 /**
110 113 * 下次联系时间
111 114 */
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerDO.java
... ... @@ -118,7 +118,10 @@ public class CrmCustomerDO extends BaseDO {
118 118 * 最后跟进时间
119 119 */
120 120 private LocalDateTime contactLastTime;
121   - // TODO @puhui999:增加一个字段 contactLastContent;最后跟进内容
  121 + /**
  122 + * 最后跟进内容
  123 + */
  124 + private String contactLastContent;
122 125 /**
123 126 * 下次联系时间
124 127 */
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/followup/CrmFollowUpRecordDO.java
1 1 package cn.iocoder.yudao.module.crm.dal.dataobject.followup;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
4 5 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
5 6 import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
6 7 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
7 8 import com.baomidou.mybatisplus.annotation.KeySequence;
  9 +import com.baomidou.mybatisplus.annotation.TableField;
8 10 import com.baomidou.mybatisplus.annotation.TableId;
9 11 import com.baomidou.mybatisplus.annotation.TableName;
10 12 import lombok.*;
... ... @@ -21,7 +23,7 @@ import java.util.List;
21 23 *
22 24 * @author 芋道源码
23 25 */
24   -@TableName(value = "crm_follow_up_record")
  26 +@TableName(value = "crm_follow_up_record", autoResultMap = true)
25 27 @KeySequence("crm_follow_up_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
26 28 @Data
27 29 @EqualsAndHashCode(callSuper = true)
... ... @@ -70,12 +72,14 @@ public class CrmFollowUpRecordDO extends BaseDO {
70 72 *
71 73 * 关联 {@link CrmBusinessDO#getId()}
72 74 */
  75 + @TableField(typeHandler = LongListTypeHandler.class)
73 76 private List<Long> businessIds;
74 77 /**
75 78 * 关联的联系人编号数组
76 79 *
77 80 * 关联 {@link CrmContactDO#getId()}
78 81 */
  82 + @TableField(typeHandler = LongListTypeHandler.class)
79 83 private List<Long> contactIds;
80 84  
81 85 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java
... ... @@ -28,6 +28,12 @@ public interface CrmContactMapper extends BaseMapperX&lt;CrmContactDO&gt; {
28 28 .set(CrmContactDO::getOwnerUserId, ownerUserId));
29 29 }
30 30  
  31 + default int updateOwnerUserIdByCustomerId(Long customerId, Long ownerUserId) {
  32 + return update(new LambdaUpdateWrapper<CrmContactDO>()
  33 + .eq(CrmContactDO::getCustomerId, customerId)
  34 + .set(CrmContactDO::getOwnerUserId, ownerUserId));
  35 + }
  36 +
31 37 default PageResult<CrmContactDO> selectPageByCustomerId(CrmContactPageReqVO pageVO) {
32 38 return selectPage(pageVO, new LambdaQueryWrapperX<CrmContactDO>()
33 39 .eq(CrmContactDO::getCustomerId, pageVO.getCustomerId()) // 指定客户编号
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerPoolConfigMapper.java
1 1 package cn.iocoder.yudao.module.crm.dal.mysql.customer;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  4 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
4 5 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;
5 6 import org.apache.ibatis.annotations.Mapper;
6 7  
... ... @@ -11,4 +12,9 @@ import org.apache.ibatis.annotations.Mapper;
11 12 */
12 13 @Mapper
13 14 public interface CrmCustomerPoolConfigMapper extends BaseMapperX<CrmCustomerPoolConfigDO> {
  15 +
  16 + default CrmCustomerPoolConfigDO selectOne() {
  17 + return selectOne(new LambdaQueryWrapperX<CrmCustomerPoolConfigDO>().last("LIMIT 1"));
  18 + }
  19 +
14 20 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/followup/CrmFollowUpRecordMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.dal.mysql.followup;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordPageReqVO;
  7 +import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;
  8 +import org.apache.ibatis.annotations.Mapper;
  9 +
  10 +/**
  11 + * 跟进记录 Mapper
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@Mapper
  16 +public interface CrmFollowUpRecordMapper extends BaseMapperX<CrmFollowUpRecordDO> {
  17 +
  18 + default PageResult<CrmFollowUpRecordDO> selectPage(CrmFollowUpRecordPageReqVO reqVO) {
  19 + return selectPage(reqVO, new LambdaQueryWrapperX<CrmFollowUpRecordDO>()
  20 + .eqIfPresent(CrmFollowUpRecordDO::getBizType, reqVO.getBizType())
  21 + .eqIfPresent(CrmFollowUpRecordDO::getBizId, reqVO.getBizId())
  22 + .eqIfPresent(CrmFollowUpRecordDO::getType, reqVO.getType())
  23 + .betweenIfPresent(CrmFollowUpRecordDO::getNextTime, reqVO.getNextTime())
  24 + .betweenIfPresent(CrmFollowUpRecordDO::getCreateTime, reqVO.getCreateTime())
  25 + .orderByDesc(CrmFollowUpRecordDO::getId));
  26 + }
  27 +
  28 +}
0 29 \ No newline at end of file
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/permission/CrmPermissionMapper.java
... ... @@ -59,4 +59,11 @@ public interface CrmPermissionMapper extends BaseMapperX&lt;CrmPermissionDO&gt; {
59 59 .eq(CrmPermissionDO::getBizId, bizId));
60 60 }
61 61  
  62 + default Long selectListByBiz(Collection<Integer> bizTypes, Collection<Long> bizIds, Collection<Long> userIds) {
  63 + return selectCount(new LambdaQueryWrapperX<CrmPermissionDO>()
  64 + .in(CrmPermissionDO::getBizType, bizTypes)
  65 + .in(CrmPermissionDO::getBizId, bizIds)
  66 + .in(CrmPermissionDO::getUserId, userIds));
  67 + }
  68 +
62 69 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmContactParseFunction.java
... ... @@ -8,8 +8,10 @@ import jakarta.annotation.Resource;
8 8 import lombok.extern.slf4j.Slf4j;
9 9 import org.springframework.stereotype.Component;
10 10  
  11 +import static cn.iocoder.yudao.module.crm.enums.operatelog.CrmParseFunctionNameConstants.GET_CONTACT_BY_ID;
  12 +
11 13 /**
12   - * 行业的 {@link IParseFunction} 实现类
  14 + * CRM 联系人的 {@link IParseFunction} 实现类
13 15 *
14 16 * @author HUIHUI
15 17 */
... ... @@ -17,8 +19,6 @@ import org.springframework.stereotype.Component;
17 19 @Slf4j
18 20 public class CrmContactParseFunction implements IParseFunction {
19 21  
20   - public static final String NAME = "getContactById";
21   -
22 22 @Resource
23 23 private CrmContactService contactService;
24 24  
... ... @@ -29,7 +29,7 @@ public class CrmContactParseFunction implements IParseFunction {
29 29  
30 30 @Override
31 31 public String functionName() {
32   - return NAME;
  32 + return GET_CONTACT_BY_ID;
33 33 }
34 34  
35 35 @Override
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmSysUserParseFunction.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmContractParseFunction.java
1 1 package cn.iocoder.yudao.module.crm.framework.operatelog.core;
2 2  
3 3 import cn.hutool.core.util.StrUtil;
4   -import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
5   -import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  4 +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
  5 +import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
6 6 import com.mzt.logapi.service.IParseFunction;
7 7 import jakarta.annotation.Resource;
8 8 import lombok.extern.slf4j.Slf4j;
9 9 import org.springframework.stereotype.Component;
10 10  
  11 +import static cn.iocoder.yudao.module.crm.enums.operatelog.CrmParseFunctionNameConstants.GET_CONTRACT_BY_ID;
  12 +
11 13 /**
12   - * 行业的 {@link IParseFunction} 实现类
  14 + * CRM 合同的 {@link IParseFunction} 实现类
13 15 *
14 16 * @author HUIHUI
15 17 */
16 18 @Component
17 19 @Slf4j
18   -public class CrmSysUserParseFunction implements IParseFunction {
19   -
20   - public static final String NAME = "getUserById";
  20 +public class CrmContractParseFunction implements IParseFunction {
21 21  
22 22 @Resource
23   - private AdminUserApi adminUserApi;
  23 + private CrmContractService contractService;
24 24  
25 25 @Override
26 26 public boolean executeBefore() {
... ... @@ -29,7 +29,7 @@ public class CrmSysUserParseFunction implements IParseFunction {
29 29  
30 30 @Override
31 31 public String functionName() {
32   - return NAME;
  32 + return GET_CONTRACT_BY_ID;
33 33 }
34 34  
35 35 @Override
... ... @@ -37,8 +37,8 @@ public class CrmSysUserParseFunction implements IParseFunction {
37 37 if (StrUtil.isEmptyIfStr(value)) {
38 38 return "";
39 39 }
40   - AdminUserRespDTO adminUserRespDTO = adminUserApi.getUser(Long.parseLong(value.toString()));
41   - return adminUserRespDTO == null ? "" : adminUserRespDTO.getNickname();
  40 + CrmContractDO contract = contractService.getContract(Long.parseLong(value.toString()));
  41 + return contract == null ? "" : contract.getName();
42 42 }
43 43  
44 44 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmIndustryParseFunction.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmCustomerIndustryParseFunction.java
... ... @@ -7,6 +7,7 @@ import lombok.extern.slf4j.Slf4j;
7 7 import org.springframework.stereotype.Component;
8 8  
9 9 import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY;
  10 +import static cn.iocoder.yudao.module.crm.enums.operatelog.CrmParseFunctionNameConstants.GET_CUSTOMER_INDUSTRY;
10 11  
11 12 /**
12 13 * 行业的 {@link IParseFunction} 实现类
... ... @@ -15,9 +16,7 @@ import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_I
15 16 */
16 17 @Component
17 18 @Slf4j
18   -public class CrmIndustryParseFunction implements IParseFunction {
19   -
20   - public static final String NAME = "getIndustryById";
  19 +public class CrmCustomerIndustryParseFunction implements IParseFunction {
21 20  
22 21 @Override
23 22 public boolean executeBefore() {
... ... @@ -26,7 +25,7 @@ public class CrmIndustryParseFunction implements IParseFunction {
26 25  
27 26 @Override
28 27 public String functionName() {
29   - return NAME;
  28 + return GET_CUSTOMER_INDUSTRY;
30 29 }
31 30  
32 31 @Override
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmLevelParseFunction.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmCustomerLevelParseFunction.java
... ... @@ -7,6 +7,7 @@ import lombok.extern.slf4j.Slf4j;
7 7 import org.springframework.stereotype.Component;
8 8  
9 9 import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_LEVEL;
  10 +import static cn.iocoder.yudao.module.crm.enums.operatelog.CrmParseFunctionNameConstants.GET_CUSTOMER_LEVEL;
10 11  
11 12 /**
12 13 * 客户等级的 {@link IParseFunction} 实现类
... ... @@ -15,9 +16,7 @@ import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_L
15 16 */
16 17 @Component
17 18 @Slf4j
18   -public class CrmLevelParseFunction implements IParseFunction {
19   -
20   - public static final String NAME = "getLevel";
  19 +public class CrmCustomerLevelParseFunction implements IParseFunction {
21 20  
22 21 @Override
23 22 public boolean executeBefore() {
... ... @@ -26,7 +25,7 @@ public class CrmLevelParseFunction implements IParseFunction {
26 25  
27 26 @Override
28 27 public String functionName() {
29   - return NAME;
  28 + return GET_CUSTOMER_LEVEL;
30 29 }
31 30  
32 31 @Override
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmCustomerParseFunction.java
... ... @@ -8,8 +8,10 @@ import jakarta.annotation.Resource;
8 8 import lombok.extern.slf4j.Slf4j;
9 9 import org.springframework.stereotype.Component;
10 10  
  11 +import static cn.iocoder.yudao.module.crm.enums.operatelog.CrmParseFunctionNameConstants.GET_CUSTOMER_BY_ID;
  12 +
11 13 /**
12   - * 行业的 {@link IParseFunction} 实现类
  14 + * CRM 客户的 {@link IParseFunction} 实现类
13 15 *
14 16 * @author HUIHUI
15 17 */
... ... @@ -17,8 +19,6 @@ import org.springframework.stereotype.Component;
17 19 @Slf4j
18 20 public class CrmCustomerParseFunction implements IParseFunction {
19 21  
20   - public static final String NAME = "getCustomerById";
21   -
22 22 @Resource
23 23 private CrmCustomerService customerService;
24 24  
... ... @@ -29,7 +29,7 @@ public class CrmCustomerParseFunction implements IParseFunction {
29 29  
30 30 @Override
31 31 public String functionName() {
32   - return NAME;
  32 + return GET_CUSTOMER_BY_ID;
33 33 }
34 34  
35 35 @Override
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmSourceParseFunction.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmCustomerSourceParseFunction.java
... ... @@ -7,17 +7,18 @@ import lombok.extern.slf4j.Slf4j;
7 7 import org.springframework.stereotype.Component;
8 8  
9 9 import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_SOURCE;
  10 +import static cn.iocoder.yudao.module.crm.enums.operatelog.CrmParseFunctionNameConstants.GET_CUSTOMER_SOURCE;
10 11  
11 12 /**
12   - * 客户来源的 {@link IParseFunction} 实现类
  13 + * CRM 客户来源的 {@link IParseFunction} 实现类
13 14 *
14 15 * @author HUIHUI
15 16 */
16 17 @Component
17 18 @Slf4j
18   -public class CrmSourceParseFunction implements IParseFunction {
  19 +public class CrmCustomerSourceParseFunction implements IParseFunction {
19 20  
20   - public static final String NAME = "getSource";
  21 + public static final String NAME = "getCustomerSource";
21 22  
22 23 @Override
23 24 public boolean executeBefore() {
... ... @@ -26,7 +27,7 @@ public class CrmSourceParseFunction implements IParseFunction {
26 27  
27 28 @Override
28 29 public String functionName() {
29   - return NAME;
  30 + return GET_CUSTOMER_SOURCE;
30 31 }
31 32  
32 33 @Override
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessService.java
... ... @@ -98,4 +98,12 @@ public interface CrmBusinessService {
98 98 */
99 99 void transferBusiness(CrmBusinessTransferReqVO reqVO, Long userId);
100 100  
  101 + /**
  102 + * 获取关联客户的商机数量
  103 + *
  104 + * @param customerId 客户编号
  105 + * @return 数量
  106 + */
  107 + Long getBusinessCountByCustomerId(Long customerId);
  108 +
101 109 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java
... ... @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.crm.service.business;
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.collection.ListUtil;
5 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 7 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessCreateReqVO;
7 8 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO;
8 9 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO;
... ... @@ -17,6 +18,9 @@ import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPerm
17 18 import cn.iocoder.yudao.module.crm.service.contact.CrmContactBusinessService;
18 19 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
19 20 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
  21 +import com.mzt.logapi.context.LogRecordContext;
  22 +import com.mzt.logapi.service.impl.DiffParseFunction;
  23 +import com.mzt.logapi.starter.annotation.LogRecord;
20 24 import jakarta.annotation.Resource;
21 25 import org.springframework.stereotype.Service;
22 26 import org.springframework.transaction.annotation.Transactional;
... ... @@ -28,6 +32,7 @@ import java.util.List;
28 32 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
29 33 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
30 34 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_NOT_EXISTS;
  35 +import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
31 36  
32 37 /**
33 38 * 商机 Service 实现类
... ... @@ -48,7 +53,8 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
48 53  
49 54 @Override
50 55 @Transactional(rollbackFor = Exception.class)
51   - // TODO @商机待定:操作日志;
  56 + @LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_CREATE_SUB_TYPE, bizNo = "{{#business.id}}",
  57 + success = CRM_BUSINESS_CREATE_SUCCESS)
52 58 public Long createBusiness(CrmBusinessCreateReqVO createReqVO, Long userId) {
53 59 // 1. 插入商机
54 60 CrmBusinessDO business = CrmBusinessConvert.INSTANCE.convert(createReqVO);
... ... @@ -60,17 +66,20 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
60 66 // 2. 创建数据权限
61 67 permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_BUSINESS.getType())
62 68 .setBizId(business.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人
  69 +
  70 + // 4. 记录操作日志上下文
  71 + LogRecordContext.putVariable("business", business);
63 72 return business.getId();
64 73 }
65 74  
66 75 @Override
67 76 @Transactional(rollbackFor = Exception.class)
68   - @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#updateReqVO.id",
69   - level = CrmPermissionLevelEnum.WRITE)
70   - // TODO @商机待定:操作日志;
  77 + @LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
  78 + success = CRM_BUSINESS_UPDATE_SUCCESS)
  79 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
71 80 public void updateBusiness(CrmBusinessUpdateReqVO updateReqVO) {
72 81 // 1. 校验存在
73   - validateBusinessExists(updateReqVO.getId());
  82 + CrmBusinessDO oldBusiness = validateBusinessExists(updateReqVO.getId());
74 83  
75 84 // 2. 更新商机
76 85 CrmBusinessDO updateObj = CrmBusinessConvert.INSTANCE.convert(updateReqVO);
... ... @@ -78,20 +87,28 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
78 87 // TODO 商机待定:插入商机与产品的关联表;校验商品存在
79 88  
80 89 // TODO @商机待定:如果状态发生变化,插入商机状态变更记录表
  90 + // 3. 记录操作日志上下文
  91 + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldBusiness, CrmBusinessUpdateReqVO.class));
  92 + LogRecordContext.putVariable("businessName", oldBusiness.getName());
81 93 }
82 94  
83 95 @Override
84 96 @Transactional(rollbackFor = Exception.class)
  97 + @LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_DELETE_SUB_TYPE, bizNo = "{{#id}}",
  98 + success = CRM_BUSINESS_DELETE_SUCCESS)
85 99 @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
86 100 public void deleteBusiness(Long id) {
87 101 // 校验存在
88   - validateBusinessExists(id);
  102 + CrmBusinessDO business = validateBusinessExists(id);
89 103 // TODO @商机待定:需要校验有没关联合同。CrmContractDO 的 businessId 字段
90 104  
91 105 // 删除
92 106 businessMapper.deleteById(id);
93 107 // 删除数据权限
94 108 permissionService.deletePermission(CrmBizTypeEnum.CRM_BUSINESS.getType(), id);
  109 +
  110 + // 记录操作日志上下文
  111 + LogRecordContext.putVariable("businessName", business.getName());
95 112 }
96 113  
97 114 private CrmBusinessDO validateBusinessExists(Long id) {
... ... @@ -102,6 +119,28 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
102 119 return crmBusiness;
103 120 }
104 121  
  122 +
  123 + @Override
  124 + @Transactional(rollbackFor = Exception.class)
  125 + @LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.id}}",
  126 + success = CRM_BUSINESS_TRANSFER_SUCCESS)
  127 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER)
  128 + public void transferBusiness(CrmBusinessTransferReqVO reqVO, Long userId) {
  129 + // 1 校验商机是否存在
  130 + CrmBusinessDO business = validateBusinessExists(reqVO.getId());
  131 +
  132 + // 2.1 数据权限转移
  133 + permissionService.transferPermission(
  134 + CrmBusinessConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_BUSINESS.getType()));
  135 + // 2.2 设置新的负责人
  136 + businessMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
  137 +
  138 + // 记录操作日志上下文
  139 + LogRecordContext.putVariable("business", business);
  140 + }
  141 +
  142 + //======================= 查询相关 =======================
  143 +
105 144 @Override
106 145 @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#id", level = CrmPermissionLevelEnum.READ)
107 146 public CrmBusinessDO getBusiness(Long id) {
... ... @@ -142,17 +181,8 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
142 181 }
143 182  
144 183 @Override
145   - @Transactional(rollbackFor = Exception.class)
146   - // TODO @puhui999:操作日志
147   - public void transferBusiness(CrmBusinessTransferReqVO reqVO, Long userId) {
148   - // 1 校验商机是否存在
149   - validateBusinessExists(reqVO.getId());
150   -
151   - // 2.1 数据权限转移
152   - permissionService.transferPermission(
153   - CrmBusinessConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_BUSINESS.getType()));
154   - // 2.2 设置新的负责人
155   - businessMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
  184 + public Long getBusinessCountByCustomerId(Long customerId) {
  185 + return businessMapper.selectCount(CrmBusinessDO::getCustomerId, customerId);
156 186 }
157 187  
158 188 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java
... ... @@ -3,12 +3,13 @@ package cn.iocoder.yudao.module.crm.service.clue;
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.collection.ListUtil;
5 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 7 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO;
7 8 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueSaveReqVO;
8 9 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransferReqVO;
9 10 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransformReqVO;
  11 +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerSaveReqVO;
10 12 import cn.iocoder.yudao.module.crm.convert.clue.CrmClueConvert;
11   -import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert;
12 13 import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;
13 14 import cn.iocoder.yudao.module.crm.dal.mysql.clue.CrmClueMapper;
14 15 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
... ... @@ -139,8 +140,9 @@ public class CrmClueServiceImpl implements CrmClueService {
139 140  
140 141 // 遍历线索,创建对应的客户
141 142 clues.forEach(clue -> {
  143 + clue.setId(null);
142 144 // 创建客户
143   - customerService.createCustomer(CrmCustomerConvert.INSTANCE.convert(clue), userId);
  145 + customerService.createCustomer(BeanUtils.toBean(clue, CrmCustomerSaveReqVO.class), userId);
144 146 // 更新线索状态
145 147 // TODO @min:新建一个 CrmClueDO 去更新。尽量规避直接用原本的对象去更新。因为这样万一并发更新,会存在覆盖的问题。
146 148 // TODO @puhui999:如果有跟进记录,需要一起转过去;
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactService.java
1 1 package cn.iocoder.yudao.module.crm.service.contact;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4   -import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.*;
  4 +import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactPageReqVO;
  5 +import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactSaveReqVO;
  6 +import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactTransferReqVO;
5 7 import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
6 8 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
7 9 import jakarta.validation.Valid;
... ... @@ -40,6 +42,22 @@ public interface CrmContactService {
40 42 void deleteContact(Long id);
41 43  
42 44 /**
  45 + * 联系人转移
  46 + *
  47 + * @param reqVO 请求
  48 + * @param userId 用户编号
  49 + */
  50 + void transferContact(CrmContactTransferReqVO reqVO, Long userId);
  51 +
  52 + /**
  53 + * 更新客户联系人负责人
  54 + *
  55 + * @param customerId 客户编号
  56 + * @param ownerUserId 用户编号
  57 + */
  58 + void updateOwnerUserIdByCustomerId(Long customerId, Long ownerUserId);
  59 +
  60 + /**
43 61 * 获得联系人
44 62 *
45 63 * @param id 编号
... ... @@ -85,11 +103,11 @@ public interface CrmContactService {
85 103 PageResult<CrmContactDO> getContactPageByCustomerId(CrmContactPageReqVO pageVO);
86 104  
87 105 /**
88   - * 联系人转移
  106 + * 获取关联客户的联系人数量
89 107 *
90   - * @param reqVO 请求
91   - * @param userId 用户编号
  108 + * @param customerId 客户编号
  109 + * @return 数量
92 110 */
93   - void transferContact(CrmContactTransferReqVO reqVO, Long userId);
  111 + Long getContactCountByCustomerId(Long customerId);
94 112  
95 113 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java
... ... @@ -33,7 +33,7 @@ import java.util.List;
33 33  
34 34 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
35 35 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
36   -import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.CRM_CONTACT_TYPE;
  36 +import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
37 37 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_NOT_EXISTS;
38 38 import static java.util.Collections.singletonList;
39 39  
... ... @@ -65,7 +65,8 @@ public class CrmContactServiceImpl implements CrmContactService {
65 65  
66 66 @Override
67 67 @Transactional(rollbackFor = Exception.class)
68   - @LogRecord(type = CRM_CONTACT_TYPE, subType = "创建联系人", bizNo = "{{#contactId}}", success = "创建了联系人[{{#contactName}}]")
  68 + @LogRecord(type = CRM_CONTACT_TYPE, subType = CRM_CONTACT_CREATE_SUB_TYPE, bizNo = "{{#contact.id}}",
  69 + success = CRM_CONTACT_CREATE_SUCCESS)
69 70 public Long createContact(CrmContactSaveReqVO createReqVO, Long userId) {
70 71 // 1. 校验
71 72 validateRelationDataExists(createReqVO);
... ... @@ -86,18 +87,18 @@ public class CrmContactServiceImpl implements CrmContactService {
86 87 }
87 88  
88 89 // 5. 记录操作日志
89   - LogRecordContext.putVariable("contactId", contact.getId());
90   - LogRecordContext.putVariable("contactName", contact.getName());
  90 + LogRecordContext.putVariable("contact", contact);
91 91 return contact.getId();
92 92 }
93 93  
94 94 @Override
95 95 @Transactional(rollbackFor = Exception.class)
96   - @LogRecord(type = CRM_CONTACT_TYPE, subType = "更新联系人", bizNo = "{{#updateReqVO.id}}", success = "更新了联系人{_DIFF{#updateReqVO}}")
  96 + @LogRecord(type = CRM_CONTACT_TYPE, subType = CRM_CONTACT_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
  97 + success = CRM_CONTACT_UPDATE_SUCCESS)
97 98 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
98 99 public void updateContact(CrmContactSaveReqVO updateReqVO) {
99 100 // 1. 校验存在
100   - CrmContactDO contactDO = validateContactExists(updateReqVO.getId());
  101 + CrmContactDO oldContact = validateContactExists(updateReqVO.getId());
101 102 validateRelationDataExists(updateReqVO);
102 103  
103 104 // 2. 更新联系人
... ... @@ -105,7 +106,8 @@ public class CrmContactServiceImpl implements CrmContactService {
105 106 contactMapper.updateById(updateObj);
106 107  
107 108 // 3. 记录操作日志
108   - LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(contactDO, CrmContactSaveReqVO.class));
  109 + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldContact, CrmContactSaveReqVO.class));
  110 + LogRecordContext.putVariable("contactName", oldContact.getName());
109 111 }
110 112  
111 113 /**
... ... @@ -133,11 +135,13 @@ public class CrmContactServiceImpl implements CrmContactService {
133 135 }
134 136  
135 137 @Override
136   - @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
137 138 @Transactional(rollbackFor = Exception.class)
  139 + @LogRecord(type = CRM_CONTACT_TYPE, subType = CRM_CONTACT_DELETE_SUB_TYPE, bizNo = "{{#id}}",
  140 + success = CRM_CONTACT_DELETE_SUCCESS)
  141 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
138 142 public void deleteContact(Long id) {
139 143 // 1.1 校验存在
140   - validateContactExists(id);
  144 + CrmContactDO contact = validateContactExists(id);
141 145 // 1.2 校验是否关联合同
142 146 if (contractService.getContractCountByContactId(id) > 0) {
143 147 throw exception(CONTACT_DELETE_FAIL_CONTRACT_LINK_EXISTS);
... ... @@ -151,6 +155,9 @@ public class CrmContactServiceImpl implements CrmContactService {
151 155 // 4.2 删除商机关联
152 156 contactBusinessService.deleteContactBusinessByContactId(id);
153 157 // TODO @puhui999:删除跟进记录
  158 +
  159 + // 记录操作日志上下文
  160 + LogRecordContext.putVariable("contactName", contact.getName());
154 161 }
155 162  
156 163 private CrmContactDO validateContactExists(Long id) {
... ... @@ -162,6 +169,32 @@ public class CrmContactServiceImpl implements CrmContactService {
162 169 }
163 170  
164 171 @Override
  172 + @Transactional(rollbackFor = Exception.class)
  173 + @LogRecord(type = CRM_CONTACT_TYPE, subType = CRM_CONTACT_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.id}}",
  174 + success = CRM_CONTACT_TRANSFER_SUCCESS)
  175 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER)
  176 + public void transferContact(CrmContactTransferReqVO reqVO, Long userId) {
  177 + // 1 校验联系人是否存在
  178 + CrmContactDO contact = validateContactExists(reqVO.getId());
  179 +
  180 + // 2.1 数据权限转移
  181 + permissionService.transferPermission(
  182 + CrmContactConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CONTACT.getType()));
  183 + // 2.2 设置新的负责人
  184 + contactMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
  185 +
  186 + // 3. 记录转移日志
  187 + LogRecordContext.putVariable("contact", contact);
  188 + }
  189 +
  190 + @Override
  191 + public void updateOwnerUserIdByCustomerId(Long customerId, Long ownerUserId) {
  192 + contactMapper.updateOwnerUserIdByCustomerId(customerId, ownerUserId);
  193 + }
  194 +
  195 + //======================= 查询相关 =======================
  196 +
  197 + @Override
165 198 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#id", level = CrmPermissionLevelEnum.READ)
166 199 public CrmContactDO getContact(Long id) {
167 200 return contactMapper.selectById(id);
... ... @@ -192,19 +225,8 @@ public class CrmContactServiceImpl implements CrmContactService {
192 225 }
193 226  
194 227 @Override
195   - // TODO @puhui999:权限校验
196   - // TODO @puhui999:记录操作日志;将联系人【名字】转移给【新负责人】
197   - public void transferContact(CrmContactTransferReqVO reqVO, Long userId) {
198   - // 1 校验联系人是否存在
199   - validateContactExists(reqVO.getId());
200   -
201   - // 2.1 数据权限转移
202   - permissionService.transferPermission(
203   - CrmContactConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CONTACT.getType()));
204   - // 2.2 设置新的负责人
205   - contactMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
206   -
207   - // 3. TODO 记录转移日志
  228 + public Long getContactCountByCustomerId(Long customerId) {
  229 + return contactMapper.selectCount(CrmContactDO::getCustomerId, customerId);
208 230 }
209 231  
210 232 }
211 233 \ No newline at end of file
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java
... ... @@ -95,4 +95,12 @@ public interface CrmContractService {
95 95 */
96 96 Long getContractCountByContactId(Long contactId);
97 97  
  98 + /**
  99 + * 获取关联客户的合同数量
  100 + *
  101 + * @param customerId 客户编号
  102 + * @return 数量
  103 + */
  104 + Long getContractCountByCustomerId(Long customerId);
  105 +
98 106 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
... ... @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.crm.service.contract;
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.collection.ListUtil;
5 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 7 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractCreateReqVO;
7 8 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO;
8 9 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO;
... ... @@ -15,6 +16,9 @@ import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
15 16 import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
16 17 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
17 18 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
  19 +import com.mzt.logapi.context.LogRecordContext;
  20 +import com.mzt.logapi.service.impl.DiffParseFunction;
  21 +import com.mzt.logapi.starter.annotation.LogRecord;
18 22 import jakarta.annotation.Resource;
19 23 import org.springframework.stereotype.Service;
20 24 import org.springframework.transaction.annotation.Transactional;
... ... @@ -25,6 +29,7 @@ import java.util.List;
25 29  
26 30 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
27 31 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CONTRACT_NOT_EXISTS;
  32 +import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
28 33  
29 34 /**
30 35 * CRM 合同 Service 实现类
... ... @@ -42,7 +47,9 @@ public class CrmContractServiceImpl implements CrmContractService {
42 47 private CrmPermissionService crmPermissionService;
43 48  
44 49 @Override
45   - // TODO @puhui999:添加操作日志
  50 + @Transactional(rollbackFor = Exception.class)
  51 + @LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_CREATE_SUB_TYPE, bizNo = "{{#contract.id}}",
  52 + success = CRM_CONTRACT_CREATE_SUCCESS)
46 53 public Long createContract(CrmContractCreateReqVO createReqVO, Long userId) {
47 54 // TODO @合同待定:插入合同商品;需要搞个 BusinessProductDO
48 55 // 插入合同
... ... @@ -53,38 +60,52 @@ public class CrmContractServiceImpl implements CrmContractService {
53 60 crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(userId)
54 61 .setBizType(CrmBizTypeEnum.CRM_CONTRACT.getType()).setBizId(contract.getId())
55 62 .setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
  63 +
  64 + // 4. 记录操作日志上下文
  65 + LogRecordContext.putVariable("contract", contract);
56 66 return contract.getId();
57 67 }
58 68  
59 69 @Override
60 70 @Transactional(rollbackFor = Exception.class)
  71 + @LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
  72 + success = CRM_CONTRACT_UPDATE_SUCCESS)
61 73 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
62   - // TODO @puhui999:添加操作日志
63 74 public void updateContract(CrmContractUpdateReqVO updateReqVO) {
64 75 // TODO @合同待定:只有草稿、审批中,可以编辑;
65 76 // 校验存在
66   - validateContractExists(updateReqVO.getId());
  77 + CrmContractDO oldContract = validateContractExists(updateReqVO.getId());
67 78 // 更新合同
68 79 CrmContractDO updateObj = CrmContractConvert.INSTANCE.convert(updateReqVO);
69 80 contractMapper.updateById(updateObj);
70 81 // TODO @合同待定:插入合同商品;需要搞个 BusinessProductDO
  82 +
  83 + // 3. 记录操作日志上下文
  84 + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldContract, CrmContractUpdateReqVO.class));
  85 + LogRecordContext.putVariable("contractName", oldContract.getName());
71 86 }
72 87  
73 88 // TODO @合同待定:缺一个取消合同的接口;只有草稿、审批中可以取消;CrmAuditStatusEnum
74 89  
75 90 // TODO @合同待定:缺一个发起审批的接口;只有草稿可以发起审批;CrmAuditStatusEnum
76 91  
  92 +
77 93 @Override
78 94 @Transactional(rollbackFor = Exception.class)
  95 + @LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_DELETE_SUB_TYPE, bizNo = "{{#id}}",
  96 + success = CRM_CONTRACT_DELETE_SUCCESS)
79 97 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
80 98 public void deleteContract(Long id) {
81 99 // TODO @合同待定:如果被 CrmReceivableDO 所使用,则不允许删除
82 100 // 校验存在
83   - validateContractExists(id);
  101 + CrmContractDO contract = validateContractExists(id);
84 102 // 删除
85 103 contractMapper.deleteById(id);
86 104 // 删除数据权限
87 105 crmPermissionService.deletePermission(CrmBizTypeEnum.CRM_CONTRACT.getType(), id);
  106 +
  107 + // 记录操作日志上下文
  108 + LogRecordContext.putVariable("contractName", contract.getName());
88 109 }
89 110  
90 111 private CrmContractDO validateContractExists(Long id) {
... ... @@ -96,6 +117,27 @@ public class CrmContractServiceImpl implements CrmContractService {
96 117 }
97 118  
98 119 @Override
  120 + @Transactional(rollbackFor = Exception.class)
  121 + @LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.id}}",
  122 + success = CRM_CONTRACT_TRANSFER_SUCCESS)
  123 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER)
  124 + public void transferContract(CrmContractTransferReqVO reqVO, Long userId) {
  125 + // 1. 校验合同是否存在
  126 + CrmContractDO contract = validateContractExists(reqVO.getId());
  127 +
  128 + // 2.1 数据权限转移
  129 + crmPermissionService.transferPermission(
  130 + CrmContractConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CONTRACT.getType()));
  131 + // 2.2 设置负责人
  132 + contractMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
  133 +
  134 + // 3. 记录转移日志
  135 + LogRecordContext.putVariable("contract", contract);
  136 + }
  137 +
  138 + //======================= 查询相关 =======================
  139 +
  140 + @Override
99 141 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#id", level = CrmPermissionLevelEnum.READ)
100 142 public CrmContractDO getContract(Long id) {
101 143 return contractMapper.selectById(id);
... ... @@ -121,23 +163,13 @@ public class CrmContractServiceImpl implements CrmContractService {
121 163 }
122 164  
123 165 @Override
124   - @Transactional(rollbackFor = Exception.class)
125   - // 3. TODO @puhui999:记录转移日志
126   - // TODO @puhui999:权限校验,这里要搞哇?
127   - public void transferContract(CrmContractTransferReqVO reqVO, Long userId) {
128   - // 1. 校验合同是否存在
129   - validateContractExists(reqVO.getId());
130   -
131   - // 2.1 数据权限转移
132   - crmPermissionService.transferPermission(
133   - CrmContractConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CONTRACT.getType()));
134   - // 2.2 设置负责人
135   - contractMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
  166 + public Long getContractCountByContactId(Long contactId) {
  167 + return contractMapper.selectCountByContactId(contactId);
136 168 }
137 169  
138 170 @Override
139   - public Long getContractCountByContactId(Long contactId) {
140   - return contractMapper.selectCountByContactId(contactId);
  171 + public Long getContractCountByCustomerId(Long customerId) {
  172 + return contractMapper.selectCount(CrmContractDO::getCustomerId, customerId);
141 173 }
142 174  
143 175 // TODO @合同待定:需要新增一个 ContractConfigDO 表,合同配置,重点是到期提醒;
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerLimitConfigServiceImpl.java
... ... @@ -5,7 +5,6 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 6 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigPageReqVO;
7 7 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigSaveReqVO;
8   -import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerLimitConfigConvert;
9 8 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO;
10 9 import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerLimitConfigMapper;
11 10 import cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum;
... ... @@ -49,7 +48,7 @@ public class CrmCustomerLimitConfigServiceImpl implements CrmCustomerLimitConfig
49 48 public Long createCustomerLimitConfig(CrmCustomerLimitConfigSaveReqVO createReqVO) {
50 49 validateUserAndDept(createReqVO.getUserIds(), createReqVO.getDeptIds());
51 50 // 插入
52   - CrmCustomerLimitConfigDO customerLimitConfig = CrmCustomerLimitConfigConvert.INSTANCE.convert(createReqVO);
  51 + CrmCustomerLimitConfigDO customerLimitConfig = BeanUtils.toBean(createReqVO, CrmCustomerLimitConfigDO.class);
53 52 customerLimitConfigMapper.insert(customerLimitConfig);
54 53  
55 54 // 记录操作日志上下文
... ... @@ -66,7 +65,7 @@ public class CrmCustomerLimitConfigServiceImpl implements CrmCustomerLimitConfig
66 65 CrmCustomerLimitConfigDO oldLimitConfig = validateCustomerLimitConfigExists(updateReqVO.getId());
67 66 validateUserAndDept(updateReqVO.getUserIds(), updateReqVO.getDeptIds());
68 67 // 更新
69   - CrmCustomerLimitConfigDO updateObj = CrmCustomerLimitConfigConvert.INSTANCE.convert(updateReqVO);
  68 + CrmCustomerLimitConfigDO updateObj = BeanUtils.toBean(updateReqVO, CrmCustomerLimitConfigDO.class);
70 69 customerLimitConfigMapper.updateById(updateObj);
71 70  
72 71 // 记录操作日志上下文
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerPoolConfigServiceImpl.java
1 1 package cn.iocoder.yudao.module.crm.service.customer;
2 2  
3   -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  3 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
4 4 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.poolconfig.CrmCustomerPoolConfigSaveReqVO;
5   -import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert;
6 5 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;
7 6 import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerPoolConfigMapper;
8 7 import com.mzt.logapi.context.LogRecordContext;
... ... @@ -34,8 +33,7 @@ public class CrmCustomerPoolConfigServiceImpl implements CrmCustomerPoolConfigSe
34 33 */
35 34 @Override
36 35 public CrmCustomerPoolConfigDO getCustomerPoolConfig() {
37   - // TODO @puhui999:这个要搞到 mapper 里噢。
38   - return customerPoolConfigMapper.selectOne(new LambdaQueryWrapperX<CrmCustomerPoolConfigDO>().last("LIMIT 1"));
  36 + return customerPoolConfigMapper.selectOne();
39 37 }
40 38  
41 39 /**
... ... @@ -49,7 +47,7 @@ public class CrmCustomerPoolConfigServiceImpl implements CrmCustomerPoolConfigSe
49 47 public void saveCustomerPoolConfig(CrmCustomerPoolConfigSaveReqVO saveReqVO) {
50 48 // 存在,则进行更新
51 49 CrmCustomerPoolConfigDO dbConfig = getCustomerPoolConfig();
52   - CrmCustomerPoolConfigDO poolConfig = CrmCustomerConvert.INSTANCE.convert(saveReqVO);
  50 + CrmCustomerPoolConfigDO poolConfig = BeanUtils.toBean(saveReqVO, CrmCustomerPoolConfigDO.class);
53 51 if (Objects.nonNull(dbConfig)) {
54 52 customerPoolConfigMapper.updateById(poolConfig.setId(dbConfig.getId()));
55 53 // 记录操作日志上下文
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java
... ... @@ -18,6 +18,9 @@ import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
18 18 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
19 19 import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
20 20 import cn.iocoder.yudao.module.crm.framework.permission.core.util.CrmPermissionUtils;
  21 +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
  22 +import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
  23 +import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
21 24 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
22 25 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
23 26 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
... ... @@ -26,6 +29,7 @@ import com.mzt.logapi.context.LogRecordContext;
26 29 import com.mzt.logapi.service.impl.DiffParseFunction;
27 30 import com.mzt.logapi.starter.annotation.LogRecord;
28 31 import jakarta.annotation.Resource;
  32 +import org.springframework.context.annotation.Lazy;
29 33 import org.springframework.stereotype.Service;
30 34 import org.springframework.transaction.annotation.Transactional;
31 35 import org.springframework.validation.annotation.Validated;
... ... @@ -59,6 +63,15 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
59 63 private CrmPermissionService permissionService;
60 64 @Resource
61 65 private CrmCustomerLimitConfigService customerLimitConfigService;
  66 + @Resource
  67 + @Lazy
  68 + private CrmContactService contactService;
  69 + @Resource
  70 + @Lazy
  71 + private CrmBusinessService businessService;
  72 + @Resource
  73 + @Lazy
  74 + private CrmContractService contractService;
62 75  
63 76 @Resource
64 77 private AdminUserApi adminUserApi;
... ... @@ -73,7 +86,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
73 86 validateCustomerExceedOwnerLimit(createReqVO.getOwnerUserId(), 1);
74 87  
75 88 // 2. 插入客户
76   - CrmCustomerDO customer = CrmCustomerConvert.INSTANCE.convert(createReqVO)
  89 + CrmCustomerDO customer = BeanUtils.toBean(createReqVO, CrmCustomerDO.class)
77 90 .setLockStatus(false).setDealStatus(false)
78 91 .setContactLastTime(LocalDateTime.now());
79 92 // TODO @puhui999:可能要加个 receiveTime 字段,记录最后接收时间
... ... @@ -100,7 +113,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
100 113 CrmCustomerDO oldCustomer = validateCustomerExists(updateReqVO.getId());
101 114  
102 115 // 2. 更新客户
103   - CrmCustomerDO updateObj = CrmCustomerConvert.INSTANCE.convert(updateReqVO);
  116 + CrmCustomerDO updateObj = BeanUtils.toBean(updateReqVO, CrmCustomerDO.class);
104 117 customerMapper.updateById(updateObj);
105 118  
106 119 // 3. 记录操作日志上下文
... ... @@ -116,8 +129,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
116 129 public void deleteCustomer(Long id) {
117 130 // 校验存在
118 131 CrmCustomerDO customer = validateCustomerExists(id);
119   - // TODO @puhui999:如果有联系人、商机,则不允许删除;
120   -
  132 + // 检查引用
  133 + checkCustomerReference(id);
121 134 // 删除
122 135 customerMapper.deleteById(id);
123 136 // 删除数据权限
... ... @@ -128,6 +141,23 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
128 141 LogRecordContext.putVariable("customerName", customer.getName());
129 142 }
130 143  
  144 + /**
  145 + * 校验客户是否被引用
  146 + *
  147 + * @param id 客户编号
  148 + */
  149 + private void checkCustomerReference(Long id) {
  150 + if (contactService.getContactCountByCustomerId(id) > 0) {
  151 + throw exception(CUSTOMER_DELETE_FAIL_HAVE_REFERENCE, CrmBizTypeEnum.CRM_CONTACT.getName());
  152 + }
  153 + if (businessService.getBusinessCountByCustomerId(id) > 0) {
  154 + throw exception(CUSTOMER_DELETE_FAIL_HAVE_REFERENCE, CrmBizTypeEnum.CRM_BUSINESS.getName());
  155 + }
  156 + if (contractService.getContractCountByCustomerId(id) > 0) {
  157 + throw exception(CUSTOMER_DELETE_FAIL_HAVE_REFERENCE, CrmBizTypeEnum.CRM_CONTRACT.getName());
  158 + }
  159 + }
  160 +
131 161 @Override
132 162 @Transactional(rollbackFor = Exception.class)
133 163 @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.id}}",
... ... @@ -145,10 +175,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
145 175 // 2.2 转移后重新设置负责人
146 176 customerMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
147 177  
148   - // 3. TODO 记录转移日志
149   - // 记录操作日志上下文
150   - // TODO @puhui999:crmCustomer=》customer,也看看其他有没类似的情况哈
151   - LogRecordContext.putVariable("crmCustomer", customer);
  178 + // 3. 记录转移日志
  179 + LogRecordContext.putVariable("customer", customer);
152 180 }
153 181  
154 182 @Override
... ... @@ -172,7 +200,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
172 200  
173 201 // 3. 记录操作日志上下文
174 202 // tips: 因为这里使用的是老的状态所以记录时反着记录,也就是 lockStatus 为 true 那么就是解锁反之为锁定
175   - LogRecordContext.putVariable("crmCustomer", customer);
  203 + LogRecordContext.putVariable("customer", customer);
176 204 }
177 205  
178 206 // ==================== 公海相关操作 ====================
... ... @@ -202,6 +230,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
202 230 permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), customer.getId(),
203 231 CrmPermissionLevelEnum.OWNER.getLevel());
204 232 // TODO @puhui999:联系人的负责人,也要设置为 null;这块和领取是对应的;因为领取后,负责人也要关联过来;
  233 + // 提问:那是不是可以这样理解客户所有联系人的负责人默认为客户的负责人,然后添加客户团队成员时才存在“同时分配给”的操作?
  234 + contactService.updateOwnerUserIdByCustomerId(customer.getId(), null);
205 235  
206 236 // 记录操作日志上下文
207 237 LogRecordContext.putVariable("customerName", customer.getName());
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.service.followup;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordPageReqVO;
  5 +import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordSaveReqVO;
  6 +import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;
  7 +import jakarta.validation.Valid;
  8 +
  9 +/**
  10 + * 跟进记录 Service 接口
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +public interface CrmFollowUpRecordService {
  15 +
  16 + /**
  17 + * 创建跟进记录
  18 + *
  19 + * @param createReqVO 创建信息
  20 + * @return 编号
  21 + */
  22 + Long createFollowUpRecord(@Valid CrmFollowUpRecordSaveReqVO createReqVO);
  23 +
  24 + /**
  25 + * 更新跟进记录
  26 + *
  27 + * @param updateReqVO 更新信息
  28 + */
  29 + void updateFollowUpRecord(@Valid CrmFollowUpRecordSaveReqVO updateReqVO);
  30 +
  31 + /**
  32 + * 删除跟进记录
  33 + *
  34 + * @param id 编号
  35 + */
  36 + void deleteFollowUpRecord(Long id);
  37 +
  38 + /**
  39 + * 获得跟进记录
  40 + *
  41 + * @param id 编号
  42 + * @return 跟进记录
  43 + */
  44 + CrmFollowUpRecordDO getFollowUpRecord(Long id);
  45 +
  46 + /**
  47 + * 获得跟进记录分页
  48 + *
  49 + * @param pageReqVO 分页查询
  50 + * @return 跟进记录分页
  51 + */
  52 + PageResult<CrmFollowUpRecordDO> getFollowUpRecordPage(CrmFollowUpRecordPageReqVO pageReqVO);
  53 +
  54 +}
0 55 \ No newline at end of file
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.service.followup;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordPageReqVO;
  6 +import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordSaveReqVO;
  7 +import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;
  8 +import cn.iocoder.yudao.module.crm.dal.mysql.followup.CrmFollowUpRecordMapper;
  9 +import jakarta.annotation.Resource;
  10 +import org.springframework.stereotype.Service;
  11 +import org.springframework.validation.annotation.Validated;
  12 +
  13 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  14 +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.FOLLOW_UP_RECORD_NOT_EXISTS;
  15 +
  16 +/**
  17 + * 跟进记录 Service 实现类
  18 + *
  19 + * @author 芋道源码
  20 + */
  21 +@Service
  22 +@Validated
  23 +public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService {
  24 +
  25 + @Resource
  26 + private CrmFollowUpRecordMapper crmFollowUpRecordMapper;
  27 +
  28 + @Override
  29 + public Long createFollowUpRecord(CrmFollowUpRecordSaveReqVO createReqVO) {
  30 + // 插入
  31 + CrmFollowUpRecordDO followUpRecord = BeanUtils.toBean(createReqVO, CrmFollowUpRecordDO.class);
  32 + crmFollowUpRecordMapper.insert(followUpRecord);
  33 + // 返回
  34 + return followUpRecord.getId();
  35 + }
  36 +
  37 + @Override
  38 + public void updateFollowUpRecord(CrmFollowUpRecordSaveReqVO updateReqVO) {
  39 + // 校验存在
  40 + validateFollowUpRecordExists(updateReqVO.getId());
  41 + // 更新
  42 + CrmFollowUpRecordDO updateObj = BeanUtils.toBean(updateReqVO, CrmFollowUpRecordDO.class);
  43 + crmFollowUpRecordMapper.updateById(updateObj);
  44 + }
  45 +
  46 + @Override
  47 + public void deleteFollowUpRecord(Long id) {
  48 + // 校验存在
  49 + validateFollowUpRecordExists(id);
  50 + // 删除
  51 + crmFollowUpRecordMapper.deleteById(id);
  52 + }
  53 +
  54 + private void validateFollowUpRecordExists(Long id) {
  55 + if (crmFollowUpRecordMapper.selectById(id) == null) {
  56 + throw exception(FOLLOW_UP_RECORD_NOT_EXISTS);
  57 + }
  58 + }
  59 +
  60 + @Override
  61 + public CrmFollowUpRecordDO getFollowUpRecord(Long id) {
  62 + return crmFollowUpRecordMapper.selectById(id);
  63 + }
  64 +
  65 + @Override
  66 + public PageResult<CrmFollowUpRecordDO> getFollowUpRecordPage(CrmFollowUpRecordPageReqVO pageReqVO) {
  67 + return crmFollowUpRecordMapper.selectPage(pageReqVO);
  68 + }
  69 +
  70 +}
0 71 \ No newline at end of file
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImpl.java
... ... @@ -20,6 +20,7 @@ import org.springframework.validation.annotation.Validated;
20 20 import java.util.Collection;
21 21 import java.util.Collections;
22 22 import java.util.List;
  23 +import java.util.Set;
23 24  
24 25 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
25 26 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
... ... @@ -36,7 +37,7 @@ import static cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnu
36 37 public class CrmPermissionServiceImpl implements CrmPermissionService {
37 38  
38 39 @Resource
39   - private CrmPermissionMapper crmPermissionMapper;
  40 + private CrmPermissionMapper permissionMapper;
40 41  
41 42 @Resource
42 43 private AdminUserApi adminUserApi;
... ... @@ -44,50 +45,59 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
44 45 @Override
45 46 @Transactional(rollbackFor = Exception.class)
46 47 public Long createPermission(CrmPermissionCreateReqBO createReqBO) {
47   - // TODO @puhui999:排重
  48 + validatePermissionNotExists(Collections.singletonList(createReqBO));
48 49 // 1. 校验用户是否存在
49 50 adminUserApi.validateUserList(Collections.singletonList(createReqBO.getUserId()));
50 51  
51 52 // 2. 创建
52 53 CrmPermissionDO permission = CrmPermissionConvert.INSTANCE.convert(createReqBO);
53   - crmPermissionMapper.insert(permission);
  54 + permissionMapper.insert(permission);
54 55 return permission.getId();
55 56 }
56 57  
57 58 @Override
58 59 public void createPermissionBatch(List<CrmPermissionCreateReqBO> createReqBOs) {
59   - // TODO @puhui999:排重
  60 + validatePermissionNotExists(createReqBOs);
60 61 // 1. 校验用户是否存在
61 62 adminUserApi.validateUserList(convertSet(createReqBOs, CrmPermissionCreateReqBO::getUserId));
62 63  
63 64 // 2. 创建
64 65 List<CrmPermissionDO> permissions = CrmPermissionConvert.INSTANCE.convertList(createReqBOs);
65   - crmPermissionMapper.insertBatch(permissions);
  66 + permissionMapper.insertBatch(permissions);
66 67 }
67 68  
68 69 @Override
69 70 @Transactional(rollbackFor = Exception.class)
70 71 public void updatePermission(CrmPermissionUpdateReqVO updateReqVO) {
71   - // TODO @puhui999:排重
72 72 // 1. 校验存在
73   - validateCrmPermissionExists(updateReqVO.getIds());
  73 + validatePermissionExists(updateReqVO.getIds());
74 74 // 2. 更新
75 75 List<CrmPermissionDO> updateDO = CrmPermissionConvert.INSTANCE.convertList(updateReqVO);
76   - crmPermissionMapper.updateBatch(updateDO);
  76 + permissionMapper.updateBatch(updateDO);
77 77 }
78 78  
79   - private void validateCrmPermissionExists(Collection<Long> ids) {
80   - List<CrmPermissionDO> permissionList = crmPermissionMapper.selectBatchIds(ids);
  79 + private void validatePermissionExists(Collection<Long> ids) {
  80 + List<CrmPermissionDO> permissionList = permissionMapper.selectBatchIds(ids);
81 81 if (ObjUtil.notEqual(permissionList.size(), ids.size())) {
82 82 throw exception(CRM_PERMISSION_NOT_EXISTS);
83 83 }
84 84 }
85 85  
  86 + private void validatePermissionNotExists(Collection<CrmPermissionCreateReqBO> createReqBOs) {
  87 + Set<Integer> bizTypes = convertSet(createReqBOs, CrmPermissionCreateReqBO::getBizType);
  88 + Set<Long> bizIds = convertSet(createReqBOs, CrmPermissionCreateReqBO::getBizId);
  89 + Set<Long> userIds = convertSet(createReqBOs, CrmPermissionCreateReqBO::getUserId);
  90 + Long count = permissionMapper.selectListByBiz(bizTypes, bizIds, userIds);
  91 + if (count > 0) {
  92 + throw exception(CRM_PERMISSION_CREATE_FAIL);
  93 + }
  94 + }
  95 +
86 96 @Override
87 97 @Transactional(rollbackFor = Exception.class)
88 98 public void transferPermission(CrmPermissionTransferReqBO transferReqBO) {
89 99 // 1. 校验数据权限:是否是负责人,只有负责人才可以转移
90   - CrmPermissionDO oldPermission = crmPermissionMapper.selectByBizTypeAndBizIdByUserId(
  100 + CrmPermissionDO oldPermission = permissionMapper.selectByBizTypeAndBizIdByUserId(
91 101 transferReqBO.getBizType(), transferReqBO.getBizId(), transferReqBO.getUserId());
92 102 String bizTypeName = CrmBizTypeEnum.getNameByType(transferReqBO.getBizType());
93 103 if (oldPermission == null // 不是拥有者,并且不是超管
... ... @@ -102,25 +112,25 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
102 112 adminUserApi.validateUserList(Collections.singletonList(transferReqBO.getNewOwnerUserId()));
103 113  
104 114 // 2. 修改新负责人的权限
105   - List<CrmPermissionDO> permissions = crmPermissionMapper.selectByBizTypeAndBizId(
  115 + List<CrmPermissionDO> permissions = permissionMapper.selectByBizTypeAndBizId(
106 116 transferReqBO.getBizType(), transferReqBO.getBizId()); // 获得所有数据权限
107 117 CrmPermissionDO permission = CollUtil.findOne(permissions,
108 118 item -> ObjUtil.equal(item.getUserId(), transferReqBO.getNewOwnerUserId()));
109 119 if (permission == null) {
110   - crmPermissionMapper.insert(new CrmPermissionDO().setBizType(transferReqBO.getBizType())
  120 + permissionMapper.insert(new CrmPermissionDO().setBizType(transferReqBO.getBizType())
111 121 .setBizId(transferReqBO.getBizId()).setUserId(transferReqBO.getNewOwnerUserId())
112 122 .setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
113 123 } else {
114   - crmPermissionMapper.updateById(new CrmPermissionDO().setId(permission.getId())
  124 + permissionMapper.updateById(new CrmPermissionDO().setId(permission.getId())
115 125 .setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
116 126 }
117 127  
118 128 // 3. 修改老负责人的权限
119 129 if (transferReqBO.getOldOwnerPermissionLevel() != null) {
120   - crmPermissionMapper.updateById(new CrmPermissionDO().setId(oldPermission.getId())
  130 + permissionMapper.updateById(new CrmPermissionDO().setId(oldPermission.getId())
121 131 .setLevel(transferReqBO.getOldOwnerPermissionLevel()));
122 132 } else {
123   - crmPermissionMapper.deleteById(oldPermission.getId());
  133 + permissionMapper.deleteById(oldPermission.getId());
124 134 }
125 135 }
126 136  
... ... @@ -128,19 +138,19 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
128 138 @Transactional(rollbackFor = Exception.class)
129 139 public void deletePermission(Integer bizType, Long bizId, Integer level) {
130 140 // 校验存在
131   - List<CrmPermissionDO> permissions = crmPermissionMapper.selectListByBizTypeAndBizIdAndLevel(
  141 + List<CrmPermissionDO> permissions = permissionMapper.selectListByBizTypeAndBizIdAndLevel(
132 142 bizType, bizId, level);
133 143 if (CollUtil.isEmpty(permissions)) {
134 144 throw exception(CRM_PERMISSION_NOT_EXISTS);
135 145 }
136 146  
137 147 // 删除数据权限
138   - crmPermissionMapper.deleteBatchIds(convertSet(permissions, CrmPermissionDO::getId));
  148 + permissionMapper.deleteBatchIds(convertSet(permissions, CrmPermissionDO::getId));
139 149 }
140 150  
141 151 @Override
142 152 public void deletePermission(Integer bizType, Long bizId) {
143   - int deletedCount = crmPermissionMapper.deletePermission(bizType, bizId);
  153 + int deletedCount = permissionMapper.deletePermission(bizType, bizId);
144 154 if (deletedCount == 0) {
145 155 throw exception(CRM_PERMISSION_NOT_EXISTS);
146 156 }
... ... @@ -148,7 +158,7 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
148 158  
149 159 @Override
150 160 public void deletePermissionBatch(Collection<Long> ids, Long userId) {
151   - List<CrmPermissionDO> permissions = crmPermissionMapper.selectBatchIds(ids);
  161 + List<CrmPermissionDO> permissions = permissionMapper.selectBatchIds(ids);
152 162 if (CollUtil.isEmpty(permissions)) {
153 163 throw exception(CRM_PERMISSION_NOT_EXISTS);
154 164 }
... ... @@ -157,19 +167,19 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
157 167 throw exception(CRM_PERMISSION_DELETE_FAIL);
158 168 }
159 169 // 校验操作人是否为负责人
160   - CrmPermissionDO permission = crmPermissionMapper.selectByIdAndUserId(permissions.get(0).getBizId(), userId);
  170 + CrmPermissionDO permission = permissionMapper.selectByIdAndUserId(permissions.get(0).getBizId(), userId);
161 171 if (!CrmPermissionLevelEnum.isOwner(permission.getLevel())) {
162 172 throw exception(CRM_PERMISSION_DELETE_DENIED);
163 173 }
164 174  
165 175 // 删除数据权限
166   - crmPermissionMapper.deleteBatchIds(ids);
  176 + permissionMapper.deleteBatchIds(ids);
167 177 }
168 178  
169 179 @Override
170 180 public void deleteSelfPermission(Long id, Long userId) {
171 181 // 校验数据存在且是自己
172   - CrmPermissionDO permission = crmPermissionMapper.selectByIdAndUserId(id, userId);
  182 + CrmPermissionDO permission = permissionMapper.selectByIdAndUserId(id, userId);
173 183 if (permission == null) {
174 184 throw exception(CRM_PERMISSION_NOT_EXISTS);
175 185 }
... ... @@ -179,22 +189,22 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
179 189 }
180 190  
181 191 // 删除
182   - crmPermissionMapper.deleteById(id);
  192 + permissionMapper.deleteById(id);
183 193 }
184 194  
185 195 @Override
186 196 public List<CrmPermissionDO> getPermissionListByBiz(Integer bizType, Long bizId) {
187   - return crmPermissionMapper.selectByBizTypeAndBizId(bizType, bizId);
  197 + return permissionMapper.selectByBizTypeAndBizId(bizType, bizId);
188 198 }
189 199  
190 200 @Override
191 201 public List<CrmPermissionDO> getPermissionListByBiz(Integer bizType, Collection<Long> bizIds) {
192   - return crmPermissionMapper.selectByBizTypeAndBizIds(bizType, bizIds);
  202 + return permissionMapper.selectByBizTypeAndBizIds(bizType, bizIds);
193 203 }
194 204  
195 205 @Override
196 206 public List<CrmPermissionDO> getPermissionListByBizTypeAndUserId(Integer bizType, Long userId) {
197   - return crmPermissionMapper.selectListByBizTypeAndUserId(bizType, userId);
  207 + return permissionMapper.selectListByBizTypeAndUserId(bizType, userId);
198 208 }
199 209  
200 210 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanService.java
... ... @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.crm.service.receivable;
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanCreateReqVO;
5 5 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO;
6   -import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanTransferReqVO;
7 6 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanUpdateReqVO;
8 7 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
9 8 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;
... ... @@ -78,12 +77,4 @@ public interface CrmReceivablePlanService {
78 77 */
79 78 PageResult<CrmReceivablePlanDO> getReceivablePlanPageByCustomerId(CrmReceivablePlanPageReqVO pageReqVO);
80 79  
81   - /**
82   - * 回款计划转移
83   - *
84   - * @param reqVO 请求
85   - * @param userId 用户编号
86   - */
87   - void transferReceivablePlan(CrmReceivablePlanTransferReqVO reqVO, Long userId);
88   -
89 80 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java
... ... @@ -4,9 +4,9 @@ import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.collection.ListUtil;
5 5 import cn.hutool.core.util.ObjectUtil;
6 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  7 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
7 8 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanCreateReqVO;
8 9 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO;
9   -import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanTransferReqVO;
10 10 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanUpdateReqVO;
11 11 import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivablePlanConvert;
12 12 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
... ... @@ -20,6 +20,9 @@ import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
20 20 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
21 21 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
22 22 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
  23 +import com.mzt.logapi.context.LogRecordContext;
  24 +import com.mzt.logapi.service.impl.DiffParseFunction;
  25 +import com.mzt.logapi.starter.annotation.LogRecord;
23 26 import jakarta.annotation.Resource;
24 27 import org.springframework.stereotype.Service;
25 28 import org.springframework.validation.annotation.Validated;
... ... @@ -29,6 +32,7 @@ import java.util.List;
29 32  
30 33 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
31 34 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
  35 +import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
32 36  
33 37 // TODO @liuhongfeng:参考 CrmReceivableServiceImpl 写的 todo 哈;
34 38  
... ... @@ -49,10 +53,11 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
49 53 @Resource
50 54 private CrmCustomerService customerService;
51 55 @Resource
52   - private CrmPermissionService crmPermissionService;
  56 + private CrmPermissionService permissionService;
53 57  
54 58 @Override
55   - // TODO @puhui999:操作日志
  59 + @LogRecord(type = CRM_RECEIVABLE_PLAN_TYPE, subType = CRM_RECEIVABLE_PLAN_CREATE_SUB_TYPE, bizNo = "{{#receivablePlan.id}}",
  60 + success = CRM_RECEIVABLE_PLAN_CREATE_SUCCESS)
56 61 public Long createReceivablePlan(CrmReceivablePlanCreateReqVO createReqVO, Long userId) {
57 62 // TODO @liuhongfeng:第几期的计算;基于是 contractId + contractDO 的第几个还款
58 63 // TODO @liuhongfeng contractId:校验合同是否存在
... ... @@ -64,9 +69,12 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
64 69  
65 70 receivablePlanMapper.insert(receivablePlan);
66 71 // 创建数据权限
67   - crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(userId)
  72 + permissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(userId)
68 73 .setBizType(CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType()).setBizId(receivablePlan.getId())
69 74 .setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
  75 +
  76 + // 4. 记录操作日志上下文
  77 + LogRecordContext.putVariable("receivablePlan", receivablePlan);
70 78 return receivablePlan.getId();
71 79 }
72 80  
... ... @@ -89,31 +97,44 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
89 97 }
90 98  
91 99 @Override
  100 + @LogRecord(type = CRM_RECEIVABLE_PLAN_TYPE, subType = CRM_RECEIVABLE_PLAN_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
  101 + success = CRM_RECEIVABLE_PLAN_UPDATE_SUCCESS)
92 102 @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
93   - // TODO @puhui999:操作日志
94 103 public void updateReceivablePlan(CrmReceivablePlanUpdateReqVO updateReqVO) {
95 104 // TODO @liuhongfeng:如果已经有对应的还款,则不允许编辑;
96 105 // 校验存在
97   - validateReceivablePlanExists(updateReqVO.getId());
  106 + CrmReceivablePlanDO oldReceivablePlan = validateReceivablePlanExists(updateReqVO.getId());
98 107  
99 108 // 更新
100 109 CrmReceivablePlanDO updateObj = CrmReceivablePlanConvert.INSTANCE.convert(updateReqVO);
101 110 receivablePlanMapper.updateById(updateObj);
  111 +
  112 + // 3. 记录操作日志上下文
  113 + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldReceivablePlan, CrmReceivablePlanUpdateReqVO.class));
  114 + LogRecordContext.putVariable("receivablePlan", oldReceivablePlan);
102 115 }
103 116  
104 117 @Override
  118 + @LogRecord(type = CRM_RECEIVABLE_PLAN_TYPE, subType = CRM_RECEIVABLE_PLAN_DELETE_SUB_TYPE, bizNo = "{{#id}}",
  119 + success = CRM_RECEIVABLE_PLAN_DELETE_SUCCESS)
105 120 @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
106 121 public void deleteReceivablePlan(Long id) {
107 122 // 校验存在
108   - validateReceivablePlanExists(id);
  123 + CrmReceivablePlanDO receivablePlan = validateReceivablePlanExists(id);
109 124 // 删除
110 125 receivablePlanMapper.deleteById(id);
  126 + // 删除数据权限
  127 + permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), id);
  128 + // 记录操作日志上下文
  129 + LogRecordContext.putVariable("receivablePlan", receivablePlan);
111 130 }
112 131  
113   - private void validateReceivablePlanExists(Long id) {
114   - if (receivablePlanMapper.selectById(id) == null) {
  132 + private CrmReceivablePlanDO validateReceivablePlanExists(Long id) {
  133 + CrmReceivablePlanDO receivablePlan = receivablePlanMapper.selectById(id);
  134 + if (receivablePlan == null) {
115 135 throw exception(RECEIVABLE_PLAN_NOT_EXISTS);
116 136 }
  137 + return receivablePlan;
117 138 }
118 139  
119 140 @Override
... ... @@ -141,19 +162,4 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
141 162 return receivablePlanMapper.selectPageByCustomerId(pageReqVO);
142 163 }
143 164  
144   - // TODO @puhui999:这个没有 transfer 接口;可能是的哈
145   - @Override
146   - public void transferReceivablePlan(CrmReceivablePlanTransferReqVO reqVO, Long userId) {
147   - // 1 校验回款计划是否存在
148   - validateReceivablePlanExists(reqVO.getId());
149   -
150   - // 2.1 数据权限转移
151   - crmPermissionService.transferPermission(
152   - CrmReceivablePlanConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType()));
153   - // 2.2 设置新的负责人
154   - receivablePlanMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
155   -
156   - // 3. TODO 记录转移日志
157   - }
158   -
159 165 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java
... ... @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.crm.service.receivable;
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableCreateReqVO;
5 5 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO;
6   -import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableTransferReqVO;
7 6 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableUpdateReqVO;
8 7 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
9 8 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;
... ... @@ -23,9 +22,10 @@ public interface CrmReceivableService {
23 22 * 创建回款
24 23 *
25 24 * @param createReqVO 创建信息
  25 + * @param userId 用户编号
26 26 * @return 编号
27 27 */
28   - Long createReceivable(@Valid CrmReceivableCreateReqVO createReqVO);
  28 + Long createReceivable(@Valid CrmReceivableCreateReqVO createReqVO, Long userId);
29 29  
30 30 /**
31 31 * 更新回款
... ... @@ -78,12 +78,4 @@ public interface CrmReceivableService {
78 78 */
79 79 PageResult<CrmReceivableDO> getReceivablePageByCustomerId(CrmReceivablePageReqVO pageReqVO);
80 80  
81   - /**
82   - * 回款转移
83   - *
84   - * @param reqVO 请求
85   - * @param userId 用户编号
86   - */
87   - void transferReceivable(CrmReceivableTransferReqVO reqVO, Long userId);
88   -
89 81 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java
... ... @@ -5,9 +5,9 @@ import cn.hutool.core.collection.ListUtil;
5 5 import cn.hutool.core.util.ObjectUtil;
6 6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
7 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  8 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
8 9 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableCreateReqVO;
9 10 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO;
10   -import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableTransferReqVO;
11 11 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableUpdateReqVO;
12 12 import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivableConvert;
13 13 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
... ... @@ -22,6 +22,10 @@ import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPerm
22 22 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
23 23 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
24 24 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
  25 +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
  26 +import com.mzt.logapi.context.LogRecordContext;
  27 +import com.mzt.logapi.service.impl.DiffParseFunction;
  28 +import com.mzt.logapi.starter.annotation.LogRecord;
25 29 import jakarta.annotation.Resource;
26 30 import org.springframework.stereotype.Service;
27 31 import org.springframework.validation.annotation.Validated;
... ... @@ -31,6 +35,7 @@ import java.util.List;
31 35  
32 36 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
33 37 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
  38 +import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
34 39  
35 40 /**
36 41 * CRM 回款 Service 实现类
... ... @@ -51,11 +56,12 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
51 56 @Resource
52 57 private CrmReceivablePlanService receivablePlanService;
53 58 @Resource
54   - private CrmPermissionService crmPermissionService;
  59 + private CrmPermissionService permissionService;
55 60  
56 61 @Override
57   - // TODO @puhui999:操作日志
58   - public Long createReceivable(CrmReceivableCreateReqVO createReqVO) {
  62 + @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_CREATE_SUB_TYPE, bizNo = "{{#receivable.id}}",
  63 + success = CRM_RECEIVABLE_CREATE_SUCCESS)
  64 + public Long createReceivable(CrmReceivableCreateReqVO createReqVO, Long userId) {
59 65 // 插入还款
60 66 CrmReceivableDO receivable = CrmReceivableConvert.INSTANCE.convert(createReqVO);
61 67 if (ObjectUtil.isNull(receivable.getAuditStatus())) {
... ... @@ -67,8 +73,12 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
67 73 checkReceivable(receivable);
68 74  
69 75 receivableMapper.insert(receivable);
70   -
  76 + // 3. 创建数据权限
  77 + permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_RECEIVABLE.getType())
  78 + .setBizId(receivable.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人
71 79 // TODO @liuhongfeng:需要更新关联的 plan
  80 + // 记录操作日志上下文
  81 + LogRecordContext.putVariable("receivable", receivable);
72 82 return receivable.getId();
73 83 }
74 84  
... ... @@ -98,11 +108,12 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
98 108 }
99 109  
100 110 @Override
101   - // TODO @puhui999:操作日志
102   - // TODO @puhui999:权限校验
  111 + @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
  112 + success = CRM_RECEIVABLE_UPDATE_SUCCESS)
  113 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
103 114 public void updateReceivable(CrmReceivableUpdateReqVO updateReqVO) {
104 115 // 校验存在
105   - validateReceivableExists(updateReqVO.getId());
  116 + CrmReceivableDO oldReceivable = validateReceivableExists(updateReqVO.getId());
106 117 // TODO @liuhongfeng:只有在草稿、审核中,可以提交修改
107 118  
108 119 // 更新还款
... ... @@ -110,6 +121,9 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
110 121 receivableMapper.updateById(updateObj);
111 122  
112 123 // TODO @liuhongfeng:需要更新关联的 plan
  124 + // 3. 记录操作日志上下文
  125 + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldReceivable, CrmReceivableUpdateReqVO.class));
  126 + LogRecordContext.putVariable("receivable", oldReceivable);
113 127 }
114 128  
115 129 // TODO @liuhongfeng:缺一个取消合同的接口;只有草稿、审批中可以取消;CrmAuditStatusEnum
... ... @@ -117,24 +131,33 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
117 131 // TODO @liuhongfeng:缺一个发起审批的接口;只有草稿可以发起审批;CrmAuditStatusEnum
118 132  
119 133 @Override
120   - // TODO @puhui999:操作日志
121   - // TODO @puhui999:权限校验
  134 + @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_DELETE_SUB_TYPE, bizNo = "{{#id}}",
  135 + success = CRM_RECEIVABLE_DELETE_SUCCESS)
  136 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
122 137 public void deleteReceivable(Long id) {
123 138 // TODO @liuhongfeng:如果被 CrmReceivablePlanDO 所使用,则不允许删除
124 139 // 校验存在
125   - validateReceivableExists(id);
  140 + CrmReceivableDO receivable = validateReceivableExists(id);
126 141 // 删除
127 142 receivableMapper.deleteById(id);
  143 +
  144 + // 删除数据权限
  145 + permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), id);
  146 +
  147 + // 记录操作日志上下文
  148 + LogRecordContext.putVariable("receivable", receivable);
128 149 }
129 150  
130   - private void validateReceivableExists(Long id) {
131   - if (receivableMapper.selectById(id) == null) {
  151 + private CrmReceivableDO validateReceivableExists(Long id) {
  152 + CrmReceivableDO receivable = receivableMapper.selectById(id);
  153 + if (receivable == null) {
132 154 throw exception(RECEIVABLE_NOT_EXISTS);
133 155 }
  156 + return receivable;
134 157 }
135 158  
136   - // TODO @芋艿:数据权限
137 159 @Override
  160 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = "#id", level = CrmPermissionLevelEnum.READ)
138 161 public CrmReceivableDO getReceivable(Long id) {
139 162 return receivableMapper.selectById(id);
140 163 }
... ... @@ -158,18 +181,4 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
158 181 return receivableMapper.selectPageByCustomerId(pageReqVO);
159 182 }
160 183  
161   - @Override
162   - public void transferReceivable(CrmReceivableTransferReqVO reqVO, Long userId) {
163   - // 1 校验回款是否存在
164   - validateReceivableExists(reqVO.getId());
165   -
166   - // 2.1 数据权限转移
167   - crmPermissionService.transferPermission(
168   - CrmReceivableConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_RECEIVABLE.getType()));
169   - // 2.2 设置新的负责人
170   - receivableMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
171   -
172   - // 3. TODO 记录转移日志
173   - }
174   -
175 184 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/receivable/CrmCrmReceivableServiceImplTest.java
... ... @@ -13,6 +13,7 @@ import org.junit.jupiter.api.Test;
13 13 import org.springframework.context.annotation.Import;
14 14  
15 15 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
  16 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
16 17 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
17 18 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
18 19 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
... ... @@ -42,7 +43,7 @@ public class CrmCrmReceivableServiceImplTest extends BaseDbUnitTest {
42 43 CrmReceivableCreateReqVO reqVO = randomPojo(CrmReceivableCreateReqVO.class);
43 44  
44 45 // 调用
45   - Long receivableId = receivableService.createReceivable(reqVO);
  46 + Long receivableId = receivableService.createReceivable(reqVO, getLoginUserId());
46 47 // 断言
47 48 assertNotNull(receivableId);
48 49 // 校验记录的属性是否正确
... ...
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/operatelog/SysParseFunctionNameConstants.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.enums.operatelog;
  2 +
  3 +/**
  4 + * functionName 常量枚举
  5 + * 方便别的模块调用
  6 + *
  7 + * @author HUIHUI
  8 + */
  9 +public interface SysParseFunctionNameConstants {
  10 +
  11 + String GET_ADMIN_USER_BY_ID = "getAdminUserById"; // 获取用户信息
  12 + String GET_DEPT_BY_ID = "getDeptById"; // 获取部门信息
  13 + String GET_AREA = "getArea"; // 获取区域信息
  14 + String GET_SEX = "getSex"; // 获取性别
  15 + String GET_BOOLEAN = "getBoolean"; // 获取是否
  16 +
  17 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AdminUserParseFunction.java
... ... @@ -8,6 +8,8 @@ import jakarta.annotation.Resource;
8 8 import lombok.extern.slf4j.Slf4j;
9 9 import org.springframework.stereotype.Component;
10 10  
  11 +import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNameConstants.GET_ADMIN_USER_BY_ID;
  12 +
11 13 /**
12 14 * 管理员名字的 {@link IParseFunction} 实现类
13 15 *
... ... @@ -22,7 +24,7 @@ public class AdminUserParseFunction implements IParseFunction {
22 24  
23 25 @Override
24 26 public String functionName() {
25   - return "getAdminUserById";
  27 + return GET_ADMIN_USER_BY_ID;
26 28 }
27 29  
28 30 @Override
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AreaParseFunction.java
... ... @@ -6,6 +6,8 @@ import com.mzt.logapi.service.IParseFunction;
6 6 import lombok.extern.slf4j.Slf4j;
7 7 import org.springframework.stereotype.Component;
8 8  
  9 +import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNameConstants.GET_AREA;
  10 +
9 11 /**
10 12 * 地名的 {@link IParseFunction} 实现类
11 13 *
... ... @@ -22,7 +24,7 @@ public class AreaParseFunction implements IParseFunction {
22 24  
23 25 @Override
24 26 public String functionName() {
25   - return "getAreaById";
  27 + return GET_AREA;
26 28 }
27 29  
28 30 @Override
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmBooleanParseFunction.java renamed to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/BooleanParseFunction.java
1   -package cn.iocoder.yudao.module.crm.framework.operatelog.core;
  1 +package cn.iocoder.yudao.module.system.framework.operatelog.core;
2 2  
3 3 import cn.hutool.core.util.StrUtil;
4 4 import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
... ... @@ -7,16 +7,16 @@ import com.mzt.logapi.service.IParseFunction;
7 7 import lombok.extern.slf4j.Slf4j;
8 8 import org.springframework.stereotype.Component;
9 9  
  10 +import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNameConstants.GET_BOOLEAN;
  11 +
10 12 /**
11   - * 行业的 {@link IParseFunction} 实现类
  13 + * 是否类型的 {@link IParseFunction} 实现类
12 14 *
13 15 * @author HUIHUI
14 16 */
15 17 @Component
16 18 @Slf4j
17   -public class CrmBooleanParseFunction implements IParseFunction {
18   -
19   - public static final String NAME = "getBooleanById";
  19 +public class BooleanParseFunction implements IParseFunction {
20 20  
21 21 @Override
22 22 public boolean executeBefore() {
... ... @@ -25,7 +25,7 @@ public class CrmBooleanParseFunction implements IParseFunction {
25 25  
26 26 @Override
27 27 public String functionName() {
28   - return NAME;
  28 + return GET_BOOLEAN;
29 29 }
30 30  
31 31 @Override
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/DeptParseFunction.java
... ... @@ -8,6 +8,8 @@ import jakarta.annotation.Resource;
8 8 import lombok.extern.slf4j.Slf4j;
9 9 import org.springframework.stereotype.Component;
10 10  
  11 +import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNameConstants.GET_DEPT_BY_ID;
  12 +
11 13 /**
12 14 * 管理员名字的 {@link IParseFunction} 实现类
13 15 *
... ... @@ -22,7 +24,7 @@ public class DeptParseFunction implements IParseFunction {
22 24  
23 25 @Override
24 26 public String functionName() {
25   - return "getDeptById";
  27 + return GET_DEPT_BY_ID;
26 28 }
27 29  
28 30 @Override
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmSexParseFunction.java renamed to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/SexParseFunction.java
1   -package cn.iocoder.yudao.module.crm.framework.operatelog.core;
  1 +package cn.iocoder.yudao.module.system.framework.operatelog.core;
2 2  
3 3 import cn.hutool.core.util.StrUtil;
4 4 import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
... ... @@ -7,6 +7,8 @@ import com.mzt.logapi.service.IParseFunction;
7 7 import lombok.extern.slf4j.Slf4j;
8 8 import org.springframework.stereotype.Component;
9 9  
  10 +import static cn.iocoder.yudao.module.system.enums.operatelog.SysParseFunctionNameConstants.GET_SEX;
  11 +
10 12 /**
11 13 * 行业的 {@link IParseFunction} 实现类
12 14 *
... ... @@ -14,9 +16,7 @@ import org.springframework.stereotype.Component;
14 16 */
15 17 @Component
16 18 @Slf4j
17   -public class CrmSexParseFunction implements IParseFunction {
18   -
19   - public static final String NAME = "getSexById";
  19 +public class SexParseFunction implements IParseFunction {
20 20  
21 21 @Override
22 22 public boolean executeBefore() {
... ... @@ -25,7 +25,7 @@ public class CrmSexParseFunction implements IParseFunction {
25 25  
26 26 @Override
27 27 public String functionName() {
28   - return NAME;
  28 + return GET_SEX;
29 29 }
30 30  
31 31 @Override
... ...