CRM:完善商机的列表

This commit is contained in:
YunaiV 2024-02-21 18:45:10 +08:00
parent 7dd35f3295
commit e01dda9baf
28 changed files with 527 additions and 438 deletions

View File

@ -67,6 +67,7 @@ public interface ErrorCodeConstants {
// ========== 产品 1_020_008_000 ========== // ========== 产品 1_020_008_000 ==========
ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1_020_008_000, "产品不存在"); ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1_020_008_000, "产品不存在");
ErrorCode PRODUCT_NO_EXISTS = new ErrorCode(1_020_008_001, "产品编号已存在"); ErrorCode PRODUCT_NO_EXISTS = new ErrorCode(1_020_008_001, "产品编号已存在");
ErrorCode PRODUCT_NOT_ENABLE = new ErrorCode(1_020_008_002, "产品【{}】已禁用");
// ========== 产品分类 1_020_009_000 ========== // ========== 产品分类 1_020_009_000 ==========
ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_020_009_000, "产品分类不存在"); ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_020_009_000, "产品分类不存在");

View File

@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.crm.controller.admin.business;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; 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
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessRespVO; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessSaveReqVO; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessSaveReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO;
import cn.iocoder.yudao.module.crm.convert.business.CrmBusinessConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessStatusService; import cn.iocoder.yudao.module.crm.service.business.CrmBusinessStatusService;
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessStatusTypeService; import cn.iocoder.yudao.module.crm.service.business.CrmBusinessStatusTypeService;
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
import cn.iocoder.yudao.module.crm.service.product.CrmProductService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
@ -30,13 +38,15 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.io.IOException; import java.io.IOException;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CUSTOMER_NOT_EXISTS; import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CUSTOMER_NOT_EXISTS;
@ -55,6 +65,13 @@ public class CrmBusinessController {
private CrmBusinessStatusTypeService businessStatusTypeService; private CrmBusinessStatusTypeService businessStatusTypeService;
@Resource @Resource
private CrmBusinessStatusService businessStatusService; private CrmBusinessStatusService businessStatusService;
@Resource
private CrmProductService productService;
@Resource
private AdminUserApi adminUserApi;
@Resource
private DeptApi deptApi;
@PostMapping("/create") @PostMapping("/create")
@Operation(summary = "创建商机") @Operation(summary = "创建商机")
@ -86,9 +103,25 @@ public class CrmBusinessController {
@PreAuthorize("@ss.hasPermission('crm:business:query')") @PreAuthorize("@ss.hasPermission('crm:business:query')")
public CommonResult<CrmBusinessRespVO> getBusiness(@RequestParam("id") Long id) { public CommonResult<CrmBusinessRespVO> getBusiness(@RequestParam("id") Long id) {
CrmBusinessDO business = businessService.getBusiness(id); CrmBusinessDO business = businessService.getBusiness(id);
return success(BeanUtils.toBean(business, CrmBusinessRespVO.class)); return success(buildBusinessDetail(business));
} }
private CrmBusinessRespVO buildBusinessDetail(CrmBusinessDO business) {
if (business == null) {
return null;
}
CrmBusinessRespVO businessVO = buildBusinessDetailList(Collections.singletonList(business)).get(0);
// 拼接产品项
List<CrmBusinessProductDO> businessProducts = businessService.getBusinessProductListByBusinessId(businessVO.getId());
Map<Long, CrmProductDO> productMap = productService.getProductMap(
convertSet(businessProducts, CrmBusinessProductDO::getProductId));
businessVO.setProducts(BeanUtils.toBean(businessProducts, CrmBusinessRespVO.Product.class, businessProductVO ->
MapUtils.findAndThen(productMap, businessProductVO.getProductId(),
product -> businessProductVO.setProductNo(product.getNo()).setProductUnit(product.getUnit()))));
return businessVO;
}
// TODO 芋艿处理下
@GetMapping("/list-by-ids") @GetMapping("/list-by-ids")
@Operation(summary = "获得商机列表") @Operation(summary = "获得商机列表")
@Parameter(name = "ids", description = "编号", required = true, example = "[1024]") @Parameter(name = "ids", description = "编号", required = true, example = "[1024]")
@ -97,6 +130,7 @@ public class CrmBusinessController {
return success(BeanUtils.toBean(businessService.getBusinessList(ids, getLoginUserId()), CrmBusinessRespVO.class)); return success(BeanUtils.toBean(businessService.getBusinessList(ids, getLoginUserId()), CrmBusinessRespVO.class));
} }
// TODO 芋艿处理下
@GetMapping("/simple-all-list") @GetMapping("/simple-all-list")
@Operation(summary = "获得联系人的精简列表") @Operation(summary = "获得联系人的精简列表")
@PreAuthorize("@ss.hasPermission('crm:contact:query')") @PreAuthorize("@ss.hasPermission('crm:contact:query')")
@ -113,7 +147,7 @@ public class CrmBusinessController {
@PreAuthorize("@ss.hasPermission('crm:business:query')") @PreAuthorize("@ss.hasPermission('crm:business:query')")
public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessPage(@Valid CrmBusinessPageReqVO pageVO) { public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessPage(@Valid CrmBusinessPageReqVO pageVO) {
PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPage(pageVO, getLoginUserId()); PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPage(pageVO, getLoginUserId());
return success(buildBusinessDetailPageResult(pageResult)); return success(new PageResult<>(buildBusinessDetailList(pageResult.getList()), pageResult.getTotal()));
} }
@GetMapping("/page-by-customer") @GetMapping("/page-by-customer")
@ -123,7 +157,7 @@ public class CrmBusinessController {
throw exception(CUSTOMER_NOT_EXISTS); throw exception(CUSTOMER_NOT_EXISTS);
} }
PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPageByCustomerId(pageReqVO); PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPageByCustomerId(pageReqVO);
return success(buildBusinessDetailPageResult(pageResult)); return success(new PageResult<>(buildBusinessDetailList(pageResult.getList()), pageResult.getTotal()));
} }
@GetMapping("/page-by-contact") @GetMapping("/page-by-contact")
@ -131,7 +165,7 @@ public class CrmBusinessController {
@PreAuthorize("@ss.hasPermission('crm:business:query')") @PreAuthorize("@ss.hasPermission('crm:business:query')")
public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessContactPage(@Valid CrmBusinessPageReqVO pageReqVO) { public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessContactPage(@Valid CrmBusinessPageReqVO pageReqVO) {
PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPageByContact(pageReqVO); PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPageByContact(pageReqVO);
return success(buildBusinessDetailPageResult(pageResult)); return success(new PageResult<>(buildBusinessDetailList(pageResult.getList()), pageResult.getTotal()));
} }
@GetMapping("/export-excel") @GetMapping("/export-excel")
@ -141,29 +175,43 @@ public class CrmBusinessController {
public void exportBusinessExcel(@Valid CrmBusinessPageReqVO exportReqVO, public void exportBusinessExcel(@Valid CrmBusinessPageReqVO exportReqVO,
HttpServletResponse response) throws IOException { HttpServletResponse response) throws IOException {
exportReqVO.setPageSize(PAGE_SIZE_NONE); exportReqVO.setPageSize(PAGE_SIZE_NONE);
PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPage(exportReqVO, getLoginUserId()); List<CrmBusinessDO> list = businessService.getBusinessPage(exportReqVO, getLoginUserId()).getList();
// 导出 Excel // 导出 Excel
ExcelUtils.write(response, "商机.xls", "数据", CrmBusinessRespVO.class, ExcelUtils.write(response, "商机.xls", "数据", CrmBusinessRespVO.class,
buildBusinessDetailPageResult(pageResult).getList()); buildBusinessDetailList(list));
} }
/** private List<CrmBusinessRespVO> buildBusinessDetailList(List<CrmBusinessDO> list) {
* 构建详细的商机分页结果 if (CollUtil.isEmpty(list)) {
* return Collections.emptyList();
* @param pageResult 简单的商机分页结果
* @return 详细的商机分页结果
*/
private PageResult<CrmBusinessRespVO> buildBusinessDetailPageResult(PageResult<CrmBusinessDO> pageResult) {
if (CollUtil.isEmpty(pageResult.getList())) {
return PageResult.empty(pageResult.getTotal());
} }
List<CrmBusinessStatusTypeDO> statusTypeList = businessStatusTypeService.getBusinessStatusTypeList( // 1.1 获取客户列表
convertSet(pageResult.getList(), CrmBusinessDO::getStatusTypeId)); Map<Long, CrmCustomerDO> customerMap = customerService.getCustomerMap(
List<CrmBusinessStatusDO> statusList = businessStatusService.getBusinessStatusList( convertSet(list, CrmBusinessDO::getCustomerId));
convertSet(pageResult.getList(), CrmBusinessDO::getStatusId)); // 1.2 获取创建人负责人列表
List<CrmCustomerDO> customerList = customerService.getCustomerList( Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(list,
convertSet(pageResult.getList(), CrmBusinessDO::getCustomerId)); contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));
return CrmBusinessConvert.INSTANCE.convertPage(pageResult, customerList, statusTypeList, statusList); Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
// 1.3 获得商机状态组
Map<Long, CrmBusinessStatusTypeDO> statusTypeMap = businessStatusTypeService.getBusinessStatusTypeMap(
convertSet(list, CrmBusinessDO::getStatusTypeId));
Map<Long, CrmBusinessStatusDO> statusMap = businessStatusService.getBusinessStatusMap(
convertSet(list, CrmBusinessDO::getStatusId));
// 2. 拼接数据
return BeanUtils.toBean(list, CrmBusinessRespVO.class, businessVO -> {
// 2.1 设置客户名称
MapUtils.findAndThen(customerMap, businessVO.getCustomerId(), customer -> businessVO.setCustomerName(customer.getName()));
// 2.2 设置创建人负责人名称
MapUtils.findAndThen(userMap, NumberUtils.parseLong(businessVO.getCreator()),
user -> businessVO.setCreatorName(user.getNickname()));
MapUtils.findAndThen(userMap, businessVO.getOwnerUserId(), user -> {
businessVO.setOwnerUserName(user.getNickname());
MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> businessVO.setOwnerUserDeptName(dept.getName()));
});
// 2.3 设置商机状态
MapUtils.findAndThen(statusTypeMap, businessVO.getStatusTypeId(), statusType -> businessVO.setStatusTypeName(statusType.getName()));
MapUtils.findAndThen(statusMap, businessVO.getStatusId(), status -> businessVO.setStatusName(status.getName()));
});
} }
@PutMapping("/transfer") @PutMapping("/transfer")

View File

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

View File

@ -1,69 +1,144 @@
package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business; package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat; import lombok.NoArgsConstructor;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; @Schema(description = "管理后台 - CRM 商机 Response VO")
@Schema(description = "管理后台 - 商机 Response VO")
@Data @Data
@ExcelIgnoreUnannotated
public class CrmBusinessRespVO { public class CrmBusinessRespVO {
@Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "32129") @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "32129")
@ExcelProperty("编号")
private Long id; private Long id;
@Schema(description = "商机名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") @Schema(description = "商机名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
@NotNull(message = "商机名称不能为空") @ExcelProperty("商机名称")
private String name; private String name;
@Schema(description = "商机状态类型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "25714")
@NotNull(message = "商机状态类型不能为空")
private Long statusTypeId;
@Schema(description = "商机状态编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320")
@NotNull(message = "商机状态不能为空")
private Long statusId;
@Schema(description = "下次联系时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime contactNextTime;
@Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10299") @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10299")
@NotNull(message = "客户不能为空")
private Long customerId; private Long customerId;
@Schema(description = "预计成交日期")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime dealTime;
@Schema(description = "商机金额", example = "12371")
private Integer price;
// TODO @ljileo折扣使用 Integer 类型存储时默认 * 100展示的时候前端需要 / 100避免精度丢失问题
@Schema(description = "整单折扣")
private Integer discountPercent;
@Schema(description = "产品总金额", example = "12025")
private BigDecimal productPrice;
@Schema(description = "备注", example = "随便")
private String remark;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
@Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") @Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
@ExcelProperty("客户名称")
private String customerName; private String customerName;
@Schema(description = "跟进状态", requiredMode = Schema.RequiredMode.REQUIRED, example ="true")
@ExcelProperty("跟进状态")
private Boolean followUpStatus;
@Schema(description = "最后跟进时间")
@ExcelProperty("最后跟进时间")
private LocalDateTime contactLastTime;
@Schema(description = "下次联系时间")
@ExcelProperty("下次联系时间")
private LocalDateTime contactNextTime;
@Schema(description = "负责人的用户编号", example = "25682")
@ExcelProperty("负责人的用户编号")
private Long ownerUserId;
@Schema(description = "负责人名字", example = "25682")
@ExcelProperty("负责人名字")
private String ownerUserName;
@Schema(description = "负责人部门")
@ExcelProperty("负责人部门")
private String ownerUserDeptName;
@Schema(description = "商机状态类型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "25714")
private Long statusTypeId;
@Schema(description = "状态类型名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "进行中") @Schema(description = "状态类型名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "进行中")
@ExcelProperty("商机状态类型")
private String statusTypeName; private String statusTypeName;
@Schema(description = "商机状态编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320")
private Long statusId;
@Schema(description = "状态名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "跟进中") @Schema(description = "状态名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "跟进中")
@ExcelProperty("商机状态")
private String statusName; private String statusName;
@Schema
@ExcelProperty("1赢单2输单3无效")
private Integer endStatus;
@ExcelProperty("结束时的备注")
private String endRemark;
@Schema(description = "预计成交日期")
@ExcelProperty("预计成交日期")
private LocalDateTime dealTime;
@Schema(description = "产品总金额", example = "12025")
@ExcelProperty("产品总金额")
private BigDecimal totalProductPrice;
@Schema(description = "整单折扣")
@ExcelProperty("整单折扣")
private BigDecimal discountPercent;
@Schema(description = "商机总金额", example = "12371")
@ExcelProperty("商机总金额")
private BigDecimal totalPrice;
@Schema(description = "备注", example = "随便")
@ExcelProperty("备注")
private String remark;
@Schema(description = "创建人", example = "1024")
@ExcelProperty("创建人")
private String creator;
@Schema(description = "创建人名字", example = "芋道源码")
@ExcelProperty("创建人名字")
private String creatorName;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
@Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("更新时间")
private LocalDateTime updateTime;
@Schema(description = "产品列表")
private List<Product> products;
@Schema(description = "产品列表")
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class Product {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "888")
private Long id;
@Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20529")
private Long productId;
@Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
private String productName;
@Schema(description = "产品条码", requiredMode = Schema.RequiredMode.REQUIRED, example = "20529")
private String productNo;
@Schema(description = "产品单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
private Integer productUnit;
@Schema(description = "产品单价", requiredMode = Schema.RequiredMode.REQUIRED, example = "123.00")
private BigDecimal productPrice;
@Schema(description = "合同价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "123.00")
private BigDecimal businessPrice;
@Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911")
private Integer count;
@Schema(description = "总计价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "123.00")
private BigDecimal totalPrice;
}
} }

View File

@ -1,8 +1,7 @@
package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business; package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.crm.enums.business.CrmBizEndStatus;
import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerParseFunction; import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerParseFunction;
import cn.iocoder.yudao.module.crm.framework.operatelog.core.SysAdminUserParseFunction;
import com.mzt.logapi.starter.annotation.DiffLogField; import com.mzt.logapi.starter.annotation.DiffLogField;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
@ -29,75 +28,68 @@ public class CrmBusinessSaveReqVO {
@NotNull(message = "商机名称不能为空") @NotNull(message = "商机名称不能为空")
private String name; private String name;
@Schema(description = "商机状态类型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "25714") @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10299")
@DiffLogField(name = "商机状态") @DiffLogField(name = "客户", function = CrmCustomerParseFunction.NAME)
@NotNull(message = "商机状态类型不能为空") @NotNull(message = "客户不能为空")
private Long statusTypeId; private Long customerId;
@Schema(description = "商机状态编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320")
@DiffLogField(name = "商机状态")
@NotNull(message = "商机状态不能为空")
private Long statusId;
@Schema(description = "下次联系时间") @Schema(description = "下次联系时间")
@DiffLogField(name = "下次联系时间") @DiffLogField(name = "下次联系时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime contactNextTime; private LocalDateTime contactNextTime;
@Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10299") @Schema(description = "负责人用户编号", example = "14334")
@DiffLogField(name = "客户", function = CrmCustomerParseFunction.NAME) @NotNull(message = "负责人不能为空")
@NotNull(message = "客户不能为空") @DiffLogField(name = "负责人", function = SysAdminUserParseFunction.NAME)
private Long customerId; private Long ownerUserId;
@Schema(description = "商机状态类型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "25714")
@DiffLogField(name = "商机状态")
@NotNull(message = "商机状态类型不能为空")
private Long statusTypeId;
@Schema(description = "预计成交日期") @Schema(description = "预计成交日期")
@DiffLogField(name = "预计成交日期") @DiffLogField(name = "预计成交日期")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime dealTime; private LocalDateTime dealTime;
@Schema(description = "商机金额", example = "12371")
@DiffLogField(name = "商机金额")
private Integer price;
@Schema(description = "整单折扣") @Schema(description = "整单折扣")
@DiffLogField(name = "整单折扣") @DiffLogField(name = "整单折扣")
private Integer discountPercent; @NotNull(message = "整单折扣不能为空")
private BigDecimal discountPercent;
@Schema(description = "产品总金额", example = "12025")
@DiffLogField(name = "产品总金额")
private BigDecimal productPrice;
@Schema(description = "备注", example = "随便") @Schema(description = "备注", example = "随便")
@DiffLogField(name = "备注") @DiffLogField(name = "备注")
private String remark; private String remark;
@Schema(description = "结束状态", example = "1")
@InEnum(CrmBizEndStatus.class)
private Integer endStatus;
@Schema(description = "联系人编号", example = "110") @Schema(description = "联系人编号", example = "110")
private Long contactId; // 使用场景联系人详情添加商机时如果需要关联两者需要传递 contactId 字段 private Long contactId; // 使用场景联系人详情添加商机时如果需要关联两者需要传递 contactId 字段
// TODO @puhui999传递 items 就行啦
@Schema(description = "产品列表") @Schema(description = "产品列表")
private List<CrmBusinessProductItem> productItems; private List<Product> products;
@Schema(description = "产品列表") @Schema(description = "产品列表")
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public static class CrmBusinessProductItem { public static class Product {
@Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20529") @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20529")
@NotNull(message = "产品编号不能为空") @NotNull(message = "产品编号不能为空")
private Long id; private Long productId;
@Schema(description = "产品单价", requiredMode = Schema.RequiredMode.REQUIRED, example = "123.00")
@NotNull(message = "产品单价不能为空")
private BigDecimal productPrice;
@Schema(description = "合同价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "123.00")
@NotNull(message = "合同价格不能为空")
private BigDecimal businessPrice;
@Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911") @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911")
@NotNull(message = "产品数量不能为空") @NotNull(message = "产品数量不能为空")
private Integer count; private Integer count;
@Schema(description = "产品折扣")
private Integer discountPercent;
} }
} }

View File

@ -179,7 +179,7 @@ public class CrmContractController {
if (contractList.size() == 1) { if (contractList.size() == 1) {
List<CrmContractProductDO> contractProductList = contractService.getContractProductListByContractId(contractList.get(0).getId()); List<CrmContractProductDO> contractProductList = contractService.getContractProductListByContractId(contractList.get(0).getId());
contractProductMap = convertMap(contractProductList, CrmContractProductDO::getProductId); contractProductMap = convertMap(contractProductList, CrmContractProductDO::getProductId);
productList = productService.getProductListByIds(convertSet(contractProductList, CrmContractProductDO::getProductId)); productList = productService.getProductList(convertSet(contractProductList, CrmContractProductDO::getProductId));
} }
return CrmContractConvert.INSTANCE.convertList(contractList, userMap, customerList, contactMap, businessMap, contractProductMap, productList); return CrmContractConvert.INSTANCE.convertList(contractList, userMap, customerList, contactMap, businessMap, contractProductMap, productList);
} }

View File

@ -1,16 +1,17 @@
package cn.iocoder.yudao.module.crm.controller.admin.product; package cn.iocoder.yudao.module.crm.controller.admin.product;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.SetUtils; import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductPageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductRespVO; import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductSaveReqVO; import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductSaveReqVO;
import cn.iocoder.yudao.module.crm.convert.product.CrmProductConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO; import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
import cn.iocoder.yudao.module.crm.service.product.CrmProductCategoryService; import cn.iocoder.yudao.module.crm.service.product.CrmProductCategoryService;
@ -34,10 +35,9 @@ import java.util.Map;
import java.util.stream.Stream; import java.util.stream.Stream;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; import static java.util.Collections.singletonList;
@Tag(name = "管理后台 - CRM 产品") @Tag(name = "管理后台 - CRM 产品")
@RestController @RestController
@ -49,6 +49,7 @@ public class CrmProductController {
private CrmProductService productService; private CrmProductService productService;
@Resource @Resource
private CrmProductCategoryService productCategoryService; private CrmProductCategoryService productCategoryService;
@Resource @Resource
private AdminUserApi adminUserApi; private AdminUserApi adminUserApi;
@ -82,21 +83,30 @@ public class CrmProductController {
@PreAuthorize("@ss.hasPermission('crm:product:query')") @PreAuthorize("@ss.hasPermission('crm:product:query')")
public CommonResult<CrmProductRespVO> getProduct(@RequestParam("id") Long id) { public CommonResult<CrmProductRespVO> getProduct(@RequestParam("id") Long id) {
CrmProductDO product = productService.getProduct(id); CrmProductDO product = productService.getProduct(id);
return success(buildProductDetail(product));
}
private CrmProductRespVO buildProductDetail(CrmProductDO product) {
if (product == null) { if (product == null) {
return success(null); return null;
} }
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap( return buildProductDetailList(singletonList(product)).get(0);
SetUtils.asSet(Long.valueOf(product.getCreator()), product.getOwnerUserId())); }
CrmProductCategoryDO category = productCategoryService.getProductCategory(product.getCategoryId());
return success(CrmProductConvert.INSTANCE.convert(product, userMap, category)); @GetMapping("/simple-list")
@Operation(summary = "获得产品精简列表", description = "只包含被开启的产品,主要用于前端的下拉选项")
public CommonResult<List<CrmProductRespVO>> getProductSimpleList() {
List<CrmProductDO> list = productService.getProductListByStatus(CommonStatusEnum.ENABLE.getStatus());
return success(convertList(list, product -> new CrmProductRespVO().setId(product.getId()).setName(product.getName())
.setUnit(product.getUnit()).setNo(product.getNo()).setPrice(product.getPrice())));
} }
@GetMapping("/page") @GetMapping("/page")
@Operation(summary = "获得产品分页") @Operation(summary = "获得产品分页")
@PreAuthorize("@ss.hasPermission('crm:product:query')") @PreAuthorize("@ss.hasPermission('crm:product:query')")
public CommonResult<PageResult<CrmProductRespVO>> getProductPage(@Valid CrmProductPageReqVO pageVO) { public CommonResult<PageResult<CrmProductRespVO>> getProductPage(@Valid CrmProductPageReqVO pageVO) {
PageResult<CrmProductDO> pageResult = productService.getProductPage(pageVO, getLoginUserId()); PageResult<CrmProductDO> pageResult = productService.getProductPage(pageVO);
return success(new PageResult<>(getProductDetailList(pageResult.getList()), pageResult.getTotal())); return success(new PageResult<>(buildProductDetailList(pageResult.getList()), pageResult.getTotal()));
} }
@GetMapping("/export-excel") @GetMapping("/export-excel")
@ -106,21 +116,30 @@ public class CrmProductController {
public void exportProductExcel(@Valid CrmProductPageReqVO exportReqVO, public void exportProductExcel(@Valid CrmProductPageReqVO exportReqVO,
HttpServletResponse response) throws IOException { HttpServletResponse response) throws IOException {
exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<CrmProductDO> list = productService.getProductPage(exportReqVO, getLoginUserId()).getList(); List<CrmProductDO> list = productService.getProductPage(exportReqVO).getList();
// 导出 Excel // 导出 Excel
ExcelUtils.write(response, "产品.xls", "数据", CrmProductRespVO.class, ExcelUtils.write(response, "产品.xls", "数据", CrmProductRespVO.class,
getProductDetailList(list)); buildProductDetailList(list));
} }
private List<CrmProductRespVO> getProductDetailList(List<CrmProductDO> list) { private List<CrmProductRespVO> buildProductDetailList(List<CrmProductDO> list) {
if (CollUtil.isEmpty(list)) { if (CollUtil.isEmpty(list)) {
return Collections.emptyList(); return Collections.emptyList();
} }
// 1.1 获得用户信息
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap( Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
convertSetByFlatMap(list, user -> Stream.of(Long.valueOf(user.getCreator()), user.getOwnerUserId()))); convertSetByFlatMap(list, user -> Stream.of(Long.valueOf(user.getCreator()), user.getOwnerUserId())));
List<CrmProductCategoryDO> productCategoryList = productCategoryService.getProductCategoryList( // 1.2 获得分类信息
Map<Long, CrmProductCategoryDO> categoryMap = productCategoryService.getProductCategoryMap(
convertSet(list, CrmProductDO::getCategoryId)); convertSet(list, CrmProductDO::getCategoryId));
return CrmProductConvert.INSTANCE.convertList(list, userMap, productCategoryList); // 2. 拼接数据
return BeanUtils.toBean(list, CrmProductRespVO.class, productVO -> {
// 2.1 设置用户信息
MapUtils.findAndThen(userMap, productVO.getOwnerUserId(), user -> productVO.setOwnerUserName(user.getNickname()));
MapUtils.findAndThen(userMap, Long.valueOf(productVO.getCreator()), user -> productVO.setCreatorName(user.getNickname()));
// 2.2 设置分类名称
MapUtils.findAndThen(categoryMap, productVO.getCategoryId(), category -> productVO.setCategoryName(category.getName()));
});
} }
} }

View File

@ -8,6 +8,7 @@ import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@Schema(description = "管理后台 - CRM 产品 Response VO") @Schema(description = "管理后台 - CRM 产品 Response VO")
@ -34,7 +35,7 @@ public class CrmProductRespVO {
@Schema(description = "价格, 单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911") @Schema(description = "价格, 单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911")
@ExcelProperty("价格,单位:分") @ExcelProperty("价格,单位:分")
private Long price; private BigDecimal price;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "上架") @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "上架")
@ExcelProperty(value = "单位", converter = DictConvert.class) @ExcelProperty(value = "单位", converter = DictConvert.class)

View File

@ -7,6 +7,8 @@ import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import lombok.Data; import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - CRM 产品创建/修改 Request VO") @Schema(description = "管理后台 - CRM 产品创建/修改 Request VO")
@Data @Data
public class CrmProductSaveReqVO { public class CrmProductSaveReqVO {
@ -28,10 +30,10 @@ public class CrmProductSaveReqVO {
@DiffLogField(name = "单位", function = CrmProductUnitParseFunction.NAME) @DiffLogField(name = "单位", function = CrmProductUnitParseFunction.NAME)
private Integer unit; private Integer unit;
@Schema(description = "价格, 单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911") @Schema(description = "价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911")
@NotNull(message = "价格不能为空") @NotNull(message = "价格不能为空")
@DiffLogField(name = "价格") @DiffLogField(name = "价格")
private Long price; private BigDecimal price;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "上架") @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "上架")
@NotNull(message = "状态不能为空") @NotNull(message = "状态不能为空")

View File

@ -1,14 +1,8 @@
package cn.iocoder.yudao.module.crm.convert.business; package cn.iocoder.yudao.module.crm.convert.business;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO; import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
@ -16,9 +10,6 @@ import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers; import org.mapstruct.factory.Mappers;
import java.util.List; import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
/** /**
* 商机 Convert * 商机 Convert
@ -33,20 +24,6 @@ public interface CrmBusinessConvert {
@Mapping(target = "bizId", source = "reqVO.id") @Mapping(target = "bizId", source = "reqVO.id")
CrmPermissionTransferReqBO convert(CrmBusinessTransferReqVO reqVO, Long userId); CrmPermissionTransferReqBO convert(CrmBusinessTransferReqVO reqVO, Long userId);
default PageResult<CrmBusinessRespVO> convertPage(PageResult<CrmBusinessDO> pageResult, List<CrmCustomerDO> customerList,
List<CrmBusinessStatusTypeDO> statusTypeList, List<CrmBusinessStatusDO> statusList) {
PageResult<CrmBusinessRespVO> voPageResult = BeanUtils.toBean(pageResult, CrmBusinessRespVO.class);
// 拼接关联字段
Map<Long, String> customerMap = convertMap(customerList, CrmCustomerDO::getId, CrmCustomerDO::getName);
Map<Long, String> statusTypeMap = convertMap(statusTypeList, CrmBusinessStatusTypeDO::getId, CrmBusinessStatusTypeDO::getName);
Map<Long, String> statusMap = convertMap(statusList, CrmBusinessStatusDO::getId, CrmBusinessStatusDO::getName);
voPageResult.getList().forEach(type -> type
.setCustomerName(customerMap.get(type.getCustomerId()))
.setStatusTypeName(statusTypeMap.get(type.getStatusTypeId()))
.setStatusName(statusMap.get(type.getStatusId())));
return voPageResult;
}
@Mapping(target = "id", source = "reqBO.bizId") @Mapping(target = "id", source = "reqBO.bizId")
CrmBusinessDO convert(CrmUpdateFollowUpReqBO reqBO); CrmBusinessDO convert(CrmUpdateFollowUpReqBO reqBO);

View File

@ -1,46 +0,0 @@
package cn.iocoder.yudao.module.crm.convert.product;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductRespVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
/**
* 产品 Convert
*
* @author ZanGe
*/
@Mapper
public interface CrmProductConvert {
CrmProductConvert INSTANCE = Mappers.getMapper(CrmProductConvert.class);
default List<CrmProductRespVO> convertList(List<CrmProductDO> list,
Map<Long, AdminUserRespDTO> userMap,
List<CrmProductCategoryDO> categoryList) {
Map<Long, CrmProductCategoryDO> categoryMap = convertMap(categoryList, CrmProductCategoryDO::getId);
return CollectionUtils.convertList(list,
product -> convert(product, userMap, categoryMap.get(product.getCategoryId())));
}
default CrmProductRespVO convert(CrmProductDO product,
Map<Long, AdminUserRespDTO> userMap, CrmProductCategoryDO category) {
CrmProductRespVO productVO = BeanUtils.toBean(product, CrmProductRespVO.class);
Optional.ofNullable(category).ifPresent(c -> productVO.setCategoryName(c.getName()));
MapUtils.findAndThen(userMap, productVO.getOwnerUserId(), user -> productVO.setOwnerUserName(user.getNickname()));
MapUtils.findAndThen(userMap, Long.valueOf(productVO.getCreator()), user -> productVO.setCreatorName(user.getNickname()));
return productVO;
}
}

View File

@ -8,10 +8,11 @@ import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*; import lombok.*;
import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
/** /**
* 商机 DO * CRM 商机 DO
* *
* @author ljlleo * @author ljlleo
*/ */
@ -26,7 +27,7 @@ import java.time.LocalDateTime;
public class CrmBusinessDO extends BaseDO { public class CrmBusinessDO extends BaseDO {
/** /**
* 主键 * 编号
*/ */
@TableId @TableId
private Long id; private Long id;
@ -34,6 +35,33 @@ public class CrmBusinessDO extends BaseDO {
* 商机名称 * 商机名称
*/ */
private String name; private String name;
/**
* 客户编号
*
* 关联 {@link CrmCustomerDO#getId()}
*/
private Long customerId;
/**
* 跟进状态
*/
private Boolean followUpStatus;
/**
* 最后跟进时间
*/
private LocalDateTime contactLastTime;
/**
* 下次联系时间
*/
private LocalDateTime contactNextTime;
/**
* 负责人的用户编号
*
* 关联 AdminUserDO id 字段
*/
private Long ownerUserId;
/** /**
* 商机状态类型编号 * 商机状态类型编号
* *
@ -46,39 +74,6 @@ public class CrmBusinessDO extends BaseDO {
* 关联 {@link CrmBusinessStatusDO#getId()} * 关联 {@link CrmBusinessStatusDO#getId()}
*/ */
private Long statusId; private Long statusId;
/**
* 下次联系时间
*/
private LocalDateTime contactNextTime;
/**
* 客户编号
*
* TODO @ljileo这个字段后续要写下关联的实体哈
* 关联 {@link CrmCustomerDO#getId()}
*/
private Long customerId;
/**
* 预计成交日期
*/
private LocalDateTime dealTime;
/**
* 商机金额
*
*/
private Integer price;
/**
* 整单折扣
*
*/
private Integer discountPercent;
/**
* 产品总金额单位
*/
private Integer productPrice;
/**
* 备注
*/
private String remark;
/** /**
* 结束状态 * 结束状态
* *
@ -89,20 +84,28 @@ public class CrmBusinessDO extends BaseDO {
* 结束时的备注 * 结束时的备注
*/ */
private String endRemark; private String endRemark;
/**
* 最后跟进时间
*/
private LocalDateTime contactLastTime;
/**
* 跟进状态
*/
private Boolean followUpStatus;
/** /**
* 负责人的用户编号 * 预计成交日期
*
* 关联 AdminUserDO id 字段
*/ */
private Long ownerUserId; private LocalDateTime dealTime;
/**
* 产品总金额单位
*
* productPrice = ({@link CrmBusinessProductDO#getTotalPrice()})
*/
private BigDecimal totalProductPrice;
/**
* 整单折扣百分比
*/
private BigDecimal discountPercent;
/**
* 商机总金额单位
*/
private BigDecimal totalPrice;
/**
* 备注
*/
private String remark;
} }

View File

@ -7,9 +7,13 @@ import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*; import lombok.*;
import java.math.BigDecimal;
/** /**
* 商机产品关联表 DO * 商机产品关联表 DO
* *
* CrmBusinessDO : CrmBusinessProductDO = 1 : N
*
* @author lzxhqs * @author lzxhqs
*/ */
@TableName("crm_business_product") @TableName("crm_business_product")
@ -40,24 +44,24 @@ public class CrmBusinessProductDO extends BaseDO {
*/ */
private Long productId; private Long productId;
/** /**
* 产品单价 * 产品单价单位
*
* 冗余 {@link CrmProductDO#getPrice()}
*/ */
private Integer price; private BigDecimal productPrice;
/** /**
* 销售价格, 单位 * 合同价格, 单位
*/ */
private Integer salesPrice; private BigDecimal businessPrice;
/** /**
* 数量 * 数量
*/ */
private Integer count; private BigDecimal count;
/** /**
* 折扣 * 总计价格单位
*
* totalPrice = businessPrice * count
*/ */
private Integer discountPercent; private BigDecimal totalPrice;
/**
* 总计价格折扣后价格
*/
private Integer totalPrice;
} }

View File

@ -11,7 +11,7 @@ import lombok.*;
import java.time.LocalDateTime; import java.time.LocalDateTime;
/** /**
* 线索 DO * CRM 线索 DO
* *
* @author Wanwan * @author Wanwan
*/ */

View File

@ -8,6 +8,8 @@ import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*; import lombok.*;
import java.math.BigDecimal;
/** /**
* CRM 产品 DO * CRM 产品 DO
* *
@ -43,9 +45,9 @@ public class CrmProductDO extends BaseDO {
*/ */
private Integer unit; private Integer unit;
/** /**
* 价格单位 * 价格单位
*/ */
private Integer price; private BigDecimal price;
/** /**
* 状态 * 状态
* *

View File

@ -1,4 +0,0 @@
/**
* 商机销售机会
*/
package cn.iocoder.yudao.module.crm.dal.mysql.business;

View File

@ -1,4 +0,0 @@
/**
* 联系人
*/
package cn.iocoder.yudao.module.crm.dal.mysql.contact;

View File

@ -5,10 +5,10 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductPageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductPageReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/** /**
* CRM 产品 Mapper * CRM 产品 Mapper
* *
@ -17,21 +17,23 @@ import org.apache.ibatis.annotations.Mapper;
@Mapper @Mapper
public interface CrmProductMapper extends BaseMapperX<CrmProductDO> { public interface CrmProductMapper extends BaseMapperX<CrmProductDO> {
default PageResult<CrmProductDO> selectPage(CrmProductPageReqVO reqVO, Long userId) { default PageResult<CrmProductDO> selectPage(CrmProductPageReqVO reqVO) {
MPJLambdaWrapperX<CrmProductDO> query = new MPJLambdaWrapperX<>(); return selectPage(reqVO, new MPJLambdaWrapperX<CrmProductDO>()
// 拼接数据权限的查询条件
CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_PRODUCT.getType(),
CrmProductDO::getId, userId, null, Boolean.FALSE);
// 拼接自身的查询条件
query.selectAll(CrmProductDO.class)
.likeIfPresent(CrmProductDO::getName, reqVO.getName()) .likeIfPresent(CrmProductDO::getName, reqVO.getName())
.eqIfPresent(CrmProductDO::getStatus, reqVO.getStatus()) .eqIfPresent(CrmProductDO::getStatus, reqVO.getStatus())
.orderByDesc(CrmProductDO::getId); .orderByDesc(CrmProductDO::getId));
return selectJoinPage(reqVO, CrmProductDO.class, query);
} }
default CrmProductDO selectByNo(String no) { default CrmProductDO selectByNo(String no) {
return selectOne(CrmProductDO::getNo, no); return selectOne(CrmProductDO::getNo, no);
} }
default Long selectCountByCategoryId(Long categoryId) {
return selectCount(CrmProductDO::getCategoryId, categoryId);
}
default List<CrmProductDO> selectListByStatus(Integer status) {
return selectList(CrmProductDO::getStatus, status);
}
} }

View File

@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusi
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessSaveReqVO; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessSaveReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.service.business.bo.CrmBusinessUpdateProductReqBO; import cn.iocoder.yudao.module.crm.service.business.bo.CrmBusinessUpdateProductReqBO;
@ -90,6 +91,14 @@ public interface CrmBusinessService {
*/ */
List<CrmBusinessDO> getBusinessList(Collection<Long> ids); List<CrmBusinessDO> getBusinessList(Collection<Long> ids);
/**
* 获得指定商机编号的产品列表
*
* @param businessId 商机编号
* @return 商机产品列表
*/
List<CrmBusinessProductDO> getBusinessProductListByBusinessId(Long businessId);
/** /**
* 获得商机分页 * 获得商机分页
* *

View File

@ -2,9 +2,7 @@ package cn.iocoder.yudao.module.crm.service.business;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil; import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO;
@ -14,7 +12,6 @@ import cn.iocoder.yudao.module.crm.convert.business.CrmBusinessConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO; import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessMapper; import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessProductMapper; import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessProductMapper;
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
@ -36,14 +33,14 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_CONTRACT_EXISTS;
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_NOT_EXISTS;
import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
/** /**
@ -75,27 +72,29 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
@LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_CREATE_SUB_TYPE, bizNo = "{{#business.id}}", @LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_CREATE_SUB_TYPE, bizNo = "{{#business.id}}",
success = CRM_BUSINESS_CREATE_SUCCESS) success = CRM_BUSINESS_CREATE_SUCCESS)
public Long createBusiness(CrmBusinessSaveReqVO createReqVO, Long userId) { public Long createBusiness(CrmBusinessSaveReqVO createReqVO, Long userId) {
createReqVO.setId(null); // 1.1 校验产品项的有效性
// 1. 插入商机 List<CrmBusinessProductDO> businessProducts = validateBusinessProducts(createReqVO.getProducts());
// 1.2 TODO 芋艿校验关联字
// 2.1 插入商机
CrmBusinessDO business = BeanUtils.toBean(createReqVO, CrmBusinessDO.class).setOwnerUserId(userId); CrmBusinessDO business = BeanUtils.toBean(createReqVO, CrmBusinessDO.class).setOwnerUserId(userId);
calculateTotalPrice(business, businessProducts);
businessMapper.insert(business); businessMapper.insert(business);
// 1.2 插入商机关联商品 // 2.2 插入商机关联商品
if (CollUtil.isNotEmpty(createReqVO.getProductItems())) { // 如果有的话 if (CollUtil.isNotEmpty(businessProducts)) {
List<CrmBusinessProductDO> productList = buildBusinessProductList(createReqVO.getProductItems(), business.getId()); businessProducts.forEach(item -> item.setBusinessId(business.getId()));
businessProductMapper.insertBatch(productList); businessProductMapper.insertBatch(businessProducts);
// 更新合同商品总金额
businessMapper.updateById(new CrmBusinessDO().setId(business.getId()).setProductPrice(
getSumValue(productList, CrmBusinessProductDO::getTotalPrice, Integer::sum)));
} }
// TODO @puhui999在联系人的详情页如果直接新建商机则需要关联下这里要搞个 CrmContactBusinessDO // TODO @puhui999在联系人的详情页如果直接新建商机则需要关联下这里要搞个 CrmContactBusinessDO
createContactBusiness(business.getId(), createReqVO.getContactId()); createContactBusiness(business.getId(), createReqVO.getContactId());
// 2. 创建数据权限 // 3. 创建数据权限
// 设置当前操作的人为负责人 // 设置当前操作的人为负责人
permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_BUSINESS.getType()) permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_BUSINESS.getType())
.setBizId(business.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); .setBizId(business.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
// 3. 记录操作日志上下文 // 4. 记录操作日志上下文
LogRecordContext.putVariable("business", business); LogRecordContext.putVariable("business", business);
return business.getId(); return business.getId();
} }
@ -114,15 +113,18 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
success = CRM_BUSINESS_UPDATE_SUCCESS) success = CRM_BUSINESS_UPDATE_SUCCESS)
@CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE) @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
public void updateBusiness(CrmBusinessSaveReqVO updateReqVO) { public void updateBusiness(CrmBusinessSaveReqVO updateReqVO) {
// 1. 校验存在 // 1.1 校验存在
CrmBusinessDO oldBusiness = validateBusinessExists(updateReqVO.getId()); CrmBusinessDO oldBusiness = validateBusinessExists(updateReqVO.getId());
// 1.2 校验产品项的有效性
List<CrmBusinessProductDO> businessProducts = validateBusinessProducts(updateReqVO.getProducts());
// 1.3 TODO 芋艿校验关联字
// 2.1 更新商机 // 2.1 更新商机
CrmBusinessDO updateObj = BeanUtils.toBean(updateReqVO, CrmBusinessDO.class); CrmBusinessDO updateObj = BeanUtils.toBean(updateReqVO, CrmBusinessDO.class);
calculateTotalPrice(updateObj, businessProducts);
businessMapper.updateById(updateObj); businessMapper.updateById(updateObj);
// 2.2 更新商机关联商品 // 2.2 更新商机关联商品
List<CrmBusinessProductDO> productList = buildBusinessProductList(updateReqVO.getProductItems(), updateObj.getId()); updateBusinessProduct(updateObj.getId(), businessProducts);
updateBusinessProduct(productList, updateObj.getId());
// TODO @商机待定如果状态发生变化插入商机状态变更记录表 // TODO @商机待定如果状态发生变化插入商机状态变更记录表
// 3. 记录操作日志上下文 // 3. 记录操作日志上下文
@ -130,6 +132,37 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
LogRecordContext.putVariable("businessName", oldBusiness.getName()); LogRecordContext.putVariable("businessName", oldBusiness.getName());
} }
private void updateBusinessProduct(Long id, List<CrmBusinessProductDO> newList) {
List<CrmBusinessProductDO> oldList = businessProductMapper.selectListByBusinessId(id);
List<List<CrmBusinessProductDO>> diffList = diffList(oldList, newList, // id 不同就认为是不同的记录
(oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));
if (CollUtil.isNotEmpty(diffList.get(0))) {
diffList.get(0).forEach(o -> o.setBusinessId(id));
businessProductMapper.insertBatch(diffList.get(0));
}
if (CollUtil.isNotEmpty(diffList.get(1))) {
businessProductMapper.updateBatch(diffList.get(1));
}
if (CollUtil.isNotEmpty(diffList.get(2))) {
businessProductMapper.deleteBatchIds(convertSet(diffList.get(2), CrmBusinessProductDO::getId));
}
}
private List<CrmBusinessProductDO> validateBusinessProducts(List<CrmBusinessSaveReqVO.Product> list) {
// 1. 校验产品存在
productService.validProductList(convertSet(list, CrmBusinessSaveReqVO.Product::getProductId));
// 2. 转化为 CrmBusinessProductDO 列表
return convertList(list, o -> BeanUtils.toBean(o, CrmBusinessProductDO.class, item -> {
item.setTotalPrice(MoneyUtils.priceMultiply(item.getBusinessPrice(), item.getCount()));
}));
}
private void calculateTotalPrice(CrmBusinessDO business, List<CrmBusinessProductDO> businessProducts) {
business.setTotalProductPrice(getSumValue(businessProducts, CrmBusinessProductDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO));
BigDecimal discountPrice = MoneyUtils.priceMultiplyPercent(business.getTotalProductPrice(), business.getDiscountPercent());
business.setTotalPrice(business.getTotalProductPrice().subtract(discountPrice));
}
@Override @Override
public void updateBusinessFollowUpBatch(List<CrmUpdateFollowUpReqBO> updateFollowUpReqBOList) { public void updateBusinessFollowUpBatch(List<CrmUpdateFollowUpReqBO> updateFollowUpReqBOList) {
businessMapper.updateBatch(CrmBusinessConvert.INSTANCE.convertList(updateFollowUpReqBOList)); businessMapper.updateBatch(CrmBusinessConvert.INSTANCE.convertList(updateFollowUpReqBOList));
@ -155,44 +188,6 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
LogRecordContext.putVariable("businessName", business.getName()); LogRecordContext.putVariable("businessName", business.getName());
} }
private void updateBusinessProduct(List<CrmBusinessProductDO> newProductList, Long businessId) {
List<CrmBusinessProductDO> oldProducts = businessProductMapper.selectListByBusinessId(businessId);
List<List<CrmBusinessProductDO>> diffList = CollectionUtils.diffList(oldProducts, newProductList, (oldValue, newValue) -> {
boolean condition = ObjectUtil.equal(oldValue.getProductId(), newValue.getProductId());
if (condition) {
newValue.setId(oldValue.getId()); // 更新需要原始编号
}
return condition;
});
if (CollUtil.isNotEmpty(diffList.get(0))) {
businessProductMapper.insertBatch(diffList.get(0));
}
if (CollUtil.isNotEmpty(diffList.get(1))) {
businessProductMapper.updateBatch(diffList.get(1));
}
if (CollUtil.isNotEmpty(diffList.get(2))) {
businessProductMapper.deleteBatchIds(convertSet(diffList.get(2), CrmBusinessProductDO::getId));
}
}
private List<CrmBusinessProductDO> buildBusinessProductList(List<CrmBusinessSaveReqVO.CrmBusinessProductItem> productItems,
Long businessId) {
// 校验商品存在
Set<Long> productIds = convertSet(productItems, CrmBusinessSaveReqVO.CrmBusinessProductItem::getId);
List<CrmProductDO> productList = productService.getProductList(productIds);
if (CollUtil.isEmpty(productIds) || productList.size() != productIds.size()) {
throw exception(PRODUCT_NOT_EXISTS);
}
Map<Long, CrmProductDO> productMap = convertMap(productList, CrmProductDO::getId);
return convertList(productItems, productItem -> {
CrmProductDO product = productMap.get(productItem.getId());
return BeanUtils.toBean(product, CrmBusinessProductDO.class)
.setId(null).setProductId(productItem.getId()).setBusinessId(businessId)
.setCount(productItem.getCount()).setDiscountPercent(productItem.getDiscountPercent())
.setTotalPrice(MoneyUtils.calculator(product.getPrice(), productItem.getCount(), productItem.getDiscountPercent()));
});
}
/** /**
* 删除校验合同是关联合同 * 删除校验合同是关联合同
* *
@ -235,10 +230,10 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
@Override @Override
public void updateBusinessProduct(CrmBusinessUpdateProductReqBO updateProductReqBO) { public void updateBusinessProduct(CrmBusinessUpdateProductReqBO updateProductReqBO) {
// 更新商机关联商品 // 更新商机关联商品 TODO yunai
List<CrmBusinessProductDO> productList = buildBusinessProductList( // List<CrmBusinessProductDO> productList = buildBusinessProductList(
BeanUtils.toBean(updateProductReqBO.getProductItems(), CrmBusinessSaveReqVO.CrmBusinessProductItem.class), updateProductReqBO.getId()); // BeanUtils.toBean(updateProductReqBO.getProductItems(), CrmBusinessSaveReqVO.Product.class), updateProductReqBO.getId());
updateBusinessProduct(productList, updateProductReqBO.getId()); // updateBusinessProduct(productList, updateProductReqBO.getId());
} }
//======================= 查询相关 ======================= //======================= 查询相关 =======================
@ -265,6 +260,11 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
return businessMapper.selectBatchIds(ids); return businessMapper.selectBatchIds(ids);
} }
@Override
public List<CrmBusinessProductDO> getBusinessProductListByBusinessId(Long businessId) {
return businessProductMapper.selectListByBusinessId(businessId);
}
@Override @Override
public PageResult<CrmBusinessDO> getBusinessPage(CrmBusinessPageReqVO pageReqVO, Long userId) { public PageResult<CrmBusinessDO> getBusinessPage(CrmBusinessPageReqVO pageReqVO, Long userId) {
return businessMapper.selectPage(pageReqVO, userId); return businessMapper.selectPage(pageReqVO, userId);

View File

@ -5,11 +5,13 @@ import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusine
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusQueryVO; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusQueryVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusSaveReqVO; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
/** /**
* 商机状态 Service 接口 * 商机状态 Service 接口
@ -74,4 +76,14 @@ public interface CrmBusinessStatusService {
*/ */
List<CrmBusinessStatusDO> getBusinessStatusList(Collection<Long> ids); List<CrmBusinessStatusDO> getBusinessStatusList(Collection<Long> ids);
/**
* 获得商机状态 Map
*
* @param ids 编号数组
* @return 商机状态 Map
*/
default Map<Long, CrmBusinessStatusDO> getBusinessStatusMap(Collection<Long> ids) {
return convertMap(getBusinessStatusList(ids), CrmBusinessStatusDO::getId);
}
} }

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.crm.service.business; package cn.iocoder.yudao.module.crm.service.business;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusPageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusPageReqVO;
@ -13,6 +14,7 @@ import org.springframework.validation.annotation.Validated;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@ -80,6 +82,9 @@ public class CrmBusinessStatusServiceImpl implements CrmBusinessStatusService {
@Override @Override
public List<CrmBusinessStatusDO> getBusinessStatusList(Collection<Long> ids) { public List<CrmBusinessStatusDO> getBusinessStatusList(Collection<Long> ids) {
if (CollUtil.isEmpty(ids)) {
return Collections.emptyList();
}
return businessStatusMapper.selectBatchIds(ids); return businessStatusMapper.selectBatchIds(ids);
} }

View File

@ -9,6 +9,9 @@ import jakarta.validation.Valid;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
/** /**
* 商机状态类型 Service 接口 * 商机状态类型 Service 接口
@ -72,4 +75,14 @@ public interface CrmBusinessStatusTypeService {
*/ */
List<CrmBusinessStatusTypeDO> getBusinessStatusTypeList(Collection<Long> ids); List<CrmBusinessStatusTypeDO> getBusinessStatusTypeList(Collection<Long> ids);
/**
* 获得商机状态类型 Map
*
* @param ids 编号数组
* @return 商机状态类型 Map
*/
default Map<Long, CrmBusinessStatusTypeDO> getBusinessStatusTypeMap(Collection<Long> ids) {
return convertMap(getBusinessStatusTypeList(ids), CrmBusinessStatusTypeDO::getId);
}
} }

View File

@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.crm.service.business;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypePageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypePageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeQueryVO; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeQueryVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeSaveReqVO; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeSaveReqVO;
@ -11,19 +10,18 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO;
import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessStatusMapper; import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessStatusMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessStatusTypeMapper; import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessStatusTypeMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import jakarta.annotation.Resource;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import jakarta.annotation.Resource;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_STATUS_TYPE_NOT_EXISTS;
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_STATUS_TYPE_NAME_EXISTS; import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_STATUS_TYPE_NAME_EXISTS;
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_STATUS_TYPE_NOT_EXISTS;
/** /**
* 商机状态类型 Service 实现类 * 商机状态类型 Service 实现类
@ -126,6 +124,9 @@ public class CrmBusinessStatusTypeServiceImpl implements CrmBusinessStatusTypeSe
@Override @Override
public List<CrmBusinessStatusTypeDO> getBusinessStatusTypeList(Collection<Long> ids) { public List<CrmBusinessStatusTypeDO> getBusinessStatusTypeList(Collection<Long> ids) {
if (CollUtil.isEmpty(ids)) {
return Collections.emptyList();
}
return businessStatusTypeMapper.selectBatchIds(ids); return businessStatusTypeMapper.selectBatchIds(ids);
} }

View File

@ -184,7 +184,8 @@ public class CrmContractServiceImpl implements CrmContractService {
return BeanUtils.toBean(product, CrmContractProductDO.class) return BeanUtils.toBean(product, CrmContractProductDO.class)
.setId(null).setProductId(productItem.getId()).setContractId(contractId) .setId(null).setProductId(productItem.getId()).setContractId(contractId)
.setCount(productItem.getCount()).setDiscountPercent(productItem.getDiscountPercent()) .setCount(productItem.getCount()).setDiscountPercent(productItem.getDiscountPercent())
.setTotalPrice(MoneyUtils.calculator(product.getPrice(), productItem.getCount(), productItem.getDiscountPercent())); // TODO 芋艿这里临时注释掉
.setTotalPrice(MoneyUtils.calculator(null, productItem.getCount(), productItem.getDiscountPercent()));
}); });
} }

View File

@ -3,10 +3,13 @@ package cn.iocoder.yudao.module.crm.service.product;
import cn.iocoder.yudao.module.crm.controller.admin.product.vo.category.CrmProductCategoryCreateReqVO; import cn.iocoder.yudao.module.crm.controller.admin.product.vo.category.CrmProductCategoryCreateReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.product.vo.category.CrmProductCategoryListReqVO; import cn.iocoder.yudao.module.crm.controller.admin.product.vo.category.CrmProductCategoryListReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO; import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
/** /**
* CRM 产品分类 Service 接口 * CRM 产品分类 Service 接口
@ -61,4 +64,14 @@ public interface CrmProductCategoryService {
*/ */
List<CrmProductCategoryDO> getProductCategoryList(Collection<Long> ids); List<CrmProductCategoryDO> getProductCategoryList(Collection<Long> ids);
/**
* 获得产品分类 Map
*
* @param ids 编号数组
* @return 产品分类 Map
*/
default Map<Long, CrmProductCategoryDO> getProductCategoryMap(Collection<Long> ids) {
return convertMap(getProductCategoryList(ids), CrmProductCategoryDO::getId);
}
} }

View File

@ -8,6 +8,9 @@ import jakarta.validation.Valid;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
/** /**
* CRM 产品 Service 接口 * CRM 产品 Service 接口
@ -54,28 +57,46 @@ public interface CrmProductService {
*/ */
List<CrmProductDO> getProductList(Collection<Long> ids); List<CrmProductDO> getProductList(Collection<Long> ids);
/**
* 获得产品 Map
*
* @param ids 编号
* @return 产品 Map
*/
default Map<Long, CrmProductDO> getProductMap(Collection<Long> ids) {
return convertMap(getProductList(ids), CrmProductDO::getId);
}
/** /**
* 获得产品分页 * 获得产品分页
* *
* @param pageReqVO 分页查询 * @param pageReqVO 分页查询
* @return 产品分页 * @return 产品分页
*/ */
PageResult<CrmProductDO> getProductPage(CrmProductPageReqVO pageReqVO, Long userId); PageResult<CrmProductDO> getProductPage(CrmProductPageReqVO pageReqVO);
/** /**
* 获得产品 * 获得产品数量
* *
* @param categoryId 分类编号 * @param categoryId 分类编号
* @return 产品 * @return 产品
*/ */
CrmProductDO getProductByCategoryId(Long categoryId); Long getProductByCategoryId(Long categoryId);
/** /**
* 获得产品列表 * 获得指定状态的产品列表
* *
* @param ids 产品编号 * @param status 状态
* @return 产品列表 * @return 产品列表
*/ */
List<CrmProductDO> getProductListByIds(Collection<Long> ids); List<CrmProductDO> getProductListByStatus(Integer status);
/**
* 校验产品们的有效性
*
* @param ids 编号数组
* @return 产品列表
*/
List<CrmProductDO> validProductList(Collection<Long> ids);
} }

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.crm.service.product; package cn.iocoder.yudao.module.crm.service.product;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductPageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductPageReqVO;
@ -15,7 +15,6 @@ import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPerm
import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.mzt.logapi.context.LogRecordContext; import com.mzt.logapi.context.LogRecordContext;
import com.mzt.logapi.service.impl.DiffParseFunction; import com.mzt.logapi.service.impl.DiffParseFunction;
import com.mzt.logapi.starter.annotation.LogRecord; import com.mzt.logapi.starter.annotation.LogRecord;
@ -27,8 +26,10 @@ import org.springframework.validation.annotation.Validated;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
@ -138,25 +139,41 @@ public class CrmProductServiceImpl implements CrmProductService {
} }
@Override @Override
public List<CrmProductDO> getProductList(Collection<Long> ids) { public PageResult<CrmProductDO> getProductPage(CrmProductPageReqVO pageReqVO) {
return productMapper.selectPage(pageReqVO);
}
@Override
public Long getProductByCategoryId(Long categoryId) {
return productMapper.selectCountByCategoryId(categoryId);
}
@Override
public List<CrmProductDO> getProductListByStatus(Integer status) {
return productMapper.selectListByStatus(status);
}
@Override
public List<CrmProductDO> validProductList(Collection<Long> ids) {
if (CollUtil.isEmpty(ids)) { if (CollUtil.isEmpty(ids)) {
return ListUtil.empty(); return Collections.emptyList();
} }
return productMapper.selectBatchIds(ids); List<CrmProductDO> list = productMapper.selectBatchIds(ids);
Map<Long, CrmProductDO> productMap = convertMap(list, CrmProductDO::getId);
for (Long id : ids) {
CrmProductDO product = productMap.get(id);
if (productMap.get(id) == null) {
throw exception(PRODUCT_NOT_EXISTS);
}
if (CommonStatusEnum.isDisable(product.getStatus())) {
throw exception(PRODUCT_NOT_ENABLE, product.getName());
}
}
return list;
} }
@Override @Override
public PageResult<CrmProductDO> getProductPage(CrmProductPageReqVO pageReqVO, Long userId) { public List<CrmProductDO> getProductList(Collection<Long> ids) {
return productMapper.selectPage(pageReqVO, userId);
}
@Override
public CrmProductDO getProductByCategoryId(Long categoryId) {
return productMapper.selectOne(new LambdaQueryWrapper<CrmProductDO>().eq(CrmProductDO::getCategoryId, categoryId));
}
@Override
public List<CrmProductDO> getProductListByIds(Collection<Long> ids) {
if (CollUtil.isEmpty(ids)) { if (CollUtil.isEmpty(ids)) {
return Collections.emptyList(); return Collections.emptyList();
} }