CRM:商机 code review

This commit is contained in:
YunaiV 2024-01-18 12:06:14 +08:00
parent 4c200eda92
commit ca55282516
20 changed files with 60 additions and 98 deletions

View File

@ -20,36 +20,4 @@ CREATE TABLE `crm_business_product` (
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
`tenant_id` bigint(0) NOT NULL DEFAULT 1 COMMENT '租户编号',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 29 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '商机产品关联表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for crm_business_status
-- ----------------------------
DROP TABLE IF EXISTS `crm_business_status`;
CREATE TABLE `crm_business_status` (
`id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`type_id` bigint(0) NOT NULL COMMENT '状态类型编号',
`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '状态名',
`percent` bigint(0) NULL DEFAULT NULL COMMENT '赢单率',
`sort` int(0) NULL DEFAULT NULL COMMENT '排序',
`tenant_id` bigint(0) NOT NULL DEFAULT 0 COMMENT '租户编号',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '商机状态' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for crm_business_status_type
-- ----------------------------
DROP TABLE IF EXISTS `crm_business_status_type`;
CREATE TABLE `crm_business_status_type` (
`id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '状态类型名',
`dept_ids` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '使用的部门编号',
`status` int(0) NOT NULL DEFAULT 1 COMMENT '开启状态',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
`create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
`update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
`tenant_id` bigint(0) NOT NULL DEFAULT 0 COMMENT '租户编号',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '商机状态类型' ROW_FORMAT = Dynamic;
) ENGINE = InnoDB AUTO_INCREMENT = 29 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '商机产品关联表' ROW_FORMAT = Dynamic;

View File

@ -1,6 +1,5 @@
package cn.iocoder.yudao.module.crm.enums.business;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import lombok.Getter;
@ -8,6 +7,7 @@ import lombok.RequiredArgsConstructor;
import java.util.Arrays;
// TODO @lzxhqs1titledescriptioncreate 可以删除非标准的 javadoc 注释哈然后可以在类上加下这个类的注释2CrmBizEndStatus 改成 CrmBusinessEndStatus非必要不缩写哈可阅读比较重要
/**
* @author lzxhqs
* @version 1.0
@ -18,12 +18,14 @@ import java.util.Arrays;
@RequiredArgsConstructor
@Getter
public enum CrmBizEndStatus implements IntArrayValuable {
WIN(1, "赢单"),
LOSE(2, "输单"),
INVALID(3, "无效");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmBizEndStatus::getStatus).toArray();
// TODO @lzxhqs这里的方法建议放到 49 行之后一般类里是静态变量普通变量静态方法普通方法
public static boolean isWin(Integer status) {
return ObjectUtil.equal(WIN.getStatus(), status);
}
@ -49,4 +51,5 @@ public enum CrmBizEndStatus implements IntArrayValuable {
public int[] array() {
return ARRAYS;
}
}

View File

@ -22,11 +22,6 @@
<artifactId>yudao-module-system-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-module-system-biz</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-module-crm-api</artifactId>

View File

@ -25,19 +25,17 @@ import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
@ -86,13 +84,13 @@ public class CrmBusinessStatusTypeController {
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('crm:business-status-type:query')")
public CommonResult<CrmBusinessStatusTypeRespVO> getBusinessStatusType(@RequestParam("id") Long id) {
CrmBusinessStatusTypeDO businessStatusType = businessStatusTypeService.getBusinessStatusType(id);
CrmBusinessStatusTypeDO statusType = businessStatusTypeService.getBusinessStatusType(id);
// 处理状态回显
// TODO @ljlleo可以使用 CollectionUtils.convertSet 替代常用的 stream 操作更简洁一点下面几个也是
// TODO @lzxhqs可以在 businessStatusService 加个 getBusinessStatusListByTypeId 方法直接返回 List<CrmBusinessStatusDO> 常用的尽量封装个简单易懂的方法不用追求绝对通用
CrmBusinessStatusQueryVO queryVO = new CrmBusinessStatusQueryVO();
queryVO.setTypeId(id);
List<CrmBusinessStatusDO> statusList = businessStatusService.selectList(queryVO);
return success(CrmBusinessStatusTypeConvert.INSTANCE.convert(businessStatusType, statusList));
return success(CrmBusinessStatusTypeConvert.INSTANCE.convert(statusType, statusList));
}
@GetMapping("/page")
@ -101,12 +99,6 @@ public class CrmBusinessStatusTypeController {
public CommonResult<PageResult<CrmBusinessStatusTypeRespVO>> getBusinessStatusTypePage(@Valid CrmBusinessStatusTypePageReqVO pageReqVO) {
PageResult<CrmBusinessStatusTypeDO> pageResult = businessStatusTypeService.getBusinessStatusTypePage(pageReqVO);
// 处理部门回显
// TODO @ljlleo可以使用 CollectionUtils.convertSet 替代常用的 stream 操作更简洁一点下面几个也是哈
// Set<Long> deptIds = pageResult.getList().stream()
// .map(CrmBusinessStatusTypeDO::getDeptIds)
// .filter(Objects::nonNull)
// .flatMap(Collection::stream)
// .collect(Collectors.toSet());
Set<Long> deptIds = CollectionUtils.convertSetByFlatMap(pageResult.getList(), CrmBusinessStatusTypeDO::getDeptIds,Collection::stream);
List<DeptRespDTO> deptList = deptApi.getDeptList(deptIds);
return success(CrmBusinessStatusTypeConvert.INSTANCE.convertPage(pageResult, deptList));

View File

@ -17,7 +17,6 @@ import java.util.List;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
// TODO @ljileoDiffLogField function 完善一下
@Schema(description = "管理后台 - CRM 商机创建/更新 Request VO")
@Data
public class CrmBusinessSaveReqVO {
@ -59,7 +58,7 @@ public class CrmBusinessSaveReqVO {
@DiffLogField(name = "商机金额")
private Integer price;
// TODO @ljileo折扣使用 Integer 类型存储时默认 * 100展示的时候前端需要 / 100避免精度丢失问题
// TODO @lzxhqs折扣使用 Integer 类型存储时默认 * 100展示的时候前端需要 / 100避免精度丢失问题
@Schema(description = "整单折扣")
@DiffLogField(name = "整单折扣")
private Integer discountPercent;
@ -71,17 +70,16 @@ public class CrmBusinessSaveReqVO {
@Schema(description = "备注", example = "随便")
@DiffLogField(name = "备注")
private String remark;
// TODO @ljileo修改的时候应该可以传递添加的产品
@Schema(description = "联系人编号", example = "110")
@NotNull(message = "联系人编号不能为空")
private Long contactId;
@Schema(description = "1赢单2输单3无效", example = "1")
@Schema(description = "结束状态", example = "1")
@InEnum(CrmBizEndStatus.class)
private Integer endStatus;
@Schema(description = "商机产品列表", example = "")
// TODO @lzxhqs不设置默认 new ArrayList<>()一般 pojo 不设置默认值哈
@Schema(description = "商机产品列表")
private List<CrmBusinessProductSaveReqVO> products = new ArrayList<>();
@Schema(description = "联系人编号", example = "110")
private Long contactId; // 使用场景联系人详情添加商机时如果需要关联两者需要传递 contactId 字段
}

View File

@ -6,10 +6,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* @author lzxhqs
*/
// TODO @lzxhqs这个类如果没用到可以考虑删除哈
@Schema(description = "管理后台 - 商机产品分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)

View File

@ -4,9 +4,6 @@ import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* @author lzxhqs
*/
@Schema(description = "管理后台 - 商机产品关联 Response VO")
@Data
@ExcelIgnoreUnannotated

View File

@ -6,9 +6,6 @@ import lombok.Data;
import java.math.BigDecimal;
/**
* @author lzxhqs
*/
@Schema(description = "管理后台 - CRM 商机产品关联表 创建/更新 Request VO")
@Data
public class CrmBusinessProductSaveReqVO {
@ -16,12 +13,14 @@ public class CrmBusinessProductSaveReqVO {
@Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "32129")
private Long id;
@Schema(description = "商机ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320")
@NotNull(message = "商机ID不能为空")
// TODO @lzxhqs这个字段应该是 Long 类型
@Schema(description = "商机编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320")
@NotNull(message = "商机编号不能为空")
private Integer businessId;
@Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320")
@NotNull(message = "产品ID不能为空")
// TODO @lzxhqs这个字段应该是 Long 类型
@Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320")
@NotNull(message = "产品编号不能为空")
private Integer productId;
@Schema(description = "产品单价", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320")
@ -44,7 +43,9 @@ public class CrmBusinessProductSaveReqVO {
@NotNull(message = "小计(折扣后价格)不能为空")
private BigDecimal subtotal;
// TODO @lzxhqs字符串 @NotEmpty因为要考虑 "" 前端搞了这个玩意
@Schema(description = "单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320")
@NotNull(message = "单位不能为空")
private String unit;
}

View File

@ -21,11 +21,11 @@ public class CrmBusinessStatusSaveReqVO {
@NotEmpty(message = "状态名不能为空")
private String name;
// TODO @lilleopercent 应该是 Integer
// TODO @lzxhqspercent 应该是 Integer
@Schema(description = "赢单率")
private String percent;
// TODO @lilleo这个是不是不用前端新增和修改的时候传递交给顺序计算出来存储起来就好了
// TODO @lzxhqs这个是不是不用前端新增和修改的时候传递交给顺序计算出来存储起来就好了
@Schema(description = "排序")
private Integer sort;

View File

@ -19,10 +19,10 @@ public class CrmBusinessStatusTypeSaveReqVO {
@NotEmpty(message = "状态类型名不能为空")
private String name;
// TODO @lzxhqs VO 里面我们不使用默认值哈这里 Lists.newArrayList() 看看怎么去掉上面 deptIds 也是类似噢
@Schema(description = "使用的部门编号", requiredMode = Schema.RequiredMode.REQUIRED)
private List<Long> deptIds = Lists.newArrayList();
// TODO @ljlleo VO 里面我们不使用默认值哈这里 Lists.newArrayList() 看看怎么去掉上面 deptIds 也是类似噢
@Schema(description = "商机状态集合", requiredMode = Schema.RequiredMode.REQUIRED)
private List<CrmBusinessStatusSaveReqVO> statusList;

View File

@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
// TODO @lzxhqs看看是不是用 BeanUtils 替代了
/**
* @author lzxhqs
* @version 1.0

View File

@ -14,6 +14,7 @@ import java.util.Map;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
// TODO @lzxhqs看看是不是用 BeanUtils 替代了
/**
* 商机状态类型 Convert
*
@ -24,7 +25,6 @@ public interface CrmBusinessStatusTypeConvert {
CrmBusinessStatusTypeConvert INSTANCE = Mappers.getMapper(CrmBusinessStatusTypeConvert.class);
CrmBusinessStatusTypeRespVO convert(CrmBusinessStatusTypeDO bean);
PageResult<CrmBusinessStatusTypeRespVO> convertPage(PageResult<CrmBusinessStatusTypeDO> page);
@ -38,9 +38,6 @@ public interface CrmBusinessStatusTypeConvert {
}
default CrmBusinessStatusTypeRespVO convert(CrmBusinessStatusTypeDO bean, List<CrmBusinessStatusDO> statusList) {
// TODO @ljlleo 可以链式赋值简化成一行
// CrmBusinessStatusTypeRespVO result = convert(bean);
// result.setStatusList(statusList);
return convert(bean).setStatusList(statusList);
}

View File

@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.crm.dal.dataobject.business;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.crm.enums.business.CrmBizEndStatus;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@ -80,9 +80,9 @@ public class CrmBusinessDO extends BaseDO {
*/
private String remark;
/**
* 1赢单2输单3无效
* 结束状态
*
* TODO @lijie搞个枚举
* 枚举 {@link CrmBizEndStatus}
*/
private Integer endStatus;
/**
@ -96,7 +96,7 @@ public class CrmBusinessDO extends BaseDO {
/**
* 跟进状态
*
* TODO @lijie目前就是 Boolean是否跟进
* TODO @lzxhqs目前就是 Boolean是否跟进
*/
private Integer followUpStatus;
@ -104,7 +104,6 @@ public class CrmBusinessDO extends BaseDO {
* 负责人的用户编号
*
* 关联 AdminUserDO id 字段
* {@link AdminUserDO#getId()}
*/
private Long ownerUserId;

View File

@ -31,17 +31,20 @@ public class CrmBusinessProductDO extends BaseDO {
private Long id;
/**
* 商机ID
* 商机编号
*
* 关联 {@link CrmBusinessDO#getId()}
*/
private Long businessId;
/**
* 产品ID
* 关联{@link CrmProductDO#getId()}
* 产品编号
*
* 关联 {@link CrmProductDO#getId()}
*/
private Long productId;
// TODO @lzxhqs改成 Integer单位目前整体倾向放大 100 倍哈
/**
* 产品单价
*/
@ -52,16 +55,19 @@ public class CrmBusinessProductDO extends BaseDO {
*/
private BigDecimal salesPrice;
// TODO @lzxhqs改成 count
/**
* 数量
*/
private BigDecimal num;
// TODO @lzxhqs改成 discountPercent
/**
* 折扣
*/
private BigDecimal discount;
// TODO @lzxhqs改成 totalPrice总计价格和现有项目风格一致
/**
* 小计折扣后价格
*/

View File

@ -35,9 +35,7 @@ public class CrmBusinessStatusDO {
*/
private String name;
/**
* 赢单率
*
* TODO 这里是不是改成 Integer 存储百分比 * 100
* 赢单率百分比
*/
private Integer percent;
/**

View File

@ -44,7 +44,7 @@ public class CrmBusinessStatusTypeDO extends BaseDO {
* 开启状态
*
* TODO 改成 Integer关联 CommonStatus
* {@link CommonStatusEnum}
* 枚举 {@link CommonStatusEnum}
*/
private Boolean status;

View File

@ -6,16 +6,16 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 商机产品 Mapper
* 商机产品 Mapper // TODO @lzxhqs类注释和作者之间要有一个空行
* @author lzxhqs
*/
@Mapper
public interface CrmBusinessProductMapper extends BaseMapperX<CrmBusinessProductDO> {
default void deleteByBusinessId(Long id) {
default void deleteByBusinessId(Long id) { // TODO @lzxhqs第一个方法和类之间最好空一行
delete(CrmBusinessProductDO::getBusinessId, id);
}
default CrmBusinessProductDO selectByBusinessId(Long id) {
default CrmBusinessProductDO selectByBusinessId(Long id) { // TODO @lzxhqsid 最好改成 businessId上面也是这样一看更容易懂
return selectOne(CrmBusinessProductDO::getBusinessId, id);
}
}

View File

@ -29,6 +29,7 @@ public interface CrmBusinessStatusTypeMapper extends BaseMapperX<CrmBusinessStat
.inIfPresent(CrmBusinessStatusTypeDO::getId, queryVO.getIdList()));
}
// TODO @lzxhqs这个可以改成 selectByName业务上基于在判断 id 匹配这样更通用一些mapper 尽量通用不关注或者特别关联业务
/**
* 根据ID和name查询
*
@ -39,4 +40,5 @@ public interface CrmBusinessStatusTypeMapper extends BaseMapperX<CrmBusinessStat
default CrmBusinessStatusTypeDO selectByIdAndName(Long id, String name) {
return selectOne(CrmBusinessStatusTypeDO::getId, id, CrmBusinessStatusTypeDO::getName, name);
}
}

View File

@ -63,7 +63,7 @@ public interface CrmContractMapper extends BaseMapperX<CrmContractDO> {
default Long selectCountByContactId(Long contactId) {
return selectCount(CrmContractDO::getContactId, contactId);
}
default CrmContractDO selectByBizId(Long businessId) {
default CrmContractDO selectByBizId(Long businessId) { // TODO @lzxhqs1方法和方法之间要有空行2selectCountByBusinessId一个是应该求数量一个是不要缩写 BizId 可读性
return selectOne(CrmContractDO::getBusinessId, businessId);
}

View File

@ -57,9 +57,11 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
@Resource
private CrmBusinessProductMapper businessProductMapper;
// TODO @lzxhqs不直接调用这个 mapper要调用对方的 service每个业务独立收敛
@Resource
private CrmContractMapper contractMapper;
// TODO @lzxhqs不直接调用这个 mapper要调用对方的 service每个业务独立收敛
@Resource
private CrmContactBusinessMapper contactBusinessMapper;
@Resource
@ -78,7 +80,9 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
.setOwnerUserId(userId);
businessMapper.insert(business);
// TODO 商机待定插入商机与产品的关联表校验商品存在
// TODO lzxhqs新增时是不是不用调用这个方法哈
verifyCrmBusinessProduct(business.getId());
// TODO @lzxhqs CollUtils.isNotEmpty
if (!createReqVO.getProducts().isEmpty()) {
createBusinessProducts(createReqVO.getProducts(), business.getId());
}
@ -95,6 +99,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
return business.getId();
}
// TODO @lzxhqsCrmContactBusinessService 调用这个这样逻辑才能收敛哈
/**
* @param businessId 商机id
* @param contactId 联系人id
@ -110,12 +115,14 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
}
// TODO @lzxhqs这个方法注释格式不对删除@description然后把 插入商机产品关联表 作为方法注释
/**
* @param products 产品集合
* @description 插入商机产品关联表
* @author lzxhqs
*/
private void createBusinessProducts(List<CrmBusinessProductSaveReqVO> products, Long businessId) {
// TODO @lzxhqs可以用 CollectionUtils.convertList
List<CrmBusinessProductDO> list = new ArrayList<>();
for (CrmBusinessProductSaveReqVO product : products) {
CrmBusinessProductDO businessProductDO = CrmBusinessProductConvert.INSTANCE.convert(product);
@ -152,6 +159,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
CrmBusinessDO updateObj = BeanUtils.toBean(updateReqVO, CrmBusinessDO.class);
businessMapper.updateById(updateObj);
// TODO 商机待定插入商机与产品的关联表校验商品存在
// TODO @lzxhqs更新时可以调用 CollectionUtils diffList尽量避免这种先删除再插入而是新增的插入变更的更新没的删除不然这个表每次更新会多好多数据
verifyCrmBusinessProduct(updateReqVO.getId());
if (!updateReqVO.getProducts().isEmpty()) {
createBusinessProducts(updateReqVO.getProducts(), updateReqVO.getId());