Commit e01dda9bafb906123fb4fdc9c951e5da5c40a53b

Authored by YunaiV
1 parent 7dd35f32

✨ CRM:完善商机的列表

Showing 28 changed files with 501 additions and 412 deletions
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
... ... @@ -67,6 +67,7 @@ public interface ErrorCodeConstants {
67 67 // ========== 产品 1_020_008_000 ==========
68 68 ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1_020_008_000, "产品不存在");
69 69 ErrorCode PRODUCT_NO_EXISTS = new ErrorCode(1_020_008_001, "产品编号已存在");
  70 + ErrorCode PRODUCT_NOT_ENABLE = new ErrorCode(1_020_008_002, "产品【{}】已禁用");
70 71  
71 72 // ========== 产品分类 1_020_009_000 ==========
72 73 ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_020_009_000, "产品分类不存在");
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java
... ... @@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.crm.controller.admin.business;
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
  7 +import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
6 8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
7 9 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
8 10 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
... ... @@ -10,15 +12,21 @@ import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusi
10 12 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessRespVO;
11 13 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessSaveReqVO;
12 14 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO;
13   -import cn.iocoder.yudao.module.crm.convert.business.CrmBusinessConvert;
14 15 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
  16 +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
15 17 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
16 18 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO;
17 19 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
  20 +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
18 21 import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
19 22 import cn.iocoder.yudao.module.crm.service.business.CrmBusinessStatusService;
20 23 import cn.iocoder.yudao.module.crm.service.business.CrmBusinessStatusTypeService;
21 24 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
  25 +import cn.iocoder.yudao.module.crm.service.product.CrmProductService;
  26 +import cn.iocoder.yudao.module.system.api.dept.DeptApi;
  27 +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
  28 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  29 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
22 30 import io.swagger.v3.oas.annotations.Operation;
23 31 import io.swagger.v3.oas.annotations.Parameter;
24 32 import io.swagger.v3.oas.annotations.tags.Tag;
... ... @@ -30,13 +38,15 @@ import org.springframework.validation.annotation.Validated;
30 38 import org.springframework.web.bind.annotation.*;
31 39  
32 40 import java.io.IOException;
  41 +import java.util.Collections;
33 42 import java.util.List;
  43 +import java.util.Map;
  44 +import java.util.stream.Stream;
34 45  
35 46 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
36 47 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
37 48 import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
38   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
39   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  49 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
40 50 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
41 51 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
42 52 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CUSTOMER_NOT_EXISTS;
... ... @@ -55,6 +65,13 @@ public class CrmBusinessController {
55 65 private CrmBusinessStatusTypeService businessStatusTypeService;
56 66 @Resource
57 67 private CrmBusinessStatusService businessStatusService;
  68 + @Resource
  69 + private CrmProductService productService;
  70 +
  71 + @Resource
  72 + private AdminUserApi adminUserApi;
  73 + @Resource
  74 + private DeptApi deptApi;
58 75  
59 76 @PostMapping("/create")
60 77 @Operation(summary = "创建商机")
... ... @@ -86,9 +103,25 @@ public class CrmBusinessController {
86 103 @PreAuthorize("@ss.hasPermission('crm:business:query')")
87 104 public CommonResult<CrmBusinessRespVO> getBusiness(@RequestParam("id") Long id) {
88 105 CrmBusinessDO business = businessService.getBusiness(id);
89   - return success(BeanUtils.toBean(business, CrmBusinessRespVO.class));
  106 + return success(buildBusinessDetail(business));
  107 + }
  108 +
  109 + private CrmBusinessRespVO buildBusinessDetail(CrmBusinessDO business) {
  110 + if (business == null) {
  111 + return null;
  112 + }
  113 + CrmBusinessRespVO businessVO = buildBusinessDetailList(Collections.singletonList(business)).get(0);
  114 + // 拼接产品项
  115 + List<CrmBusinessProductDO> businessProducts = businessService.getBusinessProductListByBusinessId(businessVO.getId());
  116 + Map<Long, CrmProductDO> productMap = productService.getProductMap(
  117 + convertSet(businessProducts, CrmBusinessProductDO::getProductId));
  118 + businessVO.setProducts(BeanUtils.toBean(businessProducts, CrmBusinessRespVO.Product.class, businessProductVO ->
  119 + MapUtils.findAndThen(productMap, businessProductVO.getProductId(),
  120 + product -> businessProductVO.setProductNo(product.getNo()).setProductUnit(product.getUnit()))));
  121 + return businessVO;
90 122 }
91 123  
  124 + // TODO 芋艿:处理下
92 125 @GetMapping("/list-by-ids")
93 126 @Operation(summary = "获得商机列表")
94 127 @Parameter(name = "ids", description = "编号", required = true, example = "[1024]")
... ... @@ -97,6 +130,7 @@ public class CrmBusinessController {
97 130 return success(BeanUtils.toBean(businessService.getBusinessList(ids, getLoginUserId()), CrmBusinessRespVO.class));
98 131 }
99 132  
  133 + // TODO 芋艿:处理下
100 134 @GetMapping("/simple-all-list")
101 135 @Operation(summary = "获得联系人的精简列表")
102 136 @PreAuthorize("@ss.hasPermission('crm:contact:query')")
... ... @@ -113,7 +147,7 @@ public class CrmBusinessController {
113 147 @PreAuthorize("@ss.hasPermission('crm:business:query')")
114 148 public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessPage(@Valid CrmBusinessPageReqVO pageVO) {
115 149 PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPage(pageVO, getLoginUserId());
116   - return success(buildBusinessDetailPageResult(pageResult));
  150 + return success(new PageResult<>(buildBusinessDetailList(pageResult.getList()), pageResult.getTotal()));
117 151 }
118 152  
119 153 @GetMapping("/page-by-customer")
... ... @@ -123,7 +157,7 @@ public class CrmBusinessController {
123 157 throw exception(CUSTOMER_NOT_EXISTS);
124 158 }
125 159 PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPageByCustomerId(pageReqVO);
126   - return success(buildBusinessDetailPageResult(pageResult));
  160 + return success(new PageResult<>(buildBusinessDetailList(pageResult.getList()), pageResult.getTotal()));
127 161 }
128 162  
129 163 @GetMapping("/page-by-contact")
... ... @@ -131,7 +165,7 @@ public class CrmBusinessController {
131 165 @PreAuthorize("@ss.hasPermission('crm:business:query')")
132 166 public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessContactPage(@Valid CrmBusinessPageReqVO pageReqVO) {
133 167 PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPageByContact(pageReqVO);
134   - return success(buildBusinessDetailPageResult(pageResult));
  168 + return success(new PageResult<>(buildBusinessDetailList(pageResult.getList()), pageResult.getTotal()));
135 169 }
136 170  
137 171 @GetMapping("/export-excel")
... ... @@ -141,29 +175,43 @@ public class CrmBusinessController {
141 175 public void exportBusinessExcel(@Valid CrmBusinessPageReqVO exportReqVO,
142 176 HttpServletResponse response) throws IOException {
143 177 exportReqVO.setPageSize(PAGE_SIZE_NONE);
144   - PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPage(exportReqVO, getLoginUserId());
  178 + List<CrmBusinessDO> list = businessService.getBusinessPage(exportReqVO, getLoginUserId()).getList();
145 179 // 导出 Excel
146 180 ExcelUtils.write(response, "商机.xls", "数据", CrmBusinessRespVO.class,
147   - buildBusinessDetailPageResult(pageResult).getList());
  181 + buildBusinessDetailList(list));
148 182 }
149 183  
150   - /**
151   - * 构建详细的商机分页结果
152   - *
153   - * @param pageResult 简单的商机分页结果
154   - * @return 详细的商机分页结果
155   - */
156   - private PageResult<CrmBusinessRespVO> buildBusinessDetailPageResult(PageResult<CrmBusinessDO> pageResult) {
157   - if (CollUtil.isEmpty(pageResult.getList())) {
158   - return PageResult.empty(pageResult.getTotal());
  184 + private List<CrmBusinessRespVO> buildBusinessDetailList(List<CrmBusinessDO> list) {
  185 + if (CollUtil.isEmpty(list)) {
  186 + return Collections.emptyList();
159 187 }
160   - List<CrmBusinessStatusTypeDO> statusTypeList = businessStatusTypeService.getBusinessStatusTypeList(
161   - convertSet(pageResult.getList(), CrmBusinessDO::getStatusTypeId));
162   - List<CrmBusinessStatusDO> statusList = businessStatusService.getBusinessStatusList(
163   - convertSet(pageResult.getList(), CrmBusinessDO::getStatusId));
164   - List<CrmCustomerDO> customerList = customerService.getCustomerList(
165   - convertSet(pageResult.getList(), CrmBusinessDO::getCustomerId));
166   - return CrmBusinessConvert.INSTANCE.convertPage(pageResult, customerList, statusTypeList, statusList);
  188 + // 1.1 获取客户列表
  189 + Map<Long, CrmCustomerDO> customerMap = customerService.getCustomerMap(
  190 + convertSet(list, CrmBusinessDO::getCustomerId));
  191 + // 1.2 获取创建人、负责人列表
  192 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(list,
  193 + contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));
  194 + Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
  195 + // 1.3 获得商机状态组
  196 + Map<Long, CrmBusinessStatusTypeDO> statusTypeMap = businessStatusTypeService.getBusinessStatusTypeMap(
  197 + convertSet(list, CrmBusinessDO::getStatusTypeId));
  198 + Map<Long, CrmBusinessStatusDO> statusMap = businessStatusService.getBusinessStatusMap(
  199 + convertSet(list, CrmBusinessDO::getStatusId));
  200 + // 2. 拼接数据
  201 + return BeanUtils.toBean(list, CrmBusinessRespVO.class, businessVO -> {
  202 + // 2.1 设置客户名称
  203 + MapUtils.findAndThen(customerMap, businessVO.getCustomerId(), customer -> businessVO.setCustomerName(customer.getName()));
  204 + // 2.2 设置创建人、负责人名称
  205 + MapUtils.findAndThen(userMap, NumberUtils.parseLong(businessVO.getCreator()),
  206 + user -> businessVO.setCreatorName(user.getNickname()));
  207 + MapUtils.findAndThen(userMap, businessVO.getOwnerUserId(), user -> {
  208 + businessVO.setOwnerUserName(user.getNickname());
  209 + MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> businessVO.setOwnerUserDeptName(dept.getName()));
  210 + });
  211 + // 2.3 设置商机状态
  212 + MapUtils.findAndThen(statusTypeMap, businessVO.getStatusTypeId(), statusType -> businessVO.setStatusTypeName(statusType.getName()));
  213 + MapUtils.findAndThen(statusMap, businessVO.getStatusId(), status -> businessVO.setStatusName(status.getName()));
  214 + });
167 215 }
168 216  
169 217 @PutMapping("/transfer")
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessExcelVO.java deleted
1   -package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business;
2   -
3   -import com.alibaba.excel.annotation.ExcelProperty;
4   -import lombok.Data;
5   -
6   -import java.math.BigDecimal;
7   -import java.time.LocalDateTime;
8   -import java.util.Set;
9   -
10   -/**
11   - * 商机 Excel VO
12   - *
13   - * @author ljlleo
14   - */
15   -@Data
16   -public class CrmBusinessExcelVO {
17   -
18   - @ExcelProperty("主键")
19   - private Long id;
20   -
21   - @ExcelProperty("商机名称")
22   - private String name;
23   -
24   - @ExcelProperty("商机状态类型编号")
25   - private Long statusTypeId;
26   -
27   - @ExcelProperty("商机状态编号")
28   - private Long statusId;
29   -
30   - @ExcelProperty("下次联系时间")
31   - private LocalDateTime contactNextTime;
32   -
33   - @ExcelProperty("客户编号")
34   - private Long customerId;
35   -
36   - @ExcelProperty("预计成交日期")
37   - private LocalDateTime dealTime;
38   -
39   - @ExcelProperty("商机金额")
40   - private BigDecimal price;
41   -
42   - @ExcelProperty("整单折扣")
43   - private BigDecimal discountPercent;
44   -
45   - @ExcelProperty("产品总金额")
46   - private BigDecimal productPrice;
47   -
48   - @ExcelProperty("备注")
49   - private String remark;
50   -
51   - @ExcelProperty("负责人的用户编号")
52   - private Long ownerUserId;
53   -
54   - @ExcelProperty("创建时间")
55   - private LocalDateTime createTime;
56   -
57   - @ExcelProperty("只读权限的用户编号数组")
58   - private Set<Long> roUserIds;
59   -
60   - @ExcelProperty("读写权限的用户编号数组")
61   - private Set<Long> rwUserIds;
62   -
63   - @ExcelProperty("1赢单2输单3无效")
64   - private Integer endStatus;
65   -
66   - @ExcelProperty("结束时的备注")
67   - private String endRemark;
68   -
69   - @ExcelProperty("最后跟进时间")
70   - private LocalDateTime contactLastTime;
71   -
72   - @ExcelProperty("跟进状态")
73   - private Integer followUpStatus;
74   -
75   -}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessRespVO.java
1 1 package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business;
2 2  
  3 +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
  4 +import com.alibaba.excel.annotation.ExcelProperty;
3 5 import io.swagger.v3.oas.annotations.media.Schema;
4   -import jakarta.validation.constraints.NotNull;
  6 +import lombok.AllArgsConstructor;
5 7 import lombok.Data;
6   -import org.springframework.format.annotation.DateTimeFormat;
  8 +import lombok.NoArgsConstructor;
7 9  
8 10 import java.math.BigDecimal;
9 11 import java.time.LocalDateTime;
  12 +import java.util.List;
10 13  
11   -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
12   -
13   -@Schema(description = "管理后台 - 商机 Response VO")
  14 +@Schema(description = "管理后台 - CRM 商机 Response VO")
14 15 @Data
  16 +@ExcelIgnoreUnannotated
15 17 public class CrmBusinessRespVO {
16 18  
17   - @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "32129")
  19 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "32129")
  20 + @ExcelProperty("编号")
18 21 private Long id;
19 22  
20 23 @Schema(description = "商机名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
21   - @NotNull(message = "商机名称不能为空")
  24 + @ExcelProperty("商机名称")
22 25 private String name;
23 26  
  27 + @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10299")
  28 + private Long customerId;
  29 + @Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
  30 + @ExcelProperty("客户名称")
  31 + private String customerName;
  32 +
  33 + @Schema(description = "跟进状态", requiredMode = Schema.RequiredMode.REQUIRED, example ="true")
  34 + @ExcelProperty("跟进状态")
  35 + private Boolean followUpStatus;
  36 +
  37 + @Schema(description = "最后跟进时间")
  38 + @ExcelProperty("最后跟进时间")
  39 + private LocalDateTime contactLastTime;
  40 +
  41 + @Schema(description = "下次联系时间")
  42 + @ExcelProperty("下次联系时间")
  43 + private LocalDateTime contactNextTime;
  44 +
  45 + @Schema(description = "负责人的用户编号", example = "25682")
  46 + @ExcelProperty("负责人的用户编号")
  47 + private Long ownerUserId;
  48 + @Schema(description = "负责人名字", example = "25682")
  49 + @ExcelProperty("负责人名字")
  50 + private String ownerUserName;
  51 + @Schema(description = "负责人部门")
  52 + @ExcelProperty("负责人部门")
  53 + private String ownerUserDeptName;
  54 +
24 55 @Schema(description = "商机状态类型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "25714")
25   - @NotNull(message = "商机状态类型不能为空")
26 56 private Long statusTypeId;
  57 + @Schema(description = "状态类型名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "进行中")
  58 + @ExcelProperty("商机状态类型")
  59 + private String statusTypeName;
27 60  
28 61 @Schema(description = "商机状态编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320")
29   - @NotNull(message = "商机状态不能为空")
30 62 private Long statusId;
  63 + @Schema(description = "状态名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "跟进中")
  64 + @ExcelProperty("商机状态")
  65 + private String statusName;
31 66  
32   - @Schema(description = "下次联系时间")
33   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
34   - private LocalDateTime contactNextTime;
  67 + @Schema
  68 + @ExcelProperty("1赢单2输单3无效")
  69 + private Integer endStatus;
35 70  
36   - @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10299")
37   - @NotNull(message = "客户不能为空")
38   - private Long customerId;
  71 + @ExcelProperty("结束时的备注")
  72 + private String endRemark;
39 73  
40 74 @Schema(description = "预计成交日期")
41   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  75 + @ExcelProperty("预计成交日期")
42 76 private LocalDateTime dealTime;
43 77  
44   - @Schema(description = "商机金额", example = "12371")
45   - private Integer price;
  78 + @Schema(description = "产品总金额", example = "12025")
  79 + @ExcelProperty("产品总金额")
  80 + private BigDecimal totalProductPrice;
46 81  
47   - // TODO @ljileo:折扣使用 Integer 类型,存储时,默认 * 100;展示的时候,前端需要 / 100;避免精度丢失问题
48 82 @Schema(description = "整单折扣")
49   - private Integer discountPercent;
  83 + @ExcelProperty("整单折扣")
  84 + private BigDecimal discountPercent;
50 85  
51   - @Schema(description = "产品总金额", example = "12025")
52   - private BigDecimal productPrice;
  86 + @Schema(description = "商机总金额", example = "12371")
  87 + @ExcelProperty("商机总金额")
  88 + private BigDecimal totalPrice;
53 89  
54 90 @Schema(description = "备注", example = "随便")
  91 + @ExcelProperty("备注")
55 92 private String remark;
56 93  
  94 + @Schema(description = "创建人", example = "1024")
  95 + @ExcelProperty("创建人")
  96 + private String creator;
  97 + @Schema(description = "创建人名字", example = "芋道源码")
  98 + @ExcelProperty("创建人名字")
  99 + private String creatorName;
  100 +
57 101 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  102 + @ExcelProperty("创建时间")
58 103 private LocalDateTime createTime;
59 104  
60   - @Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
61   - private String customerName;
  105 + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
  106 + @ExcelProperty("更新时间")
  107 + private LocalDateTime updateTime;
62 108  
63   - @Schema(description = "状态类型名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "进行中")
64   - private String statusTypeName;
  109 + @Schema(description = "产品列表")
  110 + private List<Product> products;
65 111  
66   - @Schema(description = "状态名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "跟进中")
67   - private String statusName;
  112 + @Schema(description = "产品列表")
  113 + @Data
  114 + @NoArgsConstructor
  115 + @AllArgsConstructor
  116 + public static class Product {
  117 +
  118 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "888")
  119 + private Long id;
  120 +
  121 + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20529")
  122 + private Long productId;
  123 + @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
  124 + private String productName;
  125 + @Schema(description = "产品条码", requiredMode = Schema.RequiredMode.REQUIRED, example = "20529")
  126 + private String productNo;
  127 + @Schema(description = "产品单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
  128 + private Integer productUnit;
  129 +
  130 + @Schema(description = "产品单价", requiredMode = Schema.RequiredMode.REQUIRED, example = "123.00")
  131 + private BigDecimal productPrice;
  132 +
  133 + @Schema(description = "合同价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "123.00")
  134 + private BigDecimal businessPrice;
  135 +
  136 + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911")
  137 + private Integer count;
  138 +
  139 + @Schema(description = "总计价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "123.00")
  140 + private BigDecimal totalPrice;
  141 +
  142 + }
68 143  
69 144 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessSaveReqVO.java
1 1 package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business;
2 2  
3   -import cn.iocoder.yudao.framework.common.validation.InEnum;
4   -import cn.iocoder.yudao.module.crm.enums.business.CrmBizEndStatus;
5 3 import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerParseFunction;
  4 +import cn.iocoder.yudao.module.crm.framework.operatelog.core.SysAdminUserParseFunction;
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.NotNull;
... ... @@ -29,75 +28,68 @@ public class CrmBusinessSaveReqVO {
29 28 @NotNull(message = "商机名称不能为空")
30 29 private String name;
31 30  
32   - @Schema(description = "商机状态类型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "25714")
33   - @DiffLogField(name = "商机状态")
34   - @NotNull(message = "商机状态类型不能为空")
35   - private Long statusTypeId;
36   -
37   - @Schema(description = "商机状态编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320")
38   - @DiffLogField(name = "商机状态")
39   - @NotNull(message = "商机状态不能为空")
40   - private Long statusId;
  31 + @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10299")
  32 + @DiffLogField(name = "客户", function = CrmCustomerParseFunction.NAME)
  33 + @NotNull(message = "客户不能为空")
  34 + private Long customerId;
41 35  
42 36 @Schema(description = "下次联系时间")
43 37 @DiffLogField(name = "下次联系时间")
44 38 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
45 39 private LocalDateTime contactNextTime;
46 40  
47   - @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10299")
48   - @DiffLogField(name = "客户", function = CrmCustomerParseFunction.NAME)
49   - @NotNull(message = "客户不能为空")
50   - private Long customerId;
  41 + @Schema(description = "负责人用户编号", example = "14334")
  42 + @NotNull(message = "负责人不能为空")
  43 + @DiffLogField(name = "负责人", function = SysAdminUserParseFunction.NAME)
  44 + private Long ownerUserId;
  45 +
  46 + @Schema(description = "商机状态类型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "25714")
  47 + @DiffLogField(name = "商机状态")
  48 + @NotNull(message = "商机状态类型不能为空")
  49 + private Long statusTypeId;
51 50  
52 51 @Schema(description = "预计成交日期")
53 52 @DiffLogField(name = "预计成交日期")
54 53 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
55 54 private LocalDateTime dealTime;
56 55  
57   - @Schema(description = "商机金额", example = "12371")
58   - @DiffLogField(name = "商机金额")
59   - private Integer price;
60   -
61 56 @Schema(description = "整单折扣")
62 57 @DiffLogField(name = "整单折扣")
63   - private Integer discountPercent;
64   -
65   - @Schema(description = "产品总金额", example = "12025")
66   - @DiffLogField(name = "产品总金额")
67   - private BigDecimal productPrice;
  58 + @NotNull(message = "整单折扣不能为空")
  59 + private BigDecimal discountPercent;
68 60  
69 61 @Schema(description = "备注", example = "随便")
70 62 @DiffLogField(name = "备注")
71 63 private String remark;
72 64  
73   - @Schema(description = "结束状态", example = "1")
74   - @InEnum(CrmBizEndStatus.class)
75   - private Integer endStatus;
76   -
77 65 @Schema(description = "联系人编号", example = "110")
78 66 private Long contactId; // 使用场景,在【联系人详情】添加商机时,如果需要关联两者,需要传递 contactId 字段
79 67  
80   - // TODO @puhui999:传递 items 就行啦;
81 68 @Schema(description = "产品列表")
82   - private List<CrmBusinessProductItem> productItems;
  69 + private List<Product> products;
83 70  
84 71 @Schema(description = "产品列表")
85 72 @Data
86 73 @NoArgsConstructor
87 74 @AllArgsConstructor
88   - public static class CrmBusinessProductItem {
  75 + public static class Product {
89 76  
90 77 @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20529")
91 78 @NotNull(message = "产品编号不能为空")
92   - private Long id;
  79 + private Long productId;
  80 +
  81 + @Schema(description = "产品单价", requiredMode = Schema.RequiredMode.REQUIRED, example = "123.00")
  82 + @NotNull(message = "产品单价不能为空")
  83 + private BigDecimal productPrice;
  84 +
  85 + @Schema(description = "合同价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "123.00")
  86 + @NotNull(message = "合同价格不能为空")
  87 + private BigDecimal businessPrice;
93 88  
94 89 @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911")
95 90 @NotNull(message = "产品数量不能为空")
96 91 private Integer count;
97 92  
98   - @Schema(description = "产品折扣")
99   - private Integer discountPercent;
100   -
101 93 }
102 94  
103 95 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java
... ... @@ -179,7 +179,7 @@ public class CrmContractController {
179 179 if (contractList.size() == 1) {
180 180 List<CrmContractProductDO> contractProductList = contractService.getContractProductListByContractId(contractList.get(0).getId());
181 181 contractProductMap = convertMap(contractProductList, CrmContractProductDO::getProductId);
182   - productList = productService.getProductListByIds(convertSet(contractProductList, CrmContractProductDO::getProductId));
  182 + productList = productService.getProductList(convertSet(contractProductList, CrmContractProductDO::getProductId));
183 183 }
184 184 return CrmContractConvert.INSTANCE.convertList(contractList, userMap, customerList, contactMap, businessMap, contractProductMap, productList);
185 185 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/CrmProductController.java
1 1 package cn.iocoder.yudao.module.crm.controller.admin.product;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 6 import cn.iocoder.yudao.framework.common.pojo.PageParam;
6 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7   -import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
  8 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
  9 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
8 10 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
9 11 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
10 12 import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductPageReqVO;
11 13 import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductRespVO;
12 14 import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductSaveReqVO;
13   -import cn.iocoder.yudao.module.crm.convert.product.CrmProductConvert;
14 15 import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO;
15 16 import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
16 17 import cn.iocoder.yudao.module.crm.service.product.CrmProductCategoryService;
... ... @@ -34,10 +35,9 @@ import java.util.Map;
34 35 import java.util.stream.Stream;
35 36  
36 37 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
37   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
38   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
  38 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
39 39 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
40   -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  40 +import static java.util.Collections.singletonList;
41 41  
42 42 @Tag(name = "管理后台 - CRM 产品")
43 43 @RestController
... ... @@ -49,6 +49,7 @@ public class CrmProductController {
49 49 private CrmProductService productService;
50 50 @Resource
51 51 private CrmProductCategoryService productCategoryService;
  52 +
52 53 @Resource
53 54 private AdminUserApi adminUserApi;
54 55  
... ... @@ -82,21 +83,30 @@ public class CrmProductController {
82 83 @PreAuthorize("@ss.hasPermission('crm:product:query')")
83 84 public CommonResult<CrmProductRespVO> getProduct(@RequestParam("id") Long id) {
84 85 CrmProductDO product = productService.getProduct(id);
  86 + return success(buildProductDetail(product));
  87 + }
  88 +
  89 + private CrmProductRespVO buildProductDetail(CrmProductDO product) {
85 90 if (product == null) {
86   - return success(null);
  91 + return null;
87 92 }
88   - Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
89   - SetUtils.asSet(Long.valueOf(product.getCreator()), product.getOwnerUserId()));
90   - CrmProductCategoryDO category = productCategoryService.getProductCategory(product.getCategoryId());
91   - return success(CrmProductConvert.INSTANCE.convert(product, userMap, category));
  93 + return buildProductDetailList(singletonList(product)).get(0);
  94 + }
  95 +
  96 + @GetMapping("/simple-list")
  97 + @Operation(summary = "获得产品精简列表", description = "只包含被开启的产品,主要用于前端的下拉选项")
  98 + public CommonResult<List<CrmProductRespVO>> getProductSimpleList() {
  99 + List<CrmProductDO> list = productService.getProductListByStatus(CommonStatusEnum.ENABLE.getStatus());
  100 + return success(convertList(list, product -> new CrmProductRespVO().setId(product.getId()).setName(product.getName())
  101 + .setUnit(product.getUnit()).setNo(product.getNo()).setPrice(product.getPrice())));
92 102 }
93 103  
94 104 @GetMapping("/page")
95 105 @Operation(summary = "获得产品分页")
96 106 @PreAuthorize("@ss.hasPermission('crm:product:query')")
97 107 public CommonResult<PageResult<CrmProductRespVO>> getProductPage(@Valid CrmProductPageReqVO pageVO) {
98   - PageResult<CrmProductDO> pageResult = productService.getProductPage(pageVO, getLoginUserId());
99   - return success(new PageResult<>(getProductDetailList(pageResult.getList()), pageResult.getTotal()));
  108 + PageResult<CrmProductDO> pageResult = productService.getProductPage(pageVO);
  109 + return success(new PageResult<>(buildProductDetailList(pageResult.getList()), pageResult.getTotal()));
100 110 }
101 111  
102 112 @GetMapping("/export-excel")
... ... @@ -106,21 +116,30 @@ public class CrmProductController {
106 116 public void exportProductExcel(@Valid CrmProductPageReqVO exportReqVO,
107 117 HttpServletResponse response) throws IOException {
108 118 exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
109   - List<CrmProductDO> list = productService.getProductPage(exportReqVO, getLoginUserId()).getList();
  119 + List<CrmProductDO> list = productService.getProductPage(exportReqVO).getList();
110 120 // 导出 Excel
111 121 ExcelUtils.write(response, "产品.xls", "数据", CrmProductRespVO.class,
112   - getProductDetailList(list));
  122 + buildProductDetailList(list));
113 123 }
114 124  
115   - private List<CrmProductRespVO> getProductDetailList(List<CrmProductDO> list) {
  125 + private List<CrmProductRespVO> buildProductDetailList(List<CrmProductDO> list) {
116 126 if (CollUtil.isEmpty(list)) {
117 127 return Collections.emptyList();
118 128 }
  129 + // 1.1 获得用户信息
119 130 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
120 131 convertSetByFlatMap(list, user -> Stream.of(Long.valueOf(user.getCreator()), user.getOwnerUserId())));
121   - List<CrmProductCategoryDO> productCategoryList = productCategoryService.getProductCategoryList(
  132 + // 1.2 获得分类信息
  133 + Map<Long, CrmProductCategoryDO> categoryMap = productCategoryService.getProductCategoryMap(
122 134 convertSet(list, CrmProductDO::getCategoryId));
123   - return CrmProductConvert.INSTANCE.convertList(list, userMap, productCategoryList);
  135 + // 2. 拼接数据
  136 + return BeanUtils.toBean(list, CrmProductRespVO.class, productVO -> {
  137 + // 2.1 设置用户信息
  138 + MapUtils.findAndThen(userMap, productVO.getOwnerUserId(), user -> productVO.setOwnerUserName(user.getNickname()));
  139 + MapUtils.findAndThen(userMap, Long.valueOf(productVO.getCreator()), user -> productVO.setCreatorName(user.getNickname()));
  140 + // 2.2 设置分类名称
  141 + MapUtils.findAndThen(categoryMap, productVO.getCategoryId(), category -> productVO.setCategoryName(category.getName()));
  142 + });
124 143 }
125 144  
126 145 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/product/CrmProductRespVO.java
... ... @@ -8,6 +8,7 @@ import com.alibaba.excel.annotation.ExcelProperty;
8 8 import io.swagger.v3.oas.annotations.media.Schema;
9 9 import lombok.Data;
10 10  
  11 +import java.math.BigDecimal;
11 12 import java.time.LocalDateTime;
12 13  
13 14 @Schema(description = "管理后台 - CRM 产品 Response VO")
... ... @@ -34,7 +35,7 @@ public class CrmProductRespVO {
34 35  
35 36 @Schema(description = "价格, 单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911")
36 37 @ExcelProperty("价格,单位:分")
37   - private Long price;
  38 + private BigDecimal price;
38 39  
39 40 @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "上架")
40 41 @ExcelProperty(value = "单位", converter = DictConvert.class)
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/product/CrmProductSaveReqVO.java
... ... @@ -7,6 +7,8 @@ import io.swagger.v3.oas.annotations.media.Schema;
7 7 import jakarta.validation.constraints.NotNull;
8 8 import lombok.Data;
9 9  
  10 +import java.math.BigDecimal;
  11 +
10 12 @Schema(description = "管理后台 - CRM 产品创建/修改 Request VO")
11 13 @Data
12 14 public class CrmProductSaveReqVO {
... ... @@ -28,10 +30,10 @@ public class CrmProductSaveReqVO {
28 30 @DiffLogField(name = "单位", function = CrmProductUnitParseFunction.NAME)
29 31 private Integer unit;
30 32  
31   - @Schema(description = "价格, 单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911")
  33 + @Schema(description = "价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911")
32 34 @NotNull(message = "价格不能为空")
33 35 @DiffLogField(name = "价格")
34   - private Long price;
  36 + private BigDecimal price;
35 37  
36 38 @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "上架")
37 39 @NotNull(message = "状态不能为空")
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessConvert.java
1 1 package cn.iocoder.yudao.module.crm.convert.business;
2 2  
3   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 3 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.business.vo.business.CrmBusinessRespVO;
7 4 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO;
8 5 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
9   -import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
10   -import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO;
11   -import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
12 6 import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
13 7 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
14 8 import org.mapstruct.Mapper;
... ... @@ -16,9 +10,6 @@ import org.mapstruct.Mapping;
16 10 import org.mapstruct.factory.Mappers;
17 11  
18 12 import java.util.List;
19   -import java.util.Map;
20   -
21   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
22 13  
23 14 /**
24 15 * 商机 Convert
... ... @@ -33,20 +24,6 @@ public interface CrmBusinessConvert {
33 24 @Mapping(target = "bizId", source = "reqVO.id")
34 25 CrmPermissionTransferReqBO convert(CrmBusinessTransferReqVO reqVO, Long userId);
35 26  
36   - default PageResult<CrmBusinessRespVO> convertPage(PageResult<CrmBusinessDO> pageResult, List<CrmCustomerDO> customerList,
37   - List<CrmBusinessStatusTypeDO> statusTypeList, List<CrmBusinessStatusDO> statusList) {
38   - PageResult<CrmBusinessRespVO> voPageResult = BeanUtils.toBean(pageResult, CrmBusinessRespVO.class);
39   - // 拼接关联字段
40   - Map<Long, String> customerMap = convertMap(customerList, CrmCustomerDO::getId, CrmCustomerDO::getName);
41   - Map<Long, String> statusTypeMap = convertMap(statusTypeList, CrmBusinessStatusTypeDO::getId, CrmBusinessStatusTypeDO::getName);
42   - Map<Long, String> statusMap = convertMap(statusList, CrmBusinessStatusDO::getId, CrmBusinessStatusDO::getName);
43   - voPageResult.getList().forEach(type -> type
44   - .setCustomerName(customerMap.get(type.getCustomerId()))
45   - .setStatusTypeName(statusTypeMap.get(type.getStatusTypeId()))
46   - .setStatusName(statusMap.get(type.getStatusId())));
47   - return voPageResult;
48   - }
49   -
50 27 @Mapping(target = "id", source = "reqBO.bizId")
51 28 CrmBusinessDO convert(CrmUpdateFollowUpReqBO reqBO);
52 29  
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/product/CrmProductConvert.java deleted
1   -package cn.iocoder.yudao.module.crm.convert.product;
2   -
3   -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
4   -import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
5   -import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6   -import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductRespVO;
7   -import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO;
8   -import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
9   -import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
10   -import org.mapstruct.Mapper;
11   -import org.mapstruct.factory.Mappers;
12   -
13   -import java.util.List;
14   -import java.util.Map;
15   -import java.util.Optional;
16   -
17   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
18   -
19   -/**
20   - * 产品 Convert
21   - *
22   - * @author ZanGe丶
23   - */
24   -@Mapper
25   -public interface CrmProductConvert {
26   -
27   - CrmProductConvert INSTANCE = Mappers.getMapper(CrmProductConvert.class);
28   -
29   - default List<CrmProductRespVO> convertList(List<CrmProductDO> list,
30   - Map<Long, AdminUserRespDTO> userMap,
31   - List<CrmProductCategoryDO> categoryList) {
32   - Map<Long, CrmProductCategoryDO> categoryMap = convertMap(categoryList, CrmProductCategoryDO::getId);
33   - return CollectionUtils.convertList(list,
34   - product -> convert(product, userMap, categoryMap.get(product.getCategoryId())));
35   - }
36   -
37   - default CrmProductRespVO convert(CrmProductDO product,
38   - Map<Long, AdminUserRespDTO> userMap, CrmProductCategoryDO category) {
39   - CrmProductRespVO productVO = BeanUtils.toBean(product, CrmProductRespVO.class);
40   - Optional.ofNullable(category).ifPresent(c -> productVO.setCategoryName(c.getName()));
41   - MapUtils.findAndThen(userMap, productVO.getOwnerUserId(), user -> productVO.setOwnerUserName(user.getNickname()));
42   - MapUtils.findAndThen(userMap, Long.valueOf(productVO.getCreator()), user -> productVO.setCreatorName(user.getNickname()));
43   - return productVO;
44   - }
45   -
46   -}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessDO.java
... ... @@ -8,10 +8,11 @@ import com.baomidou.mybatisplus.annotation.TableId;
8 8 import com.baomidou.mybatisplus.annotation.TableName;
9 9 import lombok.*;
10 10  
  11 +import java.math.BigDecimal;
11 12 import java.time.LocalDateTime;
12 13  
13 14 /**
14   - * 商机 DO
  15 + * CRM 商机 DO
15 16 *
16 17 * @author ljlleo
17 18 */
... ... @@ -26,7 +27,7 @@ import java.time.LocalDateTime;
26 27 public class CrmBusinessDO extends BaseDO {
27 28  
28 29 /**
29   - * 主键
  30 + * 编号
30 31 */
31 32 @TableId
32 33 private Long id;
... ... @@ -35,50 +36,44 @@ public class CrmBusinessDO extends BaseDO {
35 36 */
36 37 private String name;
37 38 /**
38   - * 商机状态类型编号
  39 + * 客户编号
39 40 *
40   - * 关联 {@link CrmBusinessStatusTypeDO#getId()}
  41 + * 关联 {@link CrmCustomerDO#getId()}
41 42 */
42   - private Long statusTypeId;
  43 + private Long customerId;
  44 +
43 45 /**
44   - * 商机状态编号
45   - *
46   - * 关联 {@link CrmBusinessStatusDO#getId()}
  46 + * 跟进状态
47 47 */
48   - private Long statusId;
  48 + private Boolean followUpStatus;
  49 + /**
  50 + * 最后跟进时间
  51 + */
  52 + private LocalDateTime contactLastTime;
49 53 /**
50 54 * 下次联系时间
51 55 */
52 56 private LocalDateTime contactNextTime;
  57 +
53 58 /**
54   - * 客户编号
  59 + * 负责人的用户编号
55 60 *
56   - * TODO @ljileo:这个字段,后续要写下关联的实体哈
57   - * 关联 {@link CrmCustomerDO#getId()}
58   - */
59   - private Long customerId;
60   - /**
61   - * 预计成交日期
  61 + * 关联 AdminUserDO 的 id 字段
62 62 */
63   - private LocalDateTime dealTime;
  63 + private Long ownerUserId;
  64 +
64 65 /**
65   - * 商机金额
  66 + * 商机状态类型编号
66 67 *
  68 + * 关联 {@link CrmBusinessStatusTypeDO#getId()}
67 69 */
68   - private Integer price;
  70 + private Long statusTypeId;
69 71 /**
70   - * 整单折扣
  72 + * 商机状态编号
71 73 *
  74 + * 关联 {@link CrmBusinessStatusDO#getId()}
72 75 */
73   - private Integer discountPercent;
74   - /**
75   - * 产品总金额,单位:分
76   - */
77   - private Integer productPrice;
78   - /**
79   - * 备注
80   - */
81   - private String remark;
  76 + private Long statusId;
82 77 /**
83 78 * 结束状态
84 79 *
... ... @@ -89,20 +84,28 @@ public class CrmBusinessDO extends BaseDO {
89 84 * 结束时的备注
90 85 */
91 86 private String endRemark;
  87 +
92 88 /**
93   - * 最后跟进时间
  89 + * 预计成交日期
94 90 */
95   - private LocalDateTime contactLastTime;
  91 + private LocalDateTime dealTime;
96 92 /**
97   - * 跟进状态
  93 + * 产品总金额,单位:元
  94 + *
  95 + * productPrice = ∑({@link CrmBusinessProductDO#getTotalPrice()})
98 96 */
99   - private Boolean followUpStatus;
100   -
  97 + private BigDecimal totalProductPrice;
101 98 /**
102   - * 负责人的用户编号
103   - *
104   - * 关联 AdminUserDO 的 id 字段
  99 + * 整单折扣,百分比
105 100 */
106   - private Long ownerUserId;
  101 + private BigDecimal discountPercent;
  102 + /**
  103 + * 商机总金额,单位:元
  104 + */
  105 + private BigDecimal totalPrice;
  106 + /**
  107 + * 备注
  108 + */
  109 + private String remark;
107 110  
108 111 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessProductDO.java
... ... @@ -7,9 +7,13 @@ 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 13 * 商机产品关联表 DO
12 14 *
  15 + * CrmBusinessDO : CrmBusinessProductDO = 1 : N
  16 + *
13 17 * @author lzxhqs
14 18 */
15 19 @TableName("crm_business_product")
... ... @@ -40,24 +44,24 @@ public class CrmBusinessProductDO extends BaseDO {
40 44 */
41 45 private Long productId;
42 46 /**
43   - * 产品单价
  47 + * 产品单价,单位:元
  48 + *
  49 + * 冗余 {@link CrmProductDO#getPrice()}
44 50 */
45   - private Integer price;
  51 + private BigDecimal productPrice;
46 52 /**
47   - * 销售价格, 单位:分
  53 + * 合同价格, 单位:元
48 54 */
49   - private Integer salesPrice;
  55 + private BigDecimal businessPrice;
50 56 /**
51 57 * 数量
52 58 */
53   - private Integer count;
  59 + private BigDecimal count;
54 60 /**
55   - * 折扣
56   - */
57   - private Integer discountPercent;
58   - /**
59   - * 总计价格(折扣后价格)
  61 + * 总计价格,单位:元
  62 + *
  63 + * totalPrice = businessPrice * count
60 64 */
61   - private Integer totalPrice;
  65 + private BigDecimal totalPrice;
62 66  
63 67 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/clue/CrmClueDO.java
... ... @@ -11,7 +11,7 @@ import lombok.*;
11 11 import java.time.LocalDateTime;
12 12  
13 13 /**
14   - * 线索 DO
  14 + * CRM 线索 DO
15 15 *
16 16 * @author Wanwan
17 17 */
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/product/CrmProductDO.java
... ... @@ -8,6 +8,8 @@ import com.baomidou.mybatisplus.annotation.TableId;
8 8 import com.baomidou.mybatisplus.annotation.TableName;
9 9 import lombok.*;
10 10  
  11 +import java.math.BigDecimal;
  12 +
11 13 /**
12 14 * CRM 产品 DO
13 15 *
... ... @@ -43,9 +45,9 @@ public class CrmProductDO extends BaseDO {
43 45 */
44 46 private Integer unit;
45 47 /**
46   - * 价格,单位:分
  48 + * 价格,单位:元
47 49 */
48   - private Integer price;
  50 + private BigDecimal price;
49 51 /**
50 52 * 状态
51 53 *
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/package-info.java deleted
1   -/**
2   - * 商机(销售机会)
3   - */
4   -package cn.iocoder.yudao.module.crm.dal.mysql.business;
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/package-info.java deleted
1   -/**
2   - * 联系人
3   - */
4   -package cn.iocoder.yudao.module.crm.dal.mysql.contact;
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/product/CrmProductMapper.java
... ... @@ -5,10 +5,10 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 5 import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
6 6 import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductPageReqVO;
7 7 import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
8   -import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
9   -import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils;
10 8 import org.apache.ibatis.annotations.Mapper;
11 9  
  10 +import java.util.List;
  11 +
12 12 /**
13 13 * CRM 产品 Mapper
14 14 *
... ... @@ -17,21 +17,23 @@ import org.apache.ibatis.annotations.Mapper;
17 17 @Mapper
18 18 public interface CrmProductMapper extends BaseMapperX<CrmProductDO> {
19 19  
20   - default PageResult<CrmProductDO> selectPage(CrmProductPageReqVO reqVO, Long userId) {
21   - MPJLambdaWrapperX<CrmProductDO> query = new MPJLambdaWrapperX<>();
22   - // 拼接数据权限的查询条件
23   - CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_PRODUCT.getType(),
24   - CrmProductDO::getId, userId, null, Boolean.FALSE);
25   - // 拼接自身的查询条件
26   - query.selectAll(CrmProductDO.class)
  20 + default PageResult<CrmProductDO> selectPage(CrmProductPageReqVO reqVO) {
  21 + return selectPage(reqVO, new MPJLambdaWrapperX<CrmProductDO>()
27 22 .likeIfPresent(CrmProductDO::getName, reqVO.getName())
28 23 .eqIfPresent(CrmProductDO::getStatus, reqVO.getStatus())
29   - .orderByDesc(CrmProductDO::getId);
30   - return selectJoinPage(reqVO, CrmProductDO.class, query);
  24 + .orderByDesc(CrmProductDO::getId));
31 25 }
32 26  
33 27 default CrmProductDO selectByNo(String no) {
34 28 return selectOne(CrmProductDO::getNo, no);
35 29 }
36 30  
  31 + default Long selectCountByCategoryId(Long categoryId) {
  32 + return selectCount(CrmProductDO::getCategoryId, categoryId);
  33 + }
  34 +
  35 + default List<CrmProductDO> selectListByStatus(Integer status) {
  36 + return selectList(CrmProductDO::getStatus, status);
  37 + }
  38 +
37 39 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessService.java
... ... @@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusi
5 5 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessSaveReqVO;
6 6 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO;
7 7 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
  8 +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
8 9 import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
9 10 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
10 11 import cn.iocoder.yudao.module.crm.service.business.bo.CrmBusinessUpdateProductReqBO;
... ... @@ -91,6 +92,14 @@ public interface CrmBusinessService {
91 92 List<CrmBusinessDO> getBusinessList(Collection<Long> ids);
92 93  
93 94 /**
  95 + * 获得指定商机编号的产品列表
  96 + *
  97 + * @param businessId 商机编号
  98 + * @return 商机产品列表
  99 + */
  100 + List<CrmBusinessProductDO> getBusinessProductListByBusinessId(Long businessId);
  101 +
  102 + /**
94 103 * 获得商机分页
95 104 *
96 105 * 数据权限:基于 {@link CrmBusinessDO}
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java
... ... @@ -2,9 +2,7 @@ package cn.iocoder.yudao.module.crm.service.business;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.collection.ListUtil;
5   -import cn.hutool.core.util.ObjectUtil;
6 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7   -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
8 6 import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
9 7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
10 8 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO;
... ... @@ -14,7 +12,6 @@ import cn.iocoder.yudao.module.crm.convert.business.CrmBusinessConvert;
14 12 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
15 13 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
16 14 import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO;
17   -import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
18 15 import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessMapper;
19 16 import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessProductMapper;
20 17 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
... ... @@ -36,14 +33,14 @@ import org.springframework.stereotype.Service;
36 33 import org.springframework.transaction.annotation.Transactional;
37 34 import org.springframework.validation.annotation.Validated;
38 35  
  36 +import java.math.BigDecimal;
39 37 import java.util.Collection;
40 38 import java.util.List;
41   -import java.util.Map;
42   -import java.util.Set;
43 39  
44 40 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
45 41 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
46   -import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
  42 +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_CONTRACT_EXISTS;
  43 +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_NOT_EXISTS;
47 44 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
48 45  
49 46 /**
... ... @@ -75,27 +72,29 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
75 72 @LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_CREATE_SUB_TYPE, bizNo = "{{#business.id}}",
76 73 success = CRM_BUSINESS_CREATE_SUCCESS)
77 74 public Long createBusiness(CrmBusinessSaveReqVO createReqVO, Long userId) {
78   - createReqVO.setId(null);
79   - // 1. 插入商机
  75 + // 1.1 校验产品项的有效性
  76 + List<CrmBusinessProductDO> businessProducts = validateBusinessProducts(createReqVO.getProducts());
  77 + // 1.2 TODO 芋艿:校验关联字
  78 +
  79 + // 2.1 插入商机
80 80 CrmBusinessDO business = BeanUtils.toBean(createReqVO, CrmBusinessDO.class).setOwnerUserId(userId);
  81 + calculateTotalPrice(business, businessProducts);
81 82 businessMapper.insert(business);
82   - // 1.2 插入商机关联商品
83   - if (CollUtil.isNotEmpty(createReqVO.getProductItems())) { // 如果有的话
84   - List<CrmBusinessProductDO> productList = buildBusinessProductList(createReqVO.getProductItems(), business.getId());
85   - businessProductMapper.insertBatch(productList);
86   - // 更新合同商品总金额
87   - businessMapper.updateById(new CrmBusinessDO().setId(business.getId()).setProductPrice(
88   - getSumValue(productList, CrmBusinessProductDO::getTotalPrice, Integer::sum)));
  83 + // 2.2 插入商机关联商品
  84 + if (CollUtil.isNotEmpty(businessProducts)) {
  85 + businessProducts.forEach(item -> item.setBusinessId(business.getId()));
  86 + businessProductMapper.insertBatch(businessProducts);
89 87 }
  88 +
90 89 // TODO @puhui999:在联系人的详情页,如果直接【新建商机】,则需要关联下。这里要搞个 CrmContactBusinessDO 表
91 90 createContactBusiness(business.getId(), createReqVO.getContactId());
92 91  
93   - // 2. 创建数据权限
  92 + // 3. 创建数据权限
94 93 // 设置当前操作的人为负责人
95 94 permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_BUSINESS.getType())
96 95 .setBizId(business.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
97 96  
98   - // 3. 记录操作日志上下文
  97 + // 4. 记录操作日志上下文
99 98 LogRecordContext.putVariable("business", business);
100 99 return business.getId();
101 100 }
... ... @@ -114,15 +113,18 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
114 113 success = CRM_BUSINESS_UPDATE_SUCCESS)
115 114 @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
116 115 public void updateBusiness(CrmBusinessSaveReqVO updateReqVO) {
117   - // 1. 校验存在
  116 + // 1.1 校验存在
118 117 CrmBusinessDO oldBusiness = validateBusinessExists(updateReqVO.getId());
  118 + // 1.2 校验产品项的有效性
  119 + List<CrmBusinessProductDO> businessProducts = validateBusinessProducts(updateReqVO.getProducts());
  120 + // 1.3 TODO 芋艿:校验关联字
119 121  
120 122 // 2.1 更新商机
121 123 CrmBusinessDO updateObj = BeanUtils.toBean(updateReqVO, CrmBusinessDO.class);
  124 + calculateTotalPrice(updateObj, businessProducts);
122 125 businessMapper.updateById(updateObj);
123 126 // 2.2 更新商机关联商品
124   - List<CrmBusinessProductDO> productList = buildBusinessProductList(updateReqVO.getProductItems(), updateObj.getId());
125   - updateBusinessProduct(productList, updateObj.getId());
  127 + updateBusinessProduct(updateObj.getId(), businessProducts);
126 128  
127 129 // TODO @商机待定:如果状态发生变化,插入商机状态变更记录表
128 130 // 3. 记录操作日志上下文
... ... @@ -130,6 +132,37 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
130 132 LogRecordContext.putVariable("businessName", oldBusiness.getName());
131 133 }
132 134  
  135 + private void updateBusinessProduct(Long id, List<CrmBusinessProductDO> newList) {
  136 + List<CrmBusinessProductDO> oldList = businessProductMapper.selectListByBusinessId(id);
  137 + List<List<CrmBusinessProductDO>> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录
  138 + (oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));
  139 + if (CollUtil.isNotEmpty(diffList.get(0))) {
  140 + diffList.get(0).forEach(o -> o.setBusinessId(id));
  141 + businessProductMapper.insertBatch(diffList.get(0));
  142 + }
  143 + if (CollUtil.isNotEmpty(diffList.get(1))) {
  144 + businessProductMapper.updateBatch(diffList.get(1));
  145 + }
  146 + if (CollUtil.isNotEmpty(diffList.get(2))) {
  147 + businessProductMapper.deleteBatchIds(convertSet(diffList.get(2), CrmBusinessProductDO::getId));
  148 + }
  149 + }
  150 +
  151 + private List<CrmBusinessProductDO> validateBusinessProducts(List<CrmBusinessSaveReqVO.Product> list) {
  152 + // 1. 校验产品存在
  153 + productService.validProductList(convertSet(list, CrmBusinessSaveReqVO.Product::getProductId));
  154 + // 2. 转化为 CrmBusinessProductDO 列表
  155 + return convertList(list, o -> BeanUtils.toBean(o, CrmBusinessProductDO.class, item -> {
  156 + item.setTotalPrice(MoneyUtils.priceMultiply(item.getBusinessPrice(), item.getCount()));
  157 + }));
  158 + }
  159 +
  160 + private void calculateTotalPrice(CrmBusinessDO business, List<CrmBusinessProductDO> businessProducts) {
  161 + business.setTotalProductPrice(getSumValue(businessProducts, CrmBusinessProductDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO));
  162 + BigDecimal discountPrice = MoneyUtils.priceMultiplyPercent(business.getTotalProductPrice(), business.getDiscountPercent());
  163 + business.setTotalPrice(business.getTotalProductPrice().subtract(discountPrice));
  164 + }
  165 +
133 166 @Override
134 167 public void updateBusinessFollowUpBatch(List<CrmUpdateFollowUpReqBO> updateFollowUpReqBOList) {
135 168 businessMapper.updateBatch(CrmBusinessConvert.INSTANCE.convertList(updateFollowUpReqBOList));
... ... @@ -155,44 +188,6 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
155 188 LogRecordContext.putVariable("businessName", business.getName());
156 189 }
157 190  
158   - private void updateBusinessProduct(List<CrmBusinessProductDO> newProductList, Long businessId) {
159   - List<CrmBusinessProductDO> oldProducts = businessProductMapper.selectListByBusinessId(businessId);
160   - List<List<CrmBusinessProductDO>> diffList = CollectionUtils.diffList(oldProducts, newProductList, (oldValue, newValue) -> {
161   - boolean condition = ObjectUtil.equal(oldValue.getProductId(), newValue.getProductId());
162   - if (condition) {
163   - newValue.setId(oldValue.getId()); // 更新需要原始编号
164   - }
165   - return condition;
166   - });
167   - if (CollUtil.isNotEmpty(diffList.get(0))) {
168   - businessProductMapper.insertBatch(diffList.get(0));
169   - }
170   - if (CollUtil.isNotEmpty(diffList.get(1))) {
171   - businessProductMapper.updateBatch(diffList.get(1));
172   - }
173   - if (CollUtil.isNotEmpty(diffList.get(2))) {
174   - businessProductMapper.deleteBatchIds(convertSet(diffList.get(2), CrmBusinessProductDO::getId));
175   - }
176   - }
177   -
178   - private List<CrmBusinessProductDO> buildBusinessProductList(List<CrmBusinessSaveReqVO.CrmBusinessProductItem> productItems,
179   - Long businessId) {
180   - // 校验商品存在
181   - Set<Long> productIds = convertSet(productItems, CrmBusinessSaveReqVO.CrmBusinessProductItem::getId);
182   - List<CrmProductDO> productList = productService.getProductList(productIds);
183   - if (CollUtil.isEmpty(productIds) || productList.size() != productIds.size()) {
184   - throw exception(PRODUCT_NOT_EXISTS);
185   - }
186   - Map<Long, CrmProductDO> productMap = convertMap(productList, CrmProductDO::getId);
187   - return convertList(productItems, productItem -> {
188   - CrmProductDO product = productMap.get(productItem.getId());
189   - return BeanUtils.toBean(product, CrmBusinessProductDO.class)
190   - .setId(null).setProductId(productItem.getId()).setBusinessId(businessId)
191   - .setCount(productItem.getCount()).setDiscountPercent(productItem.getDiscountPercent())
192   - .setTotalPrice(MoneyUtils.calculator(product.getPrice(), productItem.getCount(), productItem.getDiscountPercent()));
193   - });
194   - }
195   -
196 191 /**
197 192 * 删除校验合同是关联合同
198 193 *
... ... @@ -235,10 +230,10 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
235 230  
236 231 @Override
237 232 public void updateBusinessProduct(CrmBusinessUpdateProductReqBO updateProductReqBO) {
238   - // 更新商机关联商品
239   - List<CrmBusinessProductDO> productList = buildBusinessProductList(
240   - BeanUtils.toBean(updateProductReqBO.getProductItems(), CrmBusinessSaveReqVO.CrmBusinessProductItem.class), updateProductReqBO.getId());
241   - updateBusinessProduct(productList, updateProductReqBO.getId());
  233 + // 更新商机关联商品 TODO yunai
  234 +// List<CrmBusinessProductDO> productList = buildBusinessProductList(
  235 +// BeanUtils.toBean(updateProductReqBO.getProductItems(), CrmBusinessSaveReqVO.Product.class), updateProductReqBO.getId());
  236 +// updateBusinessProduct(productList, updateProductReqBO.getId());
242 237 }
243 238  
244 239 //======================= 查询相关 =======================
... ... @@ -266,6 +261,11 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
266 261 }
267 262  
268 263 @Override
  264 + public List<CrmBusinessProductDO> getBusinessProductListByBusinessId(Long businessId) {
  265 + return businessProductMapper.selectListByBusinessId(businessId);
  266 + }
  267 +
  268 + @Override
269 269 public PageResult<CrmBusinessDO> getBusinessPage(CrmBusinessPageReqVO pageReqVO, Long userId) {
270 270 return businessMapper.selectPage(pageReqVO, userId);
271 271 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusService.java
... ... @@ -5,11 +5,13 @@ import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusine
5 5 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusQueryVO;
6 6 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusSaveReqVO;
7 7 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
8   -
9 8 import jakarta.validation.Valid;
10 9  
11 10 import java.util.Collection;
12 11 import java.util.List;
  12 +import java.util.Map;
  13 +
  14 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
13 15  
14 16 /**
15 17 * 商机状态 Service 接口
... ... @@ -74,4 +76,14 @@ public interface CrmBusinessStatusService {
74 76 */
75 77 List<CrmBusinessStatusDO> getBusinessStatusList(Collection<Long> ids);
76 78  
  79 + /**
  80 + * 获得商机状态 Map
  81 + *
  82 + * @param ids 编号数组
  83 + * @return 商机状态 Map
  84 + */
  85 + default Map<Long, CrmBusinessStatusDO> getBusinessStatusMap(Collection<Long> ids) {
  86 + return convertMap(getBusinessStatusList(ids), CrmBusinessStatusDO::getId);
  87 + }
  88 +
77 89 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusServiceImpl.java
1 1 package cn.iocoder.yudao.module.crm.service.business;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 6 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusPageReqVO;
... ... @@ -13,6 +14,7 @@ import org.springframework.validation.annotation.Validated;
13 14 import jakarta.annotation.Resource;
14 15  
15 16 import java.util.Collection;
  17 +import java.util.Collections;
16 18 import java.util.List;
17 19  
18 20 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
... ... @@ -80,6 +82,9 @@ public class CrmBusinessStatusServiceImpl implements CrmBusinessStatusService {
80 82  
81 83 @Override
82 84 public List<CrmBusinessStatusDO> getBusinessStatusList(Collection<Long> ids) {
  85 + if (CollUtil.isEmpty(ids)) {
  86 + return Collections.emptyList();
  87 + }
83 88 return businessStatusMapper.selectBatchIds(ids);
84 89 }
85 90  
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusTypeService.java
... ... @@ -9,6 +9,9 @@ import jakarta.validation.Valid;
9 9  
10 10 import java.util.Collection;
11 11 import java.util.List;
  12 +import java.util.Map;
  13 +
  14 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
12 15  
13 16 /**
14 17 * 商机状态类型 Service 接口
... ... @@ -72,4 +75,14 @@ public interface CrmBusinessStatusTypeService {
72 75 */
73 76 List<CrmBusinessStatusTypeDO> getBusinessStatusTypeList(Collection<Long> ids);
74 77  
  78 + /**
  79 + * 获得商机状态类型 Map
  80 + *
  81 + * @param ids 编号数组
  82 + * @return 商机状态类型 Map
  83 + */
  84 + default Map<Long, CrmBusinessStatusTypeDO> getBusinessStatusTypeMap(Collection<Long> ids) {
  85 + return convertMap(getBusinessStatusTypeList(ids), CrmBusinessStatusTypeDO::getId);
  86 + }
  87 +
75 88 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusTypeServiceImpl.java
... ... @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.crm.service.business;
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6   -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
7 6 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypePageReqVO;
8 7 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeQueryVO;
9 8 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeSaveReqVO;
... ... @@ -11,19 +10,18 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
11 10 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO;
12 11 import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessStatusMapper;
13 12 import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessStatusTypeMapper;
14   -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  13 +import jakarta.annotation.Resource;
15 14 import org.springframework.stereotype.Service;
16 15 import org.springframework.transaction.annotation.Transactional;
17 16 import org.springframework.validation.annotation.Validated;
18 17  
19   -import jakarta.annotation.Resource;
20   -
21 18 import java.util.Collection;
  19 +import java.util.Collections;
22 20 import java.util.List;
23 21  
24 22 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
25   -import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_STATUS_TYPE_NOT_EXISTS;
26 23 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_STATUS_TYPE_NAME_EXISTS;
  24 +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_STATUS_TYPE_NOT_EXISTS;
27 25  
28 26 /**
29 27 * 商机状态类型 Service 实现类
... ... @@ -126,6 +124,9 @@ public class CrmBusinessStatusTypeServiceImpl implements CrmBusinessStatusTypeSe
126 124  
127 125 @Override
128 126 public List<CrmBusinessStatusTypeDO> getBusinessStatusTypeList(Collection<Long> ids) {
  127 + if (CollUtil.isEmpty(ids)) {
  128 + return Collections.emptyList();
  129 + }
129 130 return businessStatusTypeMapper.selectBatchIds(ids);
130 131 }
131 132  
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
... ... @@ -184,7 +184,8 @@ public class CrmContractServiceImpl implements CrmContractService {
184 184 return BeanUtils.toBean(product, CrmContractProductDO.class)
185 185 .setId(null).setProductId(productItem.getId()).setContractId(contractId)
186 186 .setCount(productItem.getCount()).setDiscountPercent(productItem.getDiscountPercent())
187   - .setTotalPrice(MoneyUtils.calculator(product.getPrice(), productItem.getCount(), productItem.getDiscountPercent()));
  187 + // TODO 芋艿:这里临时注释掉
  188 + .setTotalPrice(MoneyUtils.calculator(null, productItem.getCount(), productItem.getDiscountPercent()));
188 189 });
189 190 }
190 191  
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductCategoryService.java
... ... @@ -3,10 +3,13 @@ package cn.iocoder.yudao.module.crm.service.product;
3 3 import cn.iocoder.yudao.module.crm.controller.admin.product.vo.category.CrmProductCategoryCreateReqVO;
4 4 import cn.iocoder.yudao.module.crm.controller.admin.product.vo.category.CrmProductCategoryListReqVO;
5 5 import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO;
6   -
7 6 import jakarta.validation.Valid;
  7 +
8 8 import java.util.Collection;
9 9 import java.util.List;
  10 +import java.util.Map;
  11 +
  12 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
10 13  
11 14 /**
12 15 * CRM 产品分类 Service 接口
... ... @@ -61,4 +64,14 @@ public interface CrmProductCategoryService {
61 64 */
62 65 List<CrmProductCategoryDO> getProductCategoryList(Collection<Long> ids);
63 66  
  67 + /**
  68 + * 获得产品分类 Map
  69 + *
  70 + * @param ids 编号数组
  71 + * @return 产品分类 Map
  72 + */
  73 + default Map<Long, CrmProductCategoryDO> getProductCategoryMap(Collection<Long> ids) {
  74 + return convertMap(getProductCategoryList(ids), CrmProductCategoryDO::getId);
  75 + }
  76 +
64 77 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductService.java
... ... @@ -8,6 +8,9 @@ import jakarta.validation.Valid;
8 8  
9 9 import java.util.Collection;
10 10 import java.util.List;
  11 +import java.util.Map;
  12 +
  13 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
11 14  
12 15 /**
13 16 * CRM 产品 Service 接口
... ... @@ -55,27 +58,45 @@ public interface CrmProductService {
55 58 List<CrmProductDO> getProductList(Collection<Long> ids);
56 59  
57 60 /**
  61 + * 获得产品 Map
  62 + *
  63 + * @param ids 编号
  64 + * @return 产品 Map
  65 + */
  66 + default Map<Long, CrmProductDO> getProductMap(Collection<Long> ids) {
  67 + return convertMap(getProductList(ids), CrmProductDO::getId);
  68 + }
  69 +
  70 + /**
58 71 * 获得产品分页
59 72 *
60 73 * @param pageReqVO 分页查询
61 74 * @return 产品分页
62 75 */
63   - PageResult<CrmProductDO> getProductPage(CrmProductPageReqVO pageReqVO, Long userId);
  76 + PageResult<CrmProductDO> getProductPage(CrmProductPageReqVO pageReqVO);
64 77  
65 78 /**
66   - * 获得产品
  79 + * 获得产品数量
67 80 *
68 81 * @param categoryId 分类编号
69 82 * @return 产品
70 83 */
71   - CrmProductDO getProductByCategoryId(Long categoryId);
  84 + Long getProductByCategoryId(Long categoryId);
72 85  
73 86 /**
74   - * 获得产品列表
  87 + * 获得指定状态的产品列表
  88 + *
  89 + * @param status 状态
  90 + * @return 产品列表
  91 + */
  92 + List<CrmProductDO> getProductListByStatus(Integer status);
  93 +
  94 + /**
  95 + * 校验产品们的有效性
75 96 *
76   - * @param ids 产品编号
  97 + * @param ids 编号数组
77 98 * @return 产品列表
78 99 */
79   - List<CrmProductDO> getProductListByIds(Collection<Long> ids);
  100 + List<CrmProductDO> validProductList(Collection<Long> ids);
80 101  
81 102 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductServiceImpl.java
1 1 package cn.iocoder.yudao.module.crm.service.product;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
4   -import cn.hutool.core.collection.ListUtil;
  4 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
7 7 import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductPageReqVO;
... ... @@ -15,7 +15,6 @@ import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPerm
15 15 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
16 16 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
17 17 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
18   -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
19 18 import com.mzt.logapi.context.LogRecordContext;
20 19 import com.mzt.logapi.service.impl.DiffParseFunction;
21 20 import com.mzt.logapi.starter.annotation.LogRecord;
... ... @@ -27,8 +26,10 @@ import org.springframework.validation.annotation.Validated;
27 26 import java.util.Collection;
28 27 import java.util.Collections;
29 28 import java.util.List;
  29 +import java.util.Map;
30 30  
31 31 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  32 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
32 33 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
33 34 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
34 35  
... ... @@ -138,25 +139,41 @@ public class CrmProductServiceImpl implements CrmProductService {
138 139 }
139 140  
140 141 @Override
141   - public List<CrmProductDO> getProductList(Collection<Long> ids) {
142   - if (CollUtil.isEmpty(ids)) {
143   - return ListUtil.empty();
144   - }
145   - return productMapper.selectBatchIds(ids);
  142 + public PageResult<CrmProductDO> getProductPage(CrmProductPageReqVO pageReqVO) {
  143 + return productMapper.selectPage(pageReqVO);
  144 + }
  145 +
  146 + @Override
  147 + public Long getProductByCategoryId(Long categoryId) {
  148 + return productMapper.selectCountByCategoryId(categoryId);
146 149 }
147 150  
148 151 @Override
149   - public PageResult<CrmProductDO> getProductPage(CrmProductPageReqVO pageReqVO, Long userId) {
150   - return productMapper.selectPage(pageReqVO, userId);
  152 + public List<CrmProductDO> getProductListByStatus(Integer status) {
  153 + return productMapper.selectListByStatus(status);
151 154 }
152 155  
153 156 @Override
154   - public CrmProductDO getProductByCategoryId(Long categoryId) {
155   - return productMapper.selectOne(new LambdaQueryWrapper<CrmProductDO>().eq(CrmProductDO::getCategoryId, categoryId));
  157 + public List<CrmProductDO> validProductList(Collection<Long> ids) {
  158 + if (CollUtil.isEmpty(ids)) {
  159 + return Collections.emptyList();
  160 + }
  161 + List<CrmProductDO> list = productMapper.selectBatchIds(ids);
  162 + Map<Long, CrmProductDO> productMap = convertMap(list, CrmProductDO::getId);
  163 + for (Long id : ids) {
  164 + CrmProductDO product = productMap.get(id);
  165 + if (productMap.get(id) == null) {
  166 + throw exception(PRODUCT_NOT_EXISTS);
  167 + }
  168 + if (CommonStatusEnum.isDisable(product.getStatus())) {
  169 + throw exception(PRODUCT_NOT_ENABLE, product.getName());
  170 + }
  171 + }
  172 + return list;
156 173 }
157 174  
158 175 @Override
159   - public List<CrmProductDO> getProductListByIds(Collection<Long> ids) {
  176 + public List<CrmProductDO> getProductList(Collection<Long> ids) {
160 177 if (CollUtil.isEmpty(ids)) {
161 178 return Collections.emptyList();
162 179 }
... ...