Commit 0a97ff03c6492e8b00528a8049c4f1cb953d817f

Authored by puhui999
2 parents c6b133d9 d604e76d

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

# Conflicts:
#	yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
Showing 21 changed files with 318 additions and 437 deletions
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java
... ... @@ -95,7 +95,6 @@ public interface LogRecordConstants {
95 95 String CRM_BUSINESS_FOLLOW_UP_SUCCESS = "商机跟进【{{#businessName}}】";
96 96 String CRM_BUSINESS_UPDATE_STATUS_SUB_TYPE = "更新商机状态";
97 97 String CRM_BUSINESS_UPDATE_STATUS_SUCCESS = "更新了商机【{{#businessName}}】的状态从【{{#oldStatusName}}】变更为了【{{#newStatusName}}】";
98   -// String CRM_BUSINESS_UPDATE_STATUS_SUCCESS = "阿巴阿巴";
99 98  
100 99 // ======================= CRM_CONTRACT 合同 =======================
101 100  
... ... @@ -110,6 +109,8 @@ public interface LogRecordConstants {
110 109 String CRM_CONTRACT_TRANSFER_SUCCESS = "将合同【{{#contract.name}}】的负责人从【{getAdminUserById{#contract.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】";
111 110 String CRM_CONTRACT_SUBMIT_SUB_TYPE = "提交合同审批";
112 111 String CRM_CONTRACT_SUBMIT_SUCCESS = "提交合同【{{#contractName}}】审批成功";
  112 + String CRM_CONTRACT_FOLLOW_UP_SUB_TYPE = "合同跟进";
  113 + String CRM_CONTRACT_FOLLOW_UP_SUCCESS = "合同跟进【{{#contractName}}】";
113 114  
114 115 // ======================= CRM_PRODUCT 产品 =======================
115 116  
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java
... ... @@ -135,7 +135,6 @@ public class CrmBusinessController {
135 135 return success(BeanUtils.toBean(businessService.getBusinessList(ids, getLoginUserId()), CrmBusinessRespVO.class));
136 136 }
137 137  
138   - // TODO 芋艿:处理下
139 138 @GetMapping("/simple-all-list")
140 139 @Operation(summary = "获得联系人的精简列表")
141 140 @PreAuthorize("@ss.hasPermission('crm:contact:query')")
... ... @@ -144,7 +143,8 @@ public class CrmBusinessController {
144 143 reqVO.setPageSize(PAGE_SIZE_NONE); // 不分页
145 144 PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPage(reqVO, getLoginUserId());
146 145 return success(convertList(pageResult.getList(), business -> // 只返回 id、name 字段
147   - new CrmBusinessRespVO().setId(business.getId()).setName(business.getName())));
  146 + new CrmBusinessRespVO().setId(business.getId()).setName(business.getName())
  147 + .setCustomerId(business.getCustomerId())));
148 148 }
149 149  
150 150 @GetMapping("/page")
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessRespVO.java
... ... @@ -130,7 +130,7 @@ public class CrmBusinessRespVO {
130 130 @Schema(description = "产品单价", requiredMode = Schema.RequiredMode.REQUIRED, example = "123.00")
131 131 private BigDecimal productPrice;
132 132  
133   - @Schema(description = "合同价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "123.00")
  133 + @Schema(description = "商机价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "123.00")
134 134 private BigDecimal businessPrice;
135 135  
136 136 @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911")
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessSaveReqVO.java
... ... @@ -53,7 +53,7 @@ public class CrmBusinessSaveReqVO {
53 53 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
54 54 private LocalDateTime dealTime;
55 55  
56   - @Schema(description = "整单折扣")
  56 + @Schema(description = "整单折扣", requiredMode = Schema.RequiredMode.REQUIRED, example = "55.00")
57 57 @DiffLogField(name = "整单折扣")
58 58 @NotNull(message = "整单折扣不能为空")
59 59 private BigDecimal discountPercent;
... ... @@ -82,8 +82,8 @@ public class CrmBusinessSaveReqVO {
82 82 @NotNull(message = "产品单价不能为空")
83 83 private BigDecimal productPrice;
84 84  
85   - @Schema(description = "合同价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "123.00")
86   - @NotNull(message = "合同价格不能为空")
  85 + @Schema(description = "商机价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "123.00")
  86 + @NotNull(message = "商机价格不能为空")
87 87 private BigDecimal businessPrice;
88 88  
89 89 @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911")
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/CrmContactController.java
... ... @@ -120,7 +120,8 @@ public class CrmContactController {
120 120 public CommonResult<List<CrmContactRespVO>> getSimpleContactList() {
121 121 List<CrmContactDO> list = contactService.getContactList(getLoginUserId());
122 122 return success(convertList(list, contact -> // 只返回 id、name 字段
123   - new CrmContactRespVO().setId(contact.getId()).setName(contact.getName())));
  123 + new CrmContactRespVO().setId(contact.getId()).setName(contact.getName())
  124 + .setCustomerId(contact.getCustomerId())));
124 125 }
125 126  
126 127 @GetMapping("/page")
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java
... ... @@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.lang.Assert;
5 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
6 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  7 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
7 8 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
8 9 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
9 10 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
... ... @@ -12,7 +13,6 @@ import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageR
12 13 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractRespVO;
13 14 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO;
14 15 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO;
15   -import cn.iocoder.yudao.module.crm.convert.contract.CrmContractConvert;
16 16 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
17 17 import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
18 18 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
... ... @@ -24,6 +24,8 @@ import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
24 24 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
25 25 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
26 26 import cn.iocoder.yudao.module.crm.service.product.CrmProductService;
  27 +import cn.iocoder.yudao.module.system.api.dept.DeptApi;
  28 +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
27 29 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
28 30 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
29 31 import io.swagger.v3.oas.annotations.Operation;
... ... @@ -44,6 +46,7 @@ import java.util.stream.Stream;
44 46  
45 47 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
46 48 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
  49 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
47 50 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
48 51 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
49 52 import static java.util.Collections.singletonList;
... ... @@ -64,8 +67,11 @@ public class CrmContractController {
64 67 private CrmBusinessService businessService;
65 68 @Resource
66 69 private CrmProductService productService;
  70 +
67 71 @Resource
68 72 private AdminUserApi adminUserApi;
  73 + @Resource
  74 + private DeptApi deptApi;
69 75  
70 76 @PostMapping("/create")
71 77 @Operation(summary = "创建合同")
... ... @@ -96,15 +102,24 @@ public class CrmContractController {
96 102 @Parameter(name = "id", description = "编号", required = true, example = "1024")
97 103 @PreAuthorize("@ss.hasPermission('crm:contract:query')")
98 104 public CommonResult<CrmContractRespVO> getContract(@RequestParam("id") Long id) {
99   - // 1. 查询合同
100 105 CrmContractDO contract = contractService.getContract(id);
  106 + return success(buildContractDetail(contract));
  107 + }
  108 +
  109 + private CrmContractRespVO buildContractDetail(CrmContractDO contract) {
101 110 if (contract == null) {
102   - return success(null);
  111 + return null;
103 112 }
104   -
105   - // 2. 拼接合同信息
106   - List<CrmContractRespVO> respVOList = buildContractDetailList(singletonList(contract));
107   - return success(respVOList.get(0));
  113 + CrmContractRespVO contractVO = buildContractDetailList(singletonList(contract)).get(0);
  114 + // 拼接产品项
  115 + List<CrmContractProductDO> businessProducts = contractService.getContractProductListByContractId(contractVO.getId());
  116 + Map<Long, CrmProductDO> productMap = productService.getProductMap(
  117 + convertSet(businessProducts, CrmContractProductDO::getProductId));
  118 + contractVO.setProducts(BeanUtils.toBean(businessProducts, CrmContractRespVO.Product.class, businessProductVO ->
  119 + MapUtils.findAndThen(productMap, businessProductVO.getProductId(),
  120 + product -> businessProductVO.setProductName(product.getName())
  121 + .setProductNo(product.getNo()).setProductUnit(product.getUnit()))));
  122 + return contractVO;
108 123 }
109 124  
110 125 @GetMapping("/page")
... ... @@ -161,27 +176,35 @@ public class CrmContractController {
161 176 if (CollUtil.isEmpty(contractList)) {
162 177 return Collections.emptyList();
163 178 }
164   - // 1. 获取客户列表
165   - List<CrmCustomerDO> customerList = customerService.getCustomerList(
  179 + // 1.1 获取客户列表
  180 + Map<Long, CrmCustomerDO> customerMap = customerService.getCustomerMap(
166 181 convertSet(contractList, CrmContractDO::getCustomerId));
167   - // 2. 获取创建人、负责人列表
  182 + // 1.2 获取创建人、负责人列表
168 183 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(contractList,
169 184 contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));
170   - // 3. 获取联系人
  185 + Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
  186 + // 1.3 获取联系人
171 187 Map<Long, CrmContactDO> contactMap = convertMap(contactService.getContactList(convertSet(contractList,
172   - CrmContractDO::getContactId)), CrmContactDO::getId);
173   - // 4. 获取商机
  188 + CrmContractDO::getSignContactId)), CrmContactDO::getId);
  189 + // 1.4 获取商机
174 190 Map<Long, CrmBusinessDO> businessMap = convertMap(businessService.getBusinessList(convertSet(contractList,
175 191 CrmContractDO::getBusinessId)), CrmBusinessDO::getId);
176   - // 5. 获取合同关联的商品
177   - Map<Long, CrmContractProductDO> contractProductMap = null;
178   - List<CrmProductDO> productList = null;
179   - if (contractList.size() == 1) {
180   - List<CrmContractProductDO> contractProductList = contractService.getContractProductListByContractId(contractList.get(0).getId());
181   - contractProductMap = convertMap(contractProductList, CrmContractProductDO::getProductId);
182   - productList = productService.getProductList(convertSet(contractProductList, CrmContractProductDO::getProductId));
183   - }
184   - return CrmContractConvert.INSTANCE.convertList(contractList, userMap, customerList, contactMap, businessMap, contractProductMap, productList);
  192 + // 2. 拼接数据
  193 + return BeanUtils.toBean(contractList, CrmContractRespVO.class, contractVO -> {
  194 + // 2.1 设置客户信息
  195 + findAndThen(customerMap, contractVO.getCustomerId(), customer -> contractVO.setCustomerName(customer.getName()));
  196 + // 2.2 设置用户信息
  197 + findAndThen(userMap, Long.parseLong(contractVO.getCreator()), user -> contractVO.setCreatorName(user.getNickname()));
  198 + MapUtils.findAndThen(userMap, contractVO.getOwnerUserId(), user -> {
  199 + contractVO.setOwnerUserName(user.getNickname());
  200 + MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> contractVO.setOwnerUserDeptName(dept.getName()));
  201 + });
  202 + findAndThen(userMap, contractVO.getSignUserId(), user -> contractVO.setSignUserName(user.getNickname()));
  203 + // 2.3 设置联系人信息
  204 + findAndThen(contactMap, contractVO.getSignContactId(), contact -> contractVO.setSignContactName(contact.getName()));
  205 + // 2.4 设置商机信息
  206 + findAndThen(businessMap, contractVO.getBusinessId(), business -> contractVO.setBusinessName(business.getName()));
  207 + });
185 208 }
186 209  
187 210 @GetMapping("/check-contract-count")
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractRespVO.java
... ... @@ -6,13 +6,11 @@ import io.swagger.v3.oas.annotations.media.Schema;
6 6 import lombok.AllArgsConstructor;
7 7 import lombok.Data;
8 8 import lombok.NoArgsConstructor;
9   -import org.springframework.format.annotation.DateTimeFormat;
10 9  
  10 +import java.math.BigDecimal;
11 11 import java.time.LocalDateTime;
12 12 import java.util.List;
13 13  
14   -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
15   -
16 14 @Schema(description = "管理后台 - CRM 合同 Response VO")
17 15 @Data
18 16 @ExcelIgnoreUnannotated
... ... @@ -26,6 +24,10 @@ public class CrmContractRespVO {
26 24 @ExcelProperty("合同名称")
27 25 private String name;
28 26  
  27 + @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20230101")
  28 + @ExcelProperty("合同编号")
  29 + private String no;
  30 +
29 31 @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18336")
30 32 @ExcelProperty("客户编号")
31 33 private Long customerId;
... ... @@ -40,72 +42,70 @@ public class CrmContractRespVO {
40 42 @ExcelProperty("商机名称")
41 43 private String businessName;
42 44  
  45 + @Schema(description = "最后跟进时间")
  46 + @ExcelProperty("最后跟进时间")
  47 + private LocalDateTime contactLastTime;
  48 +
  49 + @Schema(description = "负责人的用户编号", example = "25682")
  50 + @ExcelProperty("负责人的用户编号")
  51 + private Long ownerUserId;
  52 + @Schema(description = "负责人名字", example = "25682")
  53 + @ExcelProperty("负责人名字")
  54 + private String ownerUserName;
  55 + @Schema(description = "负责人部门")
  56 + @ExcelProperty("负责人部门")
  57 + private String ownerUserDeptName;
  58 +
43 59 @Schema(description = "工作流编号", example = "1043")
44 60 @ExcelProperty("工作流编号")
45 61 private Long processInstanceId;
46 62  
  63 + @Schema(description = "审批状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
  64 + @ExcelProperty("审批状态")
  65 + private Integer auditStatus;
  66 +
47 67 @Schema(description = "下单日期", requiredMode = Schema.RequiredMode.REQUIRED)
48 68 @ExcelProperty("下单日期")
49   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
50 69 private LocalDateTime orderDate;
51 70  
52   - @Schema(description = "负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17144")
53   - @ExcelProperty("负责人的用户编号")
54   - private Long ownerUserId;
55   -
56   - // TODO @芋艿:未来应该支持自动生成;
57   - @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20230101")
58   - @ExcelProperty("合同编号")
59   - private String no;
60   -
61 71 @Schema(description = "开始时间")
62 72 @ExcelProperty("开始时间")
63   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
64 73 private LocalDateTime startTime;
65 74  
66 75 @Schema(description = "结束时间")
67 76 @ExcelProperty("结束时间")
68   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
69 77 private LocalDateTime endTime;
70 78  
71   - @Schema(description = "合同金额", example = "5617")
72   - @ExcelProperty("合同金额")
73   - private Integer price;
  79 + @Schema(description = "产品总金额", example = "19510")
  80 + @ExcelProperty("产品总金额")
  81 + private BigDecimal totalProductPrice;
74 82  
75 83 @Schema(description = "整单折扣")
76 84 @ExcelProperty("整单折扣")
77   - private Integer discountPercent;
  85 + private BigDecimal discountPercent;
78 86  
79   - @Schema(description = "产品总金额", example = "19510")
80   - @ExcelProperty("产品总金额")
81   - private Integer productPrice;
  87 + @Schema(description = "合同金额", example = "5617")
  88 + @ExcelProperty("合同金额")
  89 + private BigDecimal totalPrice;
82 90  
83   - @Schema(description = "联系人编号", example = "18546")
84   - @ExcelProperty("联系人编号")
85   - private Long contactId;
86   - @Schema(description = "联系人编号", example = "18546")
87   - @ExcelProperty("联系人编号")
88   - private String contactName;
  91 + @Schema(description = "客户签约人编号", example = "18546")
  92 + private Long signContactId;
  93 + @Schema(description = "客户签约人", example = "小豆")
  94 + @ExcelProperty("客户签约人")
  95 + private String signContactName;
89 96  
90 97 @Schema(description = "公司签约人", example = "14036")
91   - @ExcelProperty("公司签约人")
92 98 private Long signUserId;
93   - @Schema(description = "公司签约人", example = "14036")
  99 + @Schema(description = "公司签约人", example = "小明")
94 100 @ExcelProperty("公司签约人")
95 101 private String signUserName;
96 102  
97   - @Schema(description = "最后跟进时间")
98   - @ExcelProperty("最后跟进时间")
99   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
100   - private LocalDateTime contactLastTime;
101   -
102 103 @Schema(description = "备注", example = "你猜")
103 104 @ExcelProperty("备注")
104 105 private String remark;
105 106  
106 107 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
107 108 @ExcelProperty("创建时间")
108   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
109 109 private LocalDateTime createTime;
110 110  
111 111 @Schema(description = "创建人", example = "25682")
... ... @@ -118,46 +118,40 @@ public class CrmContractRespVO {
118 118  
119 119 @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
120 120 @ExcelProperty("更新时间")
121   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
122 121 private LocalDateTime updateTime;
123 122  
124   - @Schema(description = "负责人", example = "test")
125   - @ExcelProperty("负责人")
126   - private String ownerUserName;
127   -
128   - @Schema(description = "审批状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
129   - @ExcelProperty("审批状态")
130   - private Integer auditStatus;
131   -
132 123 @Schema(description = "产品列表")
133   - private List<Item> items;
  124 + private List<Product> products;
134 125  
135 126 @Schema(description = "产品列表")
136 127 @Data
137 128 @NoArgsConstructor
138 129 @AllArgsConstructor
139   - public static class Item {
  130 + public static class Product {
140 131  
141   - @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20529")
  132 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "888")
142 133 private Long id;
143 134  
144   - @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是产品")
145   - private String name;
146   -
147   - @Schema(description = "产品编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "N881")
148   - private String no;
  135 + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20529")
  136 + private Long productId;
  137 + @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
  138 + private String productName;
  139 + @Schema(description = "产品条码", requiredMode = Schema.RequiredMode.REQUIRED, example = "20529")
  140 + private String productNo;
  141 + @Schema(description = "产品单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
  142 + private Integer productUnit;
149 143  
150   - @Schema(description = "单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
151   - private Integer unit;
  144 + @Schema(description = "产品单价", requiredMode = Schema.RequiredMode.REQUIRED, example = "123.00")
  145 + private BigDecimal productPrice;
152 146  
153   - @Schema(description = "价格,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
154   - private Integer price;
  147 + @Schema(description = "合同价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "123.00")
  148 + private BigDecimal contractPrice;
155 149  
156   - @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
157   - private Integer count;
  150 + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911")
  151 + private BigDecimal count;
158 152  
159   - @Schema(description = "产品折扣", example = "99")
160   - private Integer discountPercent;
  153 + @Schema(description = "总计价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "123.00")
  154 + private BigDecimal totalPrice;
161 155  
162 156 }
163 157  
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractSaveReqVO.java
... ... @@ -12,6 +12,7 @@ import lombok.Data;
12 12 import lombok.NoArgsConstructor;
13 13 import org.springframework.format.annotation.DateTimeFormat;
14 14  
  15 +import java.math.BigDecimal;
15 16 import java.time.LocalDateTime;
16 17 import java.util.List;
17 18  
... ... @@ -38,22 +39,16 @@ public class CrmContractSaveReqVO {
38 39 @DiffLogField(name = "商机", function = CrmBusinessParseFunction.NAME)
39 40 private Long businessId;
40 41  
41   - @Schema(description = "下单日期", requiredMode = Schema.RequiredMode.REQUIRED)
42   - @DiffLogField(name = "下单日期")
43   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
44   - @NotNull(message = "下单日期不能为空")
45   - private LocalDateTime orderDate;
46   -
47 42 @Schema(description = "负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17144")
48 43 @DiffLogField(name = "负责人", function = SysAdminUserParseFunction.NAME)
49 44 @NotNull(message = "负责人不能为空")
50 45 private Long ownerUserId;
51 46  
52   - // TODO @芋艿:未来应该支持自动生成;
53   - @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20230101")
54   - @DiffLogField(name = "合同编号")
55   - @NotNull(message = "合同编号不能为空")
56   - private String no;
  47 + @Schema(description = "下单日期", requiredMode = Schema.RequiredMode.REQUIRED)
  48 + @DiffLogField(name = "下单日期")
  49 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  50 + @NotNull(message = "下单日期不能为空")
  51 + private LocalDateTime orderDate;
57 52  
58 53 @Schema(description = "开始时间")
59 54 @DiffLogField(name = "开始时间")
... ... @@ -65,21 +60,18 @@ public class CrmContractSaveReqVO {
65 60 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
66 61 private LocalDateTime endTime;
67 62  
68   - @Schema(description = "合同金额", example = "5617")
69   - @DiffLogField(name = "合同金额")
70   - private Integer price;
71   -
72   - @Schema(description = "整单折扣")
  63 + @Schema(description = "整单折扣", requiredMode = Schema.RequiredMode.REQUIRED, example = "55.00")
73 64 @DiffLogField(name = "整单折扣")
74   - private Integer discountPercent;
  65 + @NotNull(message = "整单折扣不能为空")
  66 + private BigDecimal discountPercent;
75 67  
76   - @Schema(description = "产品总金额", example = "19510")
77   - @DiffLogField(name = "产品总金额")
78   - private Integer productPrice;
  68 + @Schema(description = "合同金额", example = "5617")
  69 + @DiffLogField(name = "合同金额")
  70 + private BigDecimal totalPrice;
79 71  
80   - @Schema(description = "联系人编号", example = "18546")
81   - @DiffLogField(name = "联系人", function = CrmContactParseFunction.NAME)
82   - private Long contactId;
  72 + @Schema(description = "客户签约人编号", example = "18546")
  73 + @DiffLogField(name = "客户签约人", function = CrmContactParseFunction.NAME)
  74 + private Long signContactId;
83 75  
84 76 @Schema(description = "公司签约人", example = "14036")
85 77 @DiffLogField(name = "公司签约人", function = SysAdminUserParseFunction.NAME)
... ... @@ -89,27 +81,31 @@ public class CrmContractSaveReqVO {
89 81 @DiffLogField(name = "备注")
90 82 private String remark;
91 83  
92   -
93 84 @Schema(description = "产品列表")
94   - private List<CrmContractProductItem> productItems;
  85 + private List<Product> products;
95 86  
96 87 @Schema(description = "产品列表")
97 88 @Data
98 89 @NoArgsConstructor
99 90 @AllArgsConstructor
100   - public static class CrmContractProductItem {
  91 + public static class Product {
101 92  
102   - @Schema(description = "产品编号", example = "20529")
  93 + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20529")
103 94 @NotNull(message = "产品编号不能为空")
104   - private Long id;
  95 + private Long productId;
  96 +
  97 + @Schema(description = "产品单价", requiredMode = Schema.RequiredMode.REQUIRED, example = "123.00")
  98 + @NotNull(message = "产品单价不能为空")
  99 + private BigDecimal productPrice;
  100 +
  101 + @Schema(description = "合同价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "123.00")
  102 + @NotNull(message = "合同价格不能为空")
  103 + private BigDecimal contractPrice;
105 104  
106 105 @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911")
107 106 @NotNull(message = "产品数量不能为空")
108 107 private Integer count;
109 108  
110   - @Schema(description = "产品折扣")
111   - private Integer discountPercent;
112   -
113 109 }
114 110  
115 111 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/CrmContractConvert.java deleted
1   -package cn.iocoder.yudao.module.crm.convert.contract;
2   -
3   -import cn.hutool.core.collection.CollUtil;
4   -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
5   -import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6   -import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractRespVO;
7   -import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO;
8   -import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
9   -import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
10   -import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
11   -import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractProductDO;
12   -import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
13   -import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
14   -import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
15   -import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
16   -import org.mapstruct.Mapper;
17   -import org.mapstruct.Mapping;
18   -import org.mapstruct.factory.Mappers;
19   -
20   -import java.util.List;
21   -import java.util.Map;
22   -
23   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
24   -import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
25   -
26   -/**
27   - * 合同 Convert
28   - *
29   - * @author dhb52
30   - */
31   -@Mapper
32   -public interface CrmContractConvert {
33   -
34   - CrmContractConvert INSTANCE = Mappers.getMapper(CrmContractConvert.class);
35   -
36   - @Mapping(target = "bizId", source = "reqVO.id")
37   - CrmPermissionTransferReqBO convert(CrmContractTransferReqVO reqVO, Long userId);
38   -
39   - default List<CrmContractRespVO> convertList(List<CrmContractDO> contractList, Map<Long, AdminUserRespDTO> userMap,
40   - List<CrmCustomerDO> customerList, Map<Long, CrmContactDO> contactMap,
41   - Map<Long, CrmBusinessDO> businessMap, Map<Long, CrmContractProductDO> contractProductMap,
42   - List<CrmProductDO> productList) {
43   - List<CrmContractRespVO> respVOList = BeanUtils.toBean(contractList, CrmContractRespVO.class);
44   - // 拼接关联字段
45   - Map<Long, CrmCustomerDO> customerMap = convertMap(customerList, CrmCustomerDO::getId);
46   - respVOList.forEach(contract -> {
47   - findAndThen(userMap, contract.getOwnerUserId(), user -> contract.setOwnerUserName(user.getNickname()));
48   - findAndThen(userMap, Long.parseLong(contract.getCreator()), user -> contract.setCreatorName(user.getNickname()));
49   - findAndThen(userMap, contract.getSignUserId(), user -> contract.setSignUserName(user.getNickname()));
50   - findAndThen(customerMap, contract.getCustomerId(), customer -> contract.setCustomerName(customer.getName()));
51   - findAndThen(contactMap, contract.getContactId(), contact -> contract.setContactName(contact.getName()));
52   - findAndThen(businessMap, contract.getBusinessId(), business -> contract.setBusinessName(business.getName()));
53   - });
54   - if (CollUtil.isNotEmpty(respVOList) && respVOList.size() == 1) {
55   - setContractRespVOProductItems(respVOList.get(0), contractProductMap, productList);
56   - }
57   - return respVOList;
58   - }
59   -
60   - default void setContractRespVOProductItems(CrmContractRespVO respVO, Map<Long, CrmContractProductDO> contractProductMap,
61   - List<CrmProductDO> productList) {
62   - respVO.setItems(CollectionUtils.convertList(productList, product -> {
63   - CrmContractRespVO.Item productItemRespVO = BeanUtils.toBean(product, CrmContractRespVO.Item.class);
64   - findAndThen(contractProductMap, product.getId(), contractProduct ->
65   - productItemRespVO.setCount(contractProduct.getCount()).setDiscountPercent(contractProduct.getDiscountPercent()));
66   - return productItemRespVO;
67   - }));
68   - }
69   -
70   -}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessProductDO.java
... ... @@ -50,7 +50,7 @@ public class CrmBusinessProductDO extends BaseDO {
50 50 */
51 51 private BigDecimal productPrice;
52 52 /**
53   - * 合同价格, 单位:元
  53 + * 商机价格, 单位:元
54 54 */
55 55 private BigDecimal businessPrice;
56 56 /**
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractDO.java
... ... @@ -10,9 +10,9 @@ import com.baomidou.mybatisplus.annotation.TableId;
10 10 import com.baomidou.mybatisplus.annotation.TableName;
11 11 import lombok.*;
12 12  
  13 +import java.math.BigDecimal;
13 14 import java.time.LocalDateTime;
14 15  
15   -// TODO 芋艿:实体的梳理
16 16 /**
17 17 * CRM 合同 DO
18 18 *
... ... @@ -34,25 +34,38 @@ public class CrmContractDO extends BaseDO {
34 34 @TableId
35 35 private Long id;
36 36 /**
37   - * 合同编号
38   - */
39   - private String no;
40   - /**
41 37 * 合同名称
42 38 */
43 39 private String name;
44 40 /**
  41 + * 合同编号
  42 + */
  43 + private String no;
  44 + /**
45 45 * 客户编号
46 46 *
47 47 * 关联 {@link CrmCustomerDO#getId()}
48 48 */
49 49 private Long customerId;
50 50 /**
51   - * 商机编号
  51 + * 商机编号,非必须
52 52 *
53 53 * 关联 {@link CrmBusinessDO#getId()}
54 54 */
55 55 private Long businessId;
  56 +
  57 + /**
  58 + * 最后跟进时间
  59 + */
  60 + private LocalDateTime contactLastTime;
  61 +
  62 + /**
  63 + * 负责人的用户编号
  64 + *
  65 + * 关联 AdminUserDO 的 id 字段
  66 + */
  67 + private Long ownerUserId;
  68 +
56 69 /**
57 70 * 工作流编号
58 71 *
... ... @@ -60,6 +73,13 @@ public class CrmContractDO extends BaseDO {
60 73 */
61 74 private String processInstanceId;
62 75 /**
  76 + * 审批状态
  77 + *
  78 + * 枚举 {@link CrmAuditStatusEnum}
  79 + */
  80 + private Integer auditStatus;
  81 +
  82 + /**
63 83 * 下单日期
64 84 */
65 85 private LocalDateTime orderDate;
... ... @@ -72,50 +92,32 @@ public class CrmContractDO extends BaseDO {
72 92 */
73 93 private LocalDateTime endTime;
74 94 /**
75   - * 合同金额,单位:分
  95 + * 产品总金额,单位:元
76 96 */
77   - private Integer price;
  97 + private BigDecimal totalProductPrice;
78 98 /**
79 99 * 整单折扣
80 100 */
81   - private Integer discountPercent;
  101 + private BigDecimal discountPercent;
82 102 /**
83   - * 产品总金额,单位:分
  103 + * 合同总金额,单位:分
84 104 */
85   - private Integer productPrice;
  105 + private BigDecimal totalPrice;
86 106 /**
87   - * 客户签约人
  107 + * 客户签约人,非必须
88 108 *
89 109 * 关联 {@link CrmContactDO#getId()}
90 110 */
91   - private Long contactId;
  111 + private Long signContactId;
92 112 /**
93   - * 公司签约人
  113 + * 公司签约人,非必须
94 114 *
95 115 * 关联 AdminUserDO 的 id 字段
96 116 */
97 117 private Long signUserId;
98 118 /**
99   - * 最后跟进时间
100   - */
101   - private LocalDateTime contactLastTime;
102   - /**
103 119 * 备注
104 120 */
105 121 private String remark;
106 122  
107   - /**
108   - * 负责人的用户编号
109   - *
110   - * 关联 AdminUserDO 的 id 字段
111   - */
112   - private Long ownerUserId;
113   -
114   - /**
115   - * 审批状态
116   - *
117   - * 枚举 {@link CrmAuditStatusEnum}
118   - */
119   - private Integer auditStatus;
120   -
121 123 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractProductDO.java
... ... @@ -7,8 +7,10 @@ import com.baomidou.mybatisplus.annotation.TableId;
7 7 import com.baomidou.mybatisplus.annotation.TableName;
8 8 import lombok.*;
9 9  
  10 +import java.math.BigDecimal;
  11 +
10 12 /**
11   - * 合同产品关联表 DO
  13 + * CRM 合同产品关联表 DO
12 14 *
13 15 * @author HUIHUI
14 16 */
... ... @@ -28,39 +30,34 @@ public class CrmContractProductDO extends BaseDO {
28 30 @TableId
29 31 private Long id;
30 32 /**
31   - * 产品编号
32   - *
33   - * 关联 {@link CrmProductDO#getId()}
34   - */
35   - private Long productId;
36   - /**
37 33 * 合同编号
38 34 *
39 35 * 关联 {@link CrmContractDO#getId()}
40 36 */
41 37 private Long contractId;
42 38 /**
43   - * 产品单价
  39 + * 产品编号
  40 + *
  41 + * 关联 {@link CrmProductDO#getId()}
44 42 */
45   - private Integer price;
  43 + private Long productId;
46 44 /**
47   - * 销售价格, 单位:分
  45 + * 产品单价,单位:元
48 46 */
49   - private Integer salesPrice;
  47 + private BigDecimal productPrice;
50 48 /**
51   - * 数量
  49 + * 合同价格, 单位:分
52 50 */
53   - private Integer count;
  51 + private BigDecimal contractPrice;
54 52 /**
55   - * 折扣
  53 + * 数量
56 54 */
57   - private Integer discountPercent;
  55 + private BigDecimal count;
58 56 /**
59   - * 总计价格(折扣后价格)
60   - * = {@link #price}
61   - * * {@link #count}
62   - * * ({@link #discountPercent / 100})
  57 + * 总计价格,单位:元
  58 + *
  59 + * totalPrice = businessPrice * count
63 60 */
64   - private Integer totalPrice;
  61 + private BigDecimal totalPrice;
65 62  
66 63 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java
... ... @@ -81,7 +81,7 @@ public interface CrmContractMapper extends BaseMapperX&lt;CrmContractDO&gt; {
81 81 }
82 82  
83 83 default Long selectCountByContactId(Long contactId) {
84   - return selectCount(CrmContractDO::getContactId, contactId);
  84 + return selectCount(CrmContractDO::getSignContactId, contactId);
85 85 }
86 86  
87 87 default Long selectCountByBusinessId(Long businessId) {
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessService.java
... ... @@ -11,7 +11,6 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
11 11 import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
12 12 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
13 13 import cn.iocoder.yudao.module.crm.enums.business.CrmBusinessEndStatusEnum;
14   -import cn.iocoder.yudao.module.crm.service.business.bo.CrmBusinessUpdateProductReqBO;
15 14 import jakarta.validation.Valid;
16 15  
17 16 import java.time.LocalDateTime;
... ... @@ -81,19 +80,20 @@ public interface CrmBusinessService {
81 80 void transferBusiness(CrmBusinessTransferReqVO reqVO, Long userId);
82 81  
83 82 /**
84   - * 更新商机关联商品
  83 + * 获得商机
85 84 *
86   - * @param updateProductReqBO 请求
  85 + * @param id 编号
  86 + * @return 商机
87 87 */
88   - void updateBusinessProduct(CrmBusinessUpdateProductReqBO updateProductReqBO);
  88 + CrmBusinessDO getBusiness(Long id);
89 89  
90 90 /**
91   - * 获得商机
  91 + * 校验商机是否有效
92 92 *
93 93 * @param id 编号
94 94 * @return 商机
95 95 */
96   - CrmBusinessDO getBusiness(Long id);
  96 + CrmBusinessDO validateBusiness(Long id);
97 97  
98 98 /**
99 99 * 获得商机列表
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java
... ... @@ -19,7 +19,6 @@ import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessProductMapper;
19 19 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
20 20 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
21 21 import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
22   -import cn.iocoder.yudao.module.crm.service.business.bo.CrmBusinessUpdateProductReqBO;
23 22 import cn.iocoder.yudao.module.crm.service.contact.CrmContactBusinessService;
24 23 import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
25 24 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
... ... @@ -91,10 +90,10 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
91 90 // 1.1 校验产品项的有效性
92 91 List<CrmBusinessProductDO> businessProducts = validateBusinessProducts(createReqVO.getProducts());
93 92 // 1.2 校验关联字段
94   - validateBusinessForCreate(createReqVO);
  93 + validateRelationDataExists(createReqVO);
95 94  
96 95 // 2.1 插入商机
97   - CrmBusinessDO business = BeanUtils.toBean(createReqVO, CrmBusinessDO.class).setOwnerUserId(userId);
  96 + CrmBusinessDO business = BeanUtils.toBean(createReqVO, CrmBusinessDO.class);
98 97 business.setStatusId(businessStatusService.getBusinessStatusListByTypeId(createReqVO.getStatusTypeId()).get(0).getId()); // 默认状态
99 98 calculateTotalPrice(business, businessProducts);
100 99 businessMapper.insert(business);
... ... @@ -105,9 +104,9 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
105 104 }
106 105  
107 106 // 3. 创建数据权限
108   - // 设置当前操作的人为负责人
109   - permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_BUSINESS.getType())
110   - .setBizId(business.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
  107 + permissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(business.getOwnerUserId())
  108 + .setBizType(CrmBizTypeEnum.CRM_BUSINESS.getType()).setBizId(business.getId())
  109 + .setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
111 110  
112 111 // 4. 在联系人的详情页,如果直接【新建商机】,则需要关联下
113 112 if (createReqVO.getContactId() != null) {
... ... @@ -132,7 +131,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
132 131 // 1.2 校验产品项的有效性
133 132 List<CrmBusinessProductDO> businessProducts = validateBusinessProducts(updateReqVO.getProducts());
134 133 // 1.3 校验关联字段
135   - validateBusinessForCreate(updateReqVO);
  134 + validateRelationDataExists(updateReqVO);
136 135  
137 136 // 2.1 更新商机
138 137 CrmBusinessDO updateObj = BeanUtils.toBean(updateReqVO, CrmBusinessDO.class);
... ... @@ -141,7 +140,6 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
141 140 // 2.2 更新商机关联商品
142 141 updateBusinessProduct(updateObj.getId(), businessProducts);
143 142  
144   - // TODO @商机待定:如果状态发生变化,插入商机状态变更记录表
145 143 // 3. 记录操作日志上下文
146 144 LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldBusiness, CrmBusinessSaveReqVO.class));
147 145 LogRecordContext.putVariable("businessName", oldBusiness.getName());
... ... @@ -185,7 +183,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
185 183 }
186 184 }
187 185  
188   - private void validateBusinessForCreate(CrmBusinessSaveReqVO saveReqVO) {
  186 + private void validateRelationDataExists(CrmBusinessSaveReqVO saveReqVO) {
189 187 // 校验商机状态
190 188 if (saveReqVO.getStatusTypeId() != null) {
191 189 businessStatusService.validateBusinessStatusType(saveReqVO.getStatusTypeId());
... ... @@ -208,9 +206,8 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
208 206 // 1. 校验产品存在
209 207 productService.validProductList(convertSet(list, CrmBusinessSaveReqVO.Product::getProductId));
210 208 // 2. 转化为 CrmBusinessProductDO 列表
211   - return convertList(list, o -> BeanUtils.toBean(o, CrmBusinessProductDO.class, item -> {
212   - item.setTotalPrice(MoneyUtils.priceMultiply(item.getBusinessPrice(), item.getCount()));
213   - }));
  209 + return convertList(list, o -> BeanUtils.toBean(o, CrmBusinessProductDO.class,
  210 + item -> item.setTotalPrice(MoneyUtils.priceMultiply(item.getBusinessPrice(), item.getCount()))));
214 211 }
215 212  
216 213 private void calculateTotalPrice(CrmBusinessDO business, List<CrmBusinessProductDO> businessProducts) {
... ... @@ -219,7 +216,6 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
219 216 business.setTotalPrice(business.getTotalProductPrice().subtract(discountPrice));
220 217 }
221 218  
222   -
223 219 @Override
224 220 @LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_UPDATE_STATUS_SUB_TYPE, bizNo = "{{#reqVO.id}}",
225 221 success = CRM_BUSINESS_UPDATE_STATUS_SUCCESS)
... ... @@ -264,7 +260,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
264 260 // 1.2 校验是否关联合同
265 261 validateContractExists(id);
266 262  
267   - // 删除
  263 + // 删除商机
268 264 businessMapper.deleteById(id);
269 265 // 删除数据权限
270 266 permissionService.deletePermission(CrmBizTypeEnum.CRM_BUSINESS.getType(), id);
... ... @@ -313,14 +309,6 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
313 309 LogRecordContext.putVariable("business", business);
314 310 }
315 311  
316   - @Override
317   - public void updateBusinessProduct(CrmBusinessUpdateProductReqBO updateProductReqBO) {
318   - // 更新商机关联商品 TODO yunai
319   -// List<CrmBusinessProductDO> productList = buildBusinessProductList(
320   -// BeanUtils.toBean(updateProductReqBO.getProductItems(), CrmBusinessSaveReqVO.Product.class), updateProductReqBO.getId());
321   -// updateBusinessProduct(productList, updateProductReqBO.getId());
322   - }
323   -
324 312 //======================= 查询相关 =======================
325 313  
326 314 @Override
... ... @@ -330,6 +318,11 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
330 318 }
331 319  
332 320 @Override
  321 + public CrmBusinessDO validateBusiness(Long id) {
  322 + return validateBusinessExists(id);
  323 + }
  324 +
  325 + @Override
333 326 public List<CrmBusinessDO> getBusinessList(Collection<Long> ids, Long userId) {
334 327 if (CollUtil.isEmpty(ids)) {
335 328 return ListUtil.empty();
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/bo/CrmBusinessUpdateProductReqBO.java deleted
1   -package cn.iocoder.yudao.module.crm.service.business.bo;
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.AllArgsConstructor;
7   -import lombok.Data;
8   -import lombok.NoArgsConstructor;
9   -
10   -import java.util.List;
11   -
12   -/**
13   - * 更新商机商品 Update Req BO
14   - *
15   - * @author HUIHUI
16   - */
17   -@Data
18   -public class CrmBusinessUpdateProductReqBO {
19   -
20   - /**
21   - * 商机编号
22   - */
23   - @NotNull(message = "商机编号不能为空")
24   - private Long id;
25   -
26   - // TODO @芋艿:再想想
27   - @NotEmpty(message = "产品列表不能为空")
28   - private List<Item> items;
29   -
30   - @Schema(description = "产品列表")
31   - @Data
32   - @NoArgsConstructor
33   - @AllArgsConstructor
34   - public static class Item {
35   -
36   - @Schema(description = "产品编号", example = "20529")
37   - @NotNull(message = "产品编号不能为空")
38   - private Long id;
39   -
40   - @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911")
41   - @NotNull(message = "产品数量不能为空")
42   - private Integer count;
43   -
44   - @Schema(description = "产品折扣")
45   - private Integer discountPercent;
46   -
47   - }
48   -
49   -}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java
... ... @@ -19,7 +19,6 @@ import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
19 19 import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO;
20 20 import cn.iocoder.yudao.module.crm.service.followup.CrmFollowUpRecordService;
21 21 import cn.iocoder.yudao.module.crm.service.followup.bo.CrmFollowUpCreateReqBO;
22   -import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
23 22 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
24 23 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
25 24 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
... ... @@ -133,7 +132,6 @@ public class CrmClueServiceImpl implements CrmClueService {
133 132 .setContactLastTime(LocalDateTime.now()).setContactLastContent(contactLastContent));
134 133  
135 134 // 3. 记录操作日志上下文
136   - LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldClue, CrmUpdateFollowUpReqBO.class));
137 135 LogRecordContext.putVariable("clueName", oldClue.getName());
138 136 }
139 137  
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java
... ... @@ -8,9 +8,9 @@ import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTrans
8 8 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
9 9 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractProductDO;
10 10 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
11   -import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
12 11 import jakarta.validation.Valid;
13 12  
  13 +import java.time.LocalDateTime;
14 14 import java.util.Collection;
15 15 import java.util.List;
16 16  
... ... @@ -55,9 +55,11 @@ public interface CrmContractService {
55 55 /**
56 56 * 更新合同相关的更进信息
57 57 *
58   - * @param contractUpdateFollowUpReqBO 信息
  58 + * @param id 合同编号
  59 + * @param contactNextTime 下次联系时间
  60 + * @param contactLastContent 最后联系内容
59 61 */
60   - void updateContractFollowUp(CrmUpdateFollowUpReqBO contractUpdateFollowUpReqBO);
  62 + void updateContractFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent);
61 63  
62 64 /**
63 65 * 发起合同审批流程
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
... ... @@ -11,13 +11,12 @@ import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
11 11 import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO;
12 12 import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
13 13 import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
  14 +import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
14 15 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO;
15 16 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO;
16 17 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO;
17   -import cn.iocoder.yudao.module.crm.convert.contract.CrmContractConvert;
18 18 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
19 19 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractProductDO;
20   -import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
21 20 import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractMapper;
22 21 import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractProductMapper;
23 22 import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
... ... @@ -25,11 +24,11 @@ import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
25 24 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
26 25 import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
27 26 import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
28   -import cn.iocoder.yudao.module.crm.service.business.bo.CrmBusinessUpdateProductReqBO;
  27 +import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
29 28 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
30   -import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
31 29 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
32 30 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
  31 +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
33 32 import cn.iocoder.yudao.module.crm.service.product.CrmProductService;
34 33 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
35 34 import com.mzt.logapi.context.LogRecordContext;
... ... @@ -40,17 +39,15 @@ import org.springframework.stereotype.Service;
40 39 import org.springframework.transaction.annotation.Transactional;
41 40 import org.springframework.validation.annotation.Validated;
42 41  
  42 +import java.math.BigDecimal;
  43 +import java.time.LocalDateTime;
43 44 import java.util.Collection;
44 45 import java.util.List;
45   -import java.util.Map;
46   -import java.util.Set;
47 46  
48 47 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
49 48 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
50 49 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
51 50 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
52   -import static cn.iocoder.yudao.module.crm.util.CrmAuditStatusUtils.convertAuditStatus;
53   -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_NOT_EXISTS;
54 51  
55 52 /**
56 53 * CRM 合同 Service 实现类
... ... @@ -79,6 +76,8 @@ public class CrmContractServiceImpl implements CrmContractService {
79 76 private CrmCustomerService customerService;
80 77 @Resource
81 78 private CrmBusinessService businessService;
  79 + @Resource
  80 + private CrmContactService contactService;
82 81  
83 82 @Resource
84 83 private AdminUserApi adminUserApi;
... ... @@ -90,30 +89,29 @@ public class CrmContractServiceImpl implements CrmContractService {
90 89 @LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_CREATE_SUB_TYPE, bizNo = "{{#contract.id}}",
91 90 success = CRM_CONTRACT_CREATE_SUCCESS)
92 91 public Long createContract(CrmContractSaveReqVO createReqVO, Long userId) {
  92 + // 1.1 校验产品项的有效性
  93 + List<CrmContractProductDO> contractProducts = validateContractProducts(createReqVO.getProducts());
  94 + // 1.2 校验关联字段
93 95 validateRelationDataExists(createReqVO);
94   - // 1.1 插入合同
95   - CrmContractDO contract = BeanUtils.toBean(createReqVO, CrmContractDO.class).setId(null);
  96 + // TODO 芋艿:生成 no
  97 +
  98 + // 2.1 插入合同
  99 + CrmContractDO contract = BeanUtils.toBean(createReqVO, CrmContractDO.class);
  100 + contract.setNo(System.currentTimeMillis() + ""); // TODO
  101 + calculateTotalPrice(contract, contractProducts);
96 102 contractMapper.insert(contract);
97   - // 1.2 插入合同关联商品
98   - if (CollUtil.isNotEmpty(createReqVO.getProductItems())) { // 如果有的话
99   - List<CrmContractProductDO> productList = convertContractProductList(createReqVO, contract.getId());
100   - contractProductMapper.insertBatch(productList);
101   - // 更新合同商品总金额
102   - contractMapper.updateById(new CrmContractDO().setId(contract.getId()).setProductPrice(
103   - getSumValue(productList, CrmContractProductDO::getTotalPrice, Integer::sum)));
104   - // 如果存在合同关联了商机则更新商机商品关联
105   - if (contract.getBusinessId() != null) {
106   - businessService.updateBusinessProduct(new CrmBusinessUpdateProductReqBO().setId(contract.getBusinessId())
107   - .setItems(BeanUtils.toBean(createReqVO.getProductItems(), CrmBusinessUpdateProductReqBO.Item.class)));
108   - }
  103 + // 2.2 插入合同关联商品
  104 + if (CollUtil.isNotEmpty(contractProducts)) {
  105 + contractProducts.forEach(item -> item.setContractId(contract.getId()));
  106 + contractProductMapper.insertBatch(contractProducts);
109 107 }
110 108  
111   - // 2. 创建数据权限
112   - crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(userId)
  109 + // 3. 创建数据权限
  110 + crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(contract.getOwnerUserId())
113 111 .setBizType(CrmBizTypeEnum.CRM_CONTRACT.getType()).setBizId(contract.getId())
114 112 .setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
115 113  
116   - // 3. 记录操作日志上下文
  114 + // 4. 记录操作日志上下文
117 115 LogRecordContext.putVariable("contract", contract);
118 116 return contract.getId();
119 117 }
... ... @@ -125,6 +123,7 @@ public class CrmContractServiceImpl implements CrmContractService {
125 123 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
126 124 public void updateContract(CrmContractSaveReqVO updateReqVO) {
127 125 Assert.notNull(updateReqVO.getId(), "合同编号不能为空");
  126 + updateReqVO.setOwnerUserId(null); // 不允许更新的字段
128 127 // 1.1 校验存在
129 128 CrmContractDO contract = validateContractExists(updateReqVO.getId());
130 129 // 1.2 只有草稿、审批中,可以编辑;
... ... @@ -132,63 +131,41 @@ public class CrmContractServiceImpl implements CrmContractService {
132 131 CrmAuditStatusEnum.PROCESS.getStatus())) {
133 132 throw exception(CONTRACT_UPDATE_FAIL_EDITING_PROHIBITED);
134 133 }
  134 + // 1.3 校验产品项的有效性
  135 + List<CrmContractProductDO> contractProducts = validateContractProducts(updateReqVO.getProducts());
  136 + // 1.4 校验关联字段
135 137 validateRelationDataExists(updateReqVO);
136 138  
137 139 // 2.1 更新合同
138 140 CrmContractDO updateObj = BeanUtils.toBean(updateReqVO, CrmContractDO.class);
  141 + calculateTotalPrice(updateObj, contractProducts);
139 142 contractMapper.updateById(updateObj);
140 143 // 2.2 更新合同关联商品
141   - updateContractProduct(updateReqVO, updateObj.getId());
  144 + updateContractProduct(updateReqVO.getId(), contractProducts);
142 145  
143 146 // 3. 记录操作日志上下文
144 147 LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(contract, CrmContractSaveReqVO.class));
145 148 LogRecordContext.putVariable("contractName", contract.getName());
146 149 }
147 150  
148   - private void updateContractProduct(CrmContractSaveReqVO updateReqVO, Long contractId) {
149   - if (CollUtil.isEmpty(updateReqVO.getProductItems())) {
150   - return;
151   - }
152   - List<CrmContractProductDO> newProductList = convertContractProductList(updateReqVO, contractId);
153   - List<CrmContractProductDO> oldProductList = contractProductMapper.selectListByContractId(contractId);
154   - List<List<CrmContractProductDO>> diffList = diffList(oldProductList, newProductList, (oldObj, newObj) -> {
155   - boolean match = ObjUtil.equal(oldObj.getProductId(), newObj.getProductId());
156   - if (match) {
157   - newObj.setId(oldObj.getId()); // 设置一下老的编号更新时需要使用
158   - }
159   - return match;
160   - });
  151 + private void updateContractProduct(Long id, List<CrmContractProductDO> newList) {
  152 + List<CrmContractProductDO> oldList = contractProductMapper.selectListByContractId(id);
  153 + List<List<CrmContractProductDO>> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录
  154 + (oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));
161 155 if (CollUtil.isNotEmpty(diffList.get(0))) {
  156 + diffList.get(0).forEach(o -> o.setContractId(id));
162 157 contractProductMapper.insertBatch(diffList.get(0));
163 158 }
164 159 if (CollUtil.isNotEmpty(diffList.get(1))) {
165 160 contractProductMapper.updateBatch(diffList.get(1));
166 161 }
167 162 if (CollUtil.isNotEmpty(diffList.get(2))) {
168   - contractProductMapper.deleteBatchIds(convertList(diffList.get(2), CrmContractProductDO::getId));
  163 + contractProductMapper.deleteBatchIds(convertSet(diffList.get(2), CrmContractProductDO::getId));
169 164 }
170 165 }
171 166  
172 167 // TODO @合同待定:缺一个取消合同的接口;只有草稿、审批中可以取消;CrmAuditStatusEnum
173 168  
174   - private List<CrmContractProductDO> convertContractProductList(CrmContractSaveReqVO reqVO, Long contractId) {
175   - // 校验商品存在
176   - Set<Long> productIds = convertSet(reqVO.getProductItems(), CrmContractSaveReqVO.CrmContractProductItem::getId);
177   - List<CrmProductDO> productList = productService.getProductList(productIds);
178   - if (CollUtil.isEmpty(productIds) || productList.size() != productIds.size()) {
179   - throw exception(PRODUCT_NOT_EXISTS);
180   - }
181   - Map<Long, CrmProductDO> productMap = convertMap(productList, CrmProductDO::getId);
182   - return convertList(reqVO.getProductItems(), productItem -> {
183   - CrmProductDO product = productMap.get(productItem.getId());
184   - return BeanUtils.toBean(product, CrmContractProductDO.class)
185   - .setId(null).setProductId(productItem.getId()).setContractId(contractId)
186   - .setCount(productItem.getCount()).setDiscountPercent(productItem.getDiscountPercent())
187   - // TODO 芋艿:这里临时注释掉
188   - .setTotalPrice(MoneyUtils.calculator(null, productItem.getCount(), productItem.getDiscountPercent()));
189   - });
190   - }
191   -
192 169 /**
193 170 * 校验关联数据是否存在
194 171 *
... ... @@ -196,17 +173,38 @@ public class CrmContractServiceImpl implements CrmContractService {
196 173 */
197 174 private void validateRelationDataExists(CrmContractSaveReqVO reqVO) {
198 175 // 1. 校验客户
199   - if (reqVO.getCustomerId() != null && customerService.getCustomer(reqVO.getCustomerId()) == null) {
200   - throw exception(CUSTOMER_NOT_EXISTS);
  176 + if (reqVO.getCustomerId() != null) {
  177 + customerService.validateCustomer(reqVO.getCustomerId());
201 178 }
202 179 // 2. 校验负责人
203   - if (reqVO.getOwnerUserId() != null && adminUserApi.getUser(reqVO.getOwnerUserId()) == null) {
204   - throw exception(USER_NOT_EXISTS);
  180 + if (reqVO.getOwnerUserId() != null) {
  181 + adminUserApi.validateUser(reqVO.getOwnerUserId());
205 182 }
206 183 // 3. 如果有关联商机,则需要校验存在
207   - if (reqVO.getBusinessId() != null && businessService.getBusiness(reqVO.getBusinessId()) == null) {
208   - throw exception(BUSINESS_NOT_EXISTS);
  184 + if (reqVO.getBusinessId() != null) {
  185 + businessService.validateBusiness(reqVO.getBusinessId());
209 186 }
  187 + // 4. 校验签约相关字段
  188 + if (reqVO.getSignContactId() != null) {
  189 + contactService.validateContact(reqVO.getSignContactId());
  190 + }
  191 + if (reqVO.getSignUserId() != null) {
  192 + adminUserApi.validateUser(reqVO.getSignUserId());
  193 + }
  194 + }
  195 +
  196 + private List<CrmContractProductDO> validateContractProducts(List<CrmContractSaveReqVO.Product> list) {
  197 + // 1. 校验产品存在
  198 + productService.validProductList(convertSet(list, CrmContractSaveReqVO.Product::getProductId));
  199 + // 2. 转化为 CrmContractProductDO 列表
  200 + return convertList(list, o -> BeanUtils.toBean(o, CrmContractProductDO.class,
  201 + item -> item.setTotalPrice(MoneyUtils.priceMultiply(item.getContractPrice(), item.getCount()))));
  202 + }
  203 +
  204 + private void calculateTotalPrice(CrmContractDO contract, List<CrmContractProductDO> contractProducts) {
  205 + contract.setTotalProductPrice(getSumValue(contractProducts, CrmContractProductDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO));
  206 + BigDecimal discountPrice = MoneyUtils.priceMultiplyPercent(contract.getTotalProductPrice(), contract.getDiscountPercent());
  207 + contract.setTotalPrice(contract.getTotalProductPrice().subtract(discountPrice));
210 208 }
211 209  
212 210 @Override
... ... @@ -245,8 +243,8 @@ public class CrmContractServiceImpl implements CrmContractService {
245 243 CrmContractDO contract = validateContractExists(reqVO.getId());
246 244  
247 245 // 2.1 数据权限转移
248   - crmPermissionService.transferPermission(
249   - CrmContractConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CONTRACT.getType()));
  246 + crmPermissionService.transferPermission(new CrmPermissionTransferReqBO(userId, CrmBizTypeEnum.CRM_CONTRACT.getType(),
  247 + reqVO.getId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel()));
250 248 // 2.2 设置负责人
251 249 contractMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
252 250  
... ... @@ -255,8 +253,18 @@ public class CrmContractServiceImpl implements CrmContractService {
255 253 }
256 254  
257 255 @Override
258   - public void updateContractFollowUp(CrmUpdateFollowUpReqBO contractUpdateFollowUpReqBO) {
259   - contractMapper.updateById(BeanUtils.toBean(contractUpdateFollowUpReqBO, CrmContractDO.class).setId(contractUpdateFollowUpReqBO.getBizId()));
  256 + @LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_FOLLOW_UP_SUB_TYPE, bizNo = "{{#id}",
  257 + success = CRM_CONTRACT_FOLLOW_UP_SUCCESS)
  258 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#id", level = CrmPermissionLevelEnum.WRITE)
  259 + public void updateContractFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent) {
  260 + // 1. 校验存在
  261 + CrmContractDO contract = validateContractExists(id);
  262 +
  263 + // 2. 更新联系人的跟进信息
  264 + contractMapper.updateById(new CrmContractDO().setId(id).setContactLastTime(LocalDateTime.now()));
  265 +
  266 + // 3. 记录操作日志上下文
  267 + LogRecordContext.putVariable("contractName", contract.getName());
260 268 }
261 269  
262 270 @Override
... ... @@ -284,12 +292,36 @@ public class CrmContractServiceImpl implements CrmContractService {
284 292  
285 293 @Override
286 294 public void updateContractAuditStatus(BpmResultListenerRespDTO event) {
287   - convertAuditStatus(event);
  295 + // 判断下状态是否符合预期
  296 + if (!isEndResult(event.getResult())) {
  297 + return;
  298 + }
  299 + // 状态转换
  300 + if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.APPROVE.getResult())) {
  301 + event.setResult(CrmAuditStatusEnum.APPROVE.getStatus());
  302 + }
  303 + if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.REJECT.getResult())) {
  304 + event.setResult(CrmAuditStatusEnum.REJECT.getStatus());
  305 + }
  306 + if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.CANCEL.getResult())) {
  307 + event.setResult(CrmAuditStatusEnum.CANCEL.getStatus());
  308 + }
288 309 // 更新合同状态
289 310 contractMapper.updateById(new CrmContractDO().setId(Long.parseLong(event.getBusinessKey()))
290 311 .setAuditStatus(event.getResult()));
291 312 }
292 313  
  314 + /**
  315 + * 判断该结果是否处于 End 最终结果
  316 + *
  317 + * @param result 结果
  318 + * @return 是否
  319 + */
  320 + public static boolean isEndResult(Integer result) {
  321 + return ObjectUtils.equalsAny(result, BpmProcessInstanceResultEnum.APPROVE.getResult(),
  322 + BpmProcessInstanceResultEnum.REJECT.getResult(), BpmProcessInstanceResultEnum.CANCEL.getResult());
  323 + }
  324 +
293 325 //======================= 查询相关 =======================
294 326  
295 327 @Override
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordServiceImpl.java
... ... @@ -17,14 +17,12 @@ import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
17 17 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
18 18 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
19 19 import cn.iocoder.yudao.module.crm.service.followup.bo.CrmFollowUpCreateReqBO;
20   -import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
21 20 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
22 21 import jakarta.annotation.Resource;
23 22 import org.springframework.context.annotation.Lazy;
24 23 import org.springframework.stereotype.Service;
25 24 import org.springframework.validation.annotation.Validated;
26 25  
27   -import java.time.LocalDateTime;
28 26 import java.util.Collection;
29 27 import java.util.List;
30 28  
... ... @@ -71,9 +69,6 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService {
71 69 crmFollowUpRecordMapper.insert(record);
72 70  
73 71 // 2. 更新 bizId 对应的记录
74   - CrmUpdateFollowUpReqBO updateFollowUpReqBO = new CrmUpdateFollowUpReqBO().setBizId(record.getBizId())
75   - .setContactLastTime(LocalDateTime.now())
76   - .setContactNextTime(record.getNextTime()).setContactLastContent(record.getContent());
77 72 if (ObjUtil.equal(CrmBizTypeEnum.CRM_BUSINESS.getType(), record.getBizType())) { // 更新商机跟进信息
78 73 businessService.updateBusinessFollowUp(record.getBizId(), record.getNextTime(), record.getContent());
79 74 }
... ... @@ -84,7 +79,7 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService {
84 79 contactService.updateContactFollowUp(record.getBizId(), record.getNextTime(), record.getContent());
85 80 }
86 81 if (ObjUtil.equal(CrmBizTypeEnum.CRM_CONTRACT.getType(), record.getBizType())) { // 更新合同跟进信息
87   - contractService.updateContractFollowUp(updateFollowUpReqBO);
  82 + contractService.updateContractFollowUp(record.getBizId(), record.getNextTime(), record.getContent());
88 83 }
89 84 if (ObjUtil.equal(CrmBizTypeEnum.CRM_CUSTOMER.getType(), record.getBizType())) { // 更新客户跟进信息
90 85 customerService.updateCustomerFollowUp(record.getBizId(), record.getNextTime(), record.getContent());
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/bo/CrmUpdateFollowUpReqBO.java deleted
1   -package cn.iocoder.yudao.module.crm.service.followup.bo;
2   -
3   -import com.mzt.logapi.starter.annotation.DiffLogField;
4   -import io.swagger.v3.oas.annotations.media.Schema;
5   -import jakarta.validation.constraints.NotNull;
6   -import lombok.Data;
7   -
8   -import java.time.LocalDateTime;
9   -
10   -/**
11   - * 跟进信息 Update Req BO
12   - *
13   - * @author HUIHUI
14   - */
15   -@Data
16   -public class CrmUpdateFollowUpReqBO {
17   -
18   - @Schema(description = "数据编号", example = "3167")
19   - @NotNull(message = "数据编号不能为空")
20   - private Long bizId;
21   -
22   - @Schema(description = "最后跟进时间")
23   - @DiffLogField(name = "最后跟进时间")
24   - private LocalDateTime contactLastTime;
25   -
26   - @Schema(description = "下次联系时间")
27   - @DiffLogField(name = "下次联系时间")
28   - private LocalDateTime contactNextTime;
29   -
30   - @Schema(description = "最后更进内容")
31   - @DiffLogField(name = "最后更进内容")
32   - private String contactLastContent;
33   -
34   -}