📖 CRM:code review 合同流程

This commit is contained in:
YunaiV 2024-02-01 09:46:32 +08:00
parent acb8d3f23b
commit f5f827b59f
10 changed files with 39 additions and 28 deletions

View File

@ -43,6 +43,7 @@ public class BpmModelController {
return success(model); return success(model);
} }
// TODO @puhui999这个接口的目的是啥呀
@GetMapping("/get-by-key") @GetMapping("/get-by-key")
@Operation(summary = "获得模型") @Operation(summary = "获得模型")
@Parameter(name = "key", description = "流程标识", required = true, example = "oa_leave") @Parameter(name = "key", description = "流程标识", required = true, example = "oa_leave")

View File

@ -132,6 +132,7 @@ public class CrmContractController {
return CrmContractConvert.INSTANCE.convertPage(pageResult, userMap, customerList); return CrmContractConvert.INSTANCE.convertPage(pageResult, userMap, customerList);
} }
// TODO @puhui999transferContract
@PutMapping("/transfer") @PutMapping("/transfer")
@Operation(summary = "合同转移") @Operation(summary = "合同转移")
@PreAuthorize("@ss.hasPermission('crm:contract:update')") @PreAuthorize("@ss.hasPermission('crm:contract:update')")
@ -140,6 +141,7 @@ public class CrmContractController {
return success(true); return success(true);
} }
// TODO @puhui999方法名不对哈要不改成 submit提交审核的意思
@PutMapping("/approve") @PutMapping("/approve")
@Operation(summary = "发起合同审批流程") @Operation(summary = "发起合同审批流程")
@PreAuthorize("@ss.hasPermission('crm:contract:update')") @PreAuthorize("@ss.hasPermission('crm:contract:update')")

View File

@ -89,6 +89,7 @@ public class CrmContractSaveReqVO {
@DiffLogField(name = "备注") @DiffLogField(name = "备注")
private String remark; private String remark;
// TODO @puhui999这个字段按道理不用传递
@Schema(description = "审批状态", example = "1") @Schema(description = "审批状态", example = "1")
private Integer auditStatus; private Integer auditStatus;

View File

@ -217,6 +217,8 @@ public class CrmCustomerController {
ExcelUtils.write(response, "客户导入模板.xls", "客户列表", CrmCustomerImportExcelVO.class, list); ExcelUtils.write(response, "客户导入模板.xls", "客户列表", CrmCustomerImportExcelVO.class, list);
} }
// TODO @puhui999updateSupport 要不改成前端必须传递哈哈哈代码排版看着有点乱
// TODO @puhui999加一个选择负责人允许空空就进入公海
@PostMapping("/import") @PostMapping("/import")
@Operation(summary = "导入客户") @Operation(summary = "导入客户")
@Parameters({ @Parameters({
@ -224,13 +226,12 @@ public class CrmCustomerController {
@Parameter(name = "updateSupport", description = "是否支持更新,默认为 false", example = "true") @Parameter(name = "updateSupport", description = "是否支持更新,默认为 false", example = "true")
}) })
@PreAuthorize("@ss.hasPermission('system:customer:import')") @PreAuthorize("@ss.hasPermission('system:customer:import')")
public CommonResult<CrmCustomerImportRespVO> importExcel(@RequestParam("file") MultipartFile file, public CommonResult<CrmCustomerImportRespVO> importExcel(@RequestParam("file") MultipartFile file, @RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport)
@RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) throws Exception { throws Exception {
List<CrmCustomerImportExcelVO> list = ExcelUtils.read(file, CrmCustomerImportExcelVO.class); List<CrmCustomerImportExcelVO> list = ExcelUtils.read(file, CrmCustomerImportExcelVO.class);
return success(customerService.importCustomerList(list, updateSupport, getLoginUserId())); return success(customerService.importCustomerList(list, updateSupport, getLoginUserId()));
} }
@PutMapping("/transfer") @PutMapping("/transfer")
@Operation(summary = "转移客户") @Operation(summary = "转移客户")
@PreAuthorize("@ss.hasPermission('crm:customer:update')") @PreAuthorize("@ss.hasPermission('crm:customer:update')")

View File

@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.ExcelProperty;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
@ -25,6 +24,7 @@ public class CrmCustomerImportExcelVO {
@ExcelProperty("客户名称") @ExcelProperty("客户名称")
private String name; private String name;
// TODO @puhui999industryIdlevelsource 字段可以研究下怎么搞下拉框
@ExcelProperty(value = "所属行业", converter = DictConvert.class) @ExcelProperty(value = "所属行业", converter = DictConvert.class)
@DictFormat(CRM_CUSTOMER_INDUSTRY) @DictFormat(CRM_CUSTOMER_INDUSTRY)
private Integer industryId; private Integer industryId;
@ -46,25 +46,22 @@ public class CrmCustomerImportExcelVO {
@ExcelProperty("网址") @ExcelProperty("网址")
private String website; private String website;
@Size(max = 20, message = "QQ长度不能超过 20 个字符")
@ExcelProperty("QQ") @ExcelProperty("QQ")
private String qq; private String qq;
@Size(max = 255, message = "微信长度不能超过 255 个字符")
@ExcelProperty("微信") @ExcelProperty("微信")
private String wechat; private String wechat;
@Size(max = 255, message = "邮箱长度不能超过 255 个字符")
@ExcelProperty("邮箱") @ExcelProperty("邮箱")
private String email; private String email;
@Size(max = 4096, message = "客户描述长度不能超过 4096 个字符")
@ExcelProperty("客户描述") @ExcelProperty("客户描述")
private String description; private String description;
@ExcelProperty("备注") @ExcelProperty("备注")
private String remark; private String remark;
// TODO @puhui999需要选择省市区需要研究下怎么搞合理点
@ExcelProperty("地区编号") @ExcelProperty("地区编号")
private Integer areaId; private Integer areaId;

View File

@ -118,6 +118,7 @@ public interface CrmContractService {
*/ */
Long getContractCountByCustomerId(Long customerId); Long getContractCountByCustomerId(Long customerId);
// TODO @puhui999要不改成 getContractCountByBusinessId
/** /**
* 根据商机ID获取关联客户的合同数量 * 根据商机ID获取关联客户的合同数量
* *

View File

@ -22,7 +22,6 @@ import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessProductService; import cn.iocoder.yudao.module.crm.service.business.CrmBusinessProductService;
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.contact.CrmContactService;
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.followup.bo.CrmUpdateFollowUpReqBO; import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
@ -69,15 +68,14 @@ public class CrmContractServiceImpl implements CrmContractService {
@Resource @Resource
private CrmProductService productService; private CrmProductService productService;
@Resource @Resource
private BpmProcessInstanceApi bpmProcessInstanceApi;
@Resource
private CrmCustomerService customerService; private CrmCustomerService customerService;
@Resource @Resource
private CrmContactService contactService;
@Resource
private CrmBusinessService businessService; private CrmBusinessService businessService;
@Resource @Resource
private AdminUserApi adminUserApi; private AdminUserApi adminUserApi;
@Resource
private BpmProcessInstanceApi bpmProcessInstanceApi;
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@ -85,19 +83,19 @@ public class CrmContractServiceImpl implements CrmContractService {
success = CRM_CONTRACT_CREATE_SUCCESS) success = CRM_CONTRACT_CREATE_SUCCESS)
public Long createContract(CrmContractSaveReqVO createReqVO, Long userId) { public Long createContract(CrmContractSaveReqVO createReqVO, Long userId) {
validateRelationDataExists(createReqVO); validateRelationDataExists(createReqVO);
// 插入合同 // 1.1 插入合同
CrmContractDO contract = BeanUtils.toBean(createReqVO, CrmContractDO.class).setId(null); CrmContractDO contract = BeanUtils.toBean(createReqVO, CrmContractDO.class).setId(null);
contractMapper.insert(contract); contractMapper.insert(contract);
// 1.2 插入商机关联商品
List<CrmBusinessProductDO> businessProduct = convertBusinessProductList(createReqVO);
businessProductService.insertBatch(businessProduct);
// 创建数据权限 // 2. 创建数据权限
crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(userId) crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(userId)
.setBizType(CrmBizTypeEnum.CRM_CONTRACT.getType()).setBizId(contract.getId()) .setBizType(CrmBizTypeEnum.CRM_CONTRACT.getType()).setBizId(contract.getId())
.setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); .setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
// 插入商机关联商品 // 3. 记录操作日志上下文
List<CrmBusinessProductDO> businessProduct = convertBusinessProductList(createReqVO);
businessProductService.insertBatch(businessProduct);
// 4. 记录操作日志上下文
LogRecordContext.putVariable("contract", contract); LogRecordContext.putVariable("contract", contract);
return contract.getId(); return contract.getId();
} }
@ -121,6 +119,7 @@ public class CrmContractServiceImpl implements CrmContractService {
contractMapper.updateById(updateObj); contractMapper.updateById(updateObj);
// TODO puhui999: @芋艿合同变更关联的商机后商品怎么处理 // TODO puhui999: @芋艿合同变更关联的商机后商品怎么处理
// TODO @puhui999和商品 spusku 编辑一样新增的插入修改的更新删除的删除
//List<CrmBusinessProductDO> businessProduct = convertBusinessProductList(updateReqVO); //List<CrmBusinessProductDO> businessProduct = convertBusinessProductList(updateReqVO);
//businessProductService.selectListByBusinessId() //businessProductService.selectListByBusinessId()
//diffList() //diffList()
@ -141,6 +140,7 @@ public class CrmContractServiceImpl implements CrmContractService {
} }
Map<Long, CrmProductDO> productMap = convertMap(productList, CrmProductDO::getId); Map<Long, CrmProductDO> productMap = convertMap(productList, CrmProductDO::getId);
return convertList(reqVO.getProductItems(), productItem -> { return convertList(reqVO.getProductItems(), productItem -> {
// TODO @puhui999这里可以改成直接 return不用弄一个 businessProduct 变量哈
CrmBusinessProductDO businessProduct = BeanUtils.toBean(productMap.get(productItem.getId()), CrmBusinessProductDO.class); CrmBusinessProductDO businessProduct = BeanUtils.toBean(productMap.get(productItem.getId()), CrmBusinessProductDO.class);
businessProduct.setId(null).setBusinessId(reqVO.getBusinessId()).setProductId(productItem.getId()) businessProduct.setId(null).setBusinessId(reqVO.getBusinessId()).setProductId(productItem.getId())
.setCount(productItem.getCount()).setDiscountPercent(productItem.getDiscountPercent()).setTotalPrice(calculator(businessProduct)); .setCount(productItem.getCount()).setDiscountPercent(productItem.getDiscountPercent()).setTotalPrice(calculator(businessProduct));
@ -154,6 +154,7 @@ public class CrmContractServiceImpl implements CrmContractService {
* @param businessProduct 关联商品 * @param businessProduct 关联商品
* @return 商品总价 * @return 商品总价
*/ */
// TODO @puhui999这个逻辑的计算是不是可以封装到 calculateRatePriceFloor
private Integer calculator(CrmBusinessProductDO businessProduct) { private Integer calculator(CrmBusinessProductDO businessProduct) {
int price = businessProduct.getPrice() * businessProduct.getCount(); int price = businessProduct.getPrice() * businessProduct.getCount();
if (businessProduct.getDiscountPercent() == null) { if (businessProduct.getDiscountPercent() == null) {
@ -176,7 +177,7 @@ public class CrmContractServiceImpl implements CrmContractService {
if (reqVO.getOwnerUserId() != null && adminUserApi.getUser(reqVO.getOwnerUserId()) == null) { if (reqVO.getOwnerUserId() != null && adminUserApi.getUser(reqVO.getOwnerUserId()) == null) {
throw exception(USER_NOT_EXISTS); throw exception(USER_NOT_EXISTS);
} }
// 4. 如果有关联商机则需要校验存在 // 3. 如果有关联商机则需要校验存在
if (reqVO.getBusinessId() != null && businessService.getBusiness(reqVO.getBusinessId()) == null) { if (reqVO.getBusinessId() != null && businessService.getBusiness(reqVO.getBusinessId()) == null) {
throw exception(BUSINESS_NOT_EXISTS); throw exception(BUSINESS_NOT_EXISTS);
} }
@ -235,6 +236,8 @@ public class CrmContractServiceImpl implements CrmContractService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void handleApprove(Long id, Long userId) { public void handleApprove(Long id, Long userId) {
// TODO @puhui999需要做状态检查
// 创建合同审批流程实例 // 创建合同审批流程实例
String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO() String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO()
.setProcessDefinitionKey(CONTRACT_APPROVE).setBusinessKey(String.valueOf(id))); .setProcessDefinitionKey(CONTRACT_APPROVE).setBusinessKey(String.valueOf(id)));

View File

@ -232,8 +232,10 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
return customer.getId(); return customer.getId();
} }
// TODO @puhui999操作日志
@Override @Override
public CrmCustomerImportRespVO importCustomerList(List<CrmCustomerImportExcelVO> importCustomers, Boolean isUpdateSupport, Long userId) { public CrmCustomerImportRespVO importCustomerList(List<CrmCustomerImportExcelVO> importCustomers,
Boolean isUpdateSupport, Long userId) {
if (CollUtil.isEmpty(importCustomers)) { if (CollUtil.isEmpty(importCustomers)) {
throw exception(CUSTOMER_IMPORT_LIST_IS_EMPTY); throw exception(CUSTOMER_IMPORT_LIST_IS_EMPTY);
} }
@ -241,6 +243,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
.updateCustomerNames(new ArrayList<>()).failureCustomerNames(new LinkedHashMap<>()).build(); .updateCustomerNames(new ArrayList<>()).failureCustomerNames(new LinkedHashMap<>()).build();
importCustomers.forEach(importCustomer -> { importCustomers.forEach(importCustomer -> {
// 校验判断是否有不符合的原因 // 校验判断是否有不符合的原因
// TODO @puhui999可以用 ValidationUtils 做参数校验可能要封装一个方法返回 message这样的话就可以在 CrmCustomerImportExcelVO 写需要校验的参数啦
try { try {
validateCustomerForCreate(importCustomer); validateCustomerForCreate(importCustomer);
} catch (ServiceException ex) { } catch (ServiceException ex) {
@ -250,6 +253,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
// 判断如果不存在在进行插入 // 判断如果不存在在进行插入
CrmCustomerDO existCustomer = customerMapper.selectByCustomerName(importCustomer.getName()); CrmCustomerDO existCustomer = customerMapper.selectByCustomerName(importCustomer.getName());
if (existCustomer == null) { if (existCustomer == null) {
// TODO @puhui999可以搞个 initCustomer 方法这样可以把 create 和导入复用下这个方法
CrmCustomerDO customer = BeanUtils.toBean(importCustomer, CrmCustomerDO.class).setOwnerUserId(userId) CrmCustomerDO customer = BeanUtils.toBean(importCustomer, CrmCustomerDO.class).setOwnerUserId(userId)
.setLockStatus(false).setDealStatus(false).setContactLastTime(LocalDateTime.now()); .setLockStatus(false).setDealStatus(false).setContactLastTime(LocalDateTime.now());
customerMapper.insert(customer); customerMapper.insert(customer);
@ -366,6 +370,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
// 1.1 获取没有锁定的不在公海的客户且没有成交的 // 1.1 获取没有锁定的不在公海的客户且没有成交的
List<CrmCustomerDO> notDealCustomerList = customerMapper.selectListByLockAndDealStatusAndNotPool(Boolean.FALSE, Boolean.FALSE); List<CrmCustomerDO> notDealCustomerList = customerMapper.selectListByLockAndDealStatusAndNotPool(Boolean.FALSE, Boolean.FALSE);
// 1.2 获取没有锁定的不在公海的客户且成交的 // 1.2 获取没有锁定的不在公海的客户且成交的
// TODO @puhui999下面也搞到 sql 里去哈 or 查询问题不大的
List<CrmCustomerDO> dealCustomerList = customerMapper.selectListByLockAndDealStatusAndNotPool(Boolean.FALSE, Boolean.TRUE); List<CrmCustomerDO> dealCustomerList = customerMapper.selectListByLockAndDealStatusAndNotPool(Boolean.FALSE, Boolean.TRUE);
List<CrmCustomerDO> poolCustomerList = new ArrayList<>(); List<CrmCustomerDO> poolCustomerList = new ArrayList<>();
poolCustomerList.addAll(filterList(notDealCustomerList, customer -> poolCustomerList.addAll(filterList(notDealCustomerList, customer ->
@ -382,7 +387,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
getSelf().putCustomerPool(customer); getSelf().putCustomerPool(customer);
count++; count++;
} catch (Throwable e) { } catch (Throwable e) {
log.error("[customerAutoPutPoolBySystem][Customer 客户({}) 放入公海异常]", customer.getId(), e); log.error("[autoPutCustomerPool][Customer 客户({}) 放入公海异常]", customer.getId(), e);
} }
} }
return count; return count;

View File

@ -109,14 +109,14 @@ public interface CrmPermissionService {
List<CrmPermissionDO> getPermissionListByBizTypeAndUserId(Integer bizType, Long userId); List<CrmPermissionDO> getPermissionListByBizTypeAndUserId(Integer bizType, Long userId);
/** /**
* 校验权限 * 校验是否有指定数据的操作权限
* *
* @param bizType 数据类型关联 {@link CrmBizTypeEnum} * @param bizType 数据类型关联 {@link CrmBizTypeEnum}
* @param bizId 数据编号关联 {@link CrmBizTypeEnum} 对应模块 DO#getId() * @param bizId 数据编号关联 {@link CrmBizTypeEnum} 对应模块 DO#getId()
* @param userId 用户编号 * @param userId 用户编号
* @param levelEnum 权限级别 * @param level 权限级别
* @return boolean * @return 是否有权限
*/ */
boolean hasPermission(Integer bizType, Long bizId, Long userId, CrmPermissionLevelEnum levelEnum); boolean hasPermission(Integer bizType, Long bizId, Long userId, CrmPermissionLevelEnum level);
} }

View File

@ -213,10 +213,10 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
} }
@Override @Override
public boolean hasPermission(Integer bizType, Long bizId, Long userId, CrmPermissionLevelEnum levelEnum) { public boolean hasPermission(Integer bizType, Long bizId, Long userId, CrmPermissionLevelEnum level) {
List<CrmPermissionDO> permissionList = permissionMapper.selectByBizTypeAndBizId(bizType, bizId); List<CrmPermissionDO> permissionList = permissionMapper.selectByBizTypeAndBizId(bizType, bizId);
return anyMatch(permissionList, permission -> return anyMatch(permissionList, permission ->
ObjUtil.equal(permission.getUserId(), userId) && ObjUtil.equal(permission.getLevel(), levelEnum.getLevel())); ObjUtil.equal(permission.getUserId(), userId) && ObjUtil.equal(permission.getLevel(), level.getLevel()));
} }
} }