diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java index 137acee10..5126a4424 100644 --- a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java +++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java @@ -40,7 +40,7 @@ public interface ErrorCodeConstants { ErrorCode CUSTOMER_LOCKED_PUT_POOL_FAIL = new ErrorCode(1_020_006_005, "客户【{}】放入公海失败,原因:客户已锁定"); ErrorCode CUSTOMER_UPDATE_OWNER_USER_FAIL = new ErrorCode(1_020_006_006, "更新客户【{}】负责人失败, 原因:系统异常"); ErrorCode CUSTOMER_LOCK_FAIL_IS_LOCK = new ErrorCode(1_020_006_007, "锁定客户失败,它已经处于锁定状态"); - ErrorCode CUSTOMER_UNLOCK_FAIL_IS_UNLOCK = new ErrorCode(1_020_006_008, "锁定客户失败,它已经处于未锁定状态"); + ErrorCode CUSTOMER_UNLOCK_FAIL_IS_UNLOCK = new ErrorCode(1_020_006_008, "解锁客户失败,它已经处于未锁定状态"); ErrorCode CUSTOMER_LOCK_EXCEED_LIMIT = new ErrorCode(1_020_006_009, "锁定客户失败,超出锁定规则上限"); ErrorCode CUSTOMER_OWNER_EXCEED_LIMIT = new ErrorCode(1_020_006_010, "操作失败,超出客户数拥有上限"); diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java index b522993eb..48f0a0c32 100644 --- a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java +++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java @@ -2,32 +2,56 @@ package cn.iocoder.yudao.module.crm.enums; /** * CRM 操作日志枚举 + * 目的:统一管理,也减少 Service 里各种“复杂”字符串 * * @author HUIHUI */ public interface LogRecordConstants { - //======================= 客户模块类型 ======================= - // TODO puhui999: 确保模块命名方式为 module + 子模块名称的方式。统一定义模块名称是为了方便查询各自记录的操作日志,列如说:查询客户【张三的操作日志】就可以 module + bizId - String CRM_LEADS = "CRM 线索"; - String CRM_CUSTOMER = "CRM 客户"; - String CRM_CONTACT = "CRM 联系人"; - String CRM_BUSINESS = "CRM 商机"; - String CRM_CONTRACT = "CRM 合同"; - String CRM_PRODUCT = "CRM 产品"; - String CRM_RECEIVABLE = "CRM 回款"; - String CRM_RECEIVABLE_PLAN = "CRM 回款计划"; + // ======================= CRM_LEADS 线索 ======================= - //======================= 客户转移操作日志 ======================= + String CRM_LEADS_TYPE = "CRM 线索"; - String TRANSFER_CUSTOMER_LOG_SUCCESS = "把客户【{{#crmCustomer.name}}】的负责人从【{getAdminUserById{#crmCustomer.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】"; + // ======================= CRM_CUSTOMER 客户 ======================= - // TODO @puhui999:这里格式是不是可以这样;目的是:统一管理,也减少 Service 里各种“复杂”字符串 - // ======================= Customer 客户 ======================= - String CUSTOMER_TYPE = "CRM 客户"; - String CUSTOMER_CREATE_SUB_TYPE = "创建客户"; - String CUSTOMER_CREATE_SUCCESS = "更新了客户{_DIFF{#updateReqVO}}"; + String CRM_CUSTOMER_TYPE = "CRM 客户"; + String CRM_CUSTOMER_CREATE_SUB_TYPE = "创建客户"; + String CRM_CUSTOMER_CREATE_SUCCESS = "创建了客户{{#customer.name}}"; + String CRM_CUSTOMER_UPDATE_SUB_TYPE = "更新客户"; + String CRM_CUSTOMER_UPDATE_SUCCESS = "更新了客户【{{#customerName}}】{_DIFF{#updateReqVO}}"; + String CRM_CUSTOMER_DELETE_SUB_TYPE = "删除客户"; + String CRM_CUSTOMER_DELETE_SUCCESS = "删除了客户【{{#customerName}}】"; + String CRM_CUSTOMER_TRANSFER_SUB_TYPE = "转移客户"; + String CRM_CUSTOMER_TRANSFER_SUCCESS = "将客户【{{#crmCustomer.name}}】的负责人从【{getAdminUserById{#crmCustomer.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】"; + String CRM_CUSTOMER_LOCK_SUB_TYPE = "{{#crmCustomer.lockStatus ? '锁定客户' : '解锁客户'}}"; + String CRM_CUSTOMER_LOCK_SUCCESS = "{{#crmCustomer.lockStatus ? '将客户【#crmCustomer.name】锁定' : '将客户【#crmCustomer.name】解锁'}}"; + String CRM_CUSTOMER_POOL_SUB_TYPE = "客户放入公海"; + String CRM_CUSTOMER_POOL_SUCCESS = "将客户【{{#customerName}}】放入了公海"; + String CRM_CUSTOMER_RECEIVE_SUB_TYPE = "{{#ownerUserName != null ? '分配客户' : '领取客户'}}"; + String CRM_CUSTOMER_RECEIVE_SUCCESS = "{{#ownerUserName != null ? '将客户【#customer.name】分配给【#ownerUserName】' : '领取客户【#customer.name】'}}"; - String CUSTOMER_UPDATE_SUB_TYPE = "更新客户"; + // ======================= CRM_CONTACT 联系人 ======================= + + String CRM_CONTACT_TYPE = "CRM 联系人"; + + // ======================= CRM_BUSINESS 商机 ======================= + + String CRM_BUSINESS_TYPE = "CRM 商机"; + + // ======================= CRM_CONTRACT 合同 ======================= + + String CRM_CONTRACT_TYPE = "CRM 合同"; + + // ======================= CRM_PRODUCT 产品 ======================= + + String CRM_PRODUCT_TYPE = "CRM 产品"; + + // ======================= CRM_RECEIVABLE 回款 ======================= + + String CRM_RECEIVABLE_TYPE = "CRM 回款"; + + // ======================= CRM_RECEIVABLE_PLAN 回款计划 ======================= + + String CRM_RECEIVABLE_PLAN_TYPE = "CRM 回款计划"; } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java index 25bca4485..ab0ae0be4 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.customer; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.*; @@ -38,7 +39,7 @@ 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.security.core.util.SecurityFrameworkUtils.getLoginUserId; -import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.CRM_CUSTOMER; +import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.CRM_CUSTOMER_TYPE; @Tag(name = "管理后台 - CRM 客户") @RestController @@ -115,6 +116,15 @@ public class CrmCustomerController { return success(CrmCustomerConvert.INSTANCE.convertPage(pageResult, userMap, deptMap)); } + @GetMapping(value = {"/list-all-simple"}) + @Operation(summary = "获取客户精简信息列表", description = "只包含有读权限的客户,主要用于前端的下拉选项") + public CommonResult> getSimpleDeptList() { + CrmCustomerPageReqVO reqVO = new CrmCustomerPageReqVO(); + reqVO.setPageSize(PAGE_SIZE_NONE); // 不分页 + List list = customerService.getCustomerPage(reqVO, getLoginUserId()).getList(); + return success(BeanUtils.toBean(list, CrmCustomerSimpleRespVO.class)); + } + @GetMapping("/export-excel") @Operation(summary = "导出客户 Excel") @PreAuthorize("@ss.hasPermission('crm:customer:export')") @@ -143,7 +153,7 @@ public class CrmCustomerController { public CommonResult> getCustomerOperateLog(@RequestParam("id") Long id) { OperateLogV2PageReqDTO reqDTO = new OperateLogV2PageReqDTO(); reqDTO.setPageSize(PAGE_SIZE_NONE); // 不分页 - reqDTO.setBizType(CRM_CUSTOMER); + reqDTO.setBizType(CRM_CUSTOMER_TYPE); reqDTO.setBizId(id); return success(operateLogApi.getOperateLogPage(reqDTO)); } @@ -172,7 +182,7 @@ public class CrmCustomerController { @Parameter(name = "ids", description = "编号数组", required = true, example = "1,2,3") @PreAuthorize("@ss.hasPermission('crm:customer:receive')") public CommonResult receiveCustomer(@RequestParam(value = "ids") List ids) { - customerService.receiveCustomer(ids, getLoginUserId()); + customerService.receiveCustomer(ids, getLoginUserId(), Boolean.TRUE); return success(true); } @@ -180,18 +190,8 @@ public class CrmCustomerController { @Operation(summary = "分配公海给对应负责人") @PreAuthorize("@ss.hasPermission('crm:customer:distribute')") public CommonResult distributeCustomer(@Valid @RequestBody CrmCustomerDistributeReqVO distributeReqVO) { - customerService.receiveCustomer(distributeReqVO.getIds(), distributeReqVO.getOwnerUserId()); + customerService.receiveCustomer(distributeReqVO.getIds(), distributeReqVO.getOwnerUserId(), Boolean.FALSE); return success(true); } - // TODO 芋艿:这个接口要调整下 - //@GetMapping("/query-all-list") - //@Operation(summary = "查询客户列表") - //@PreAuthorize("@ss.hasPermission('crm:customer:all')") - //public CommonResult> queryAll() { - // List crmCustomerDOList = customerService.getCustomerList(); - // List data = CrmCustomerConvert.INSTANCE.convertQueryAll(crmCustomerDOList); - // return success(data); - //} - } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerSimpleRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerSimpleRespVO.java new file mode 100644 index 000000000..22ba6ed4e --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerSimpleRespVO.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Schema(description = "管理后台 - 客户精简信息 Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class CrmCustomerSimpleRespVO { + + @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + private String name; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/permission/CrmPermissionMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/permission/CrmPermissionMapper.java index e7de279d8..a797da779 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/permission/CrmPermissionMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/permission/CrmPermissionMapper.java @@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; import org.apache.ibatis.annotations.Mapper; +import java.util.Collection; import java.util.List; /** @@ -28,6 +29,12 @@ public interface CrmPermissionMapper extends BaseMapperX { .eq(CrmPermissionDO::getBizId, bizId)); } + default List selectByBizTypeAndBizIds(Integer bizType, Collection bizIds) { + return selectList(new LambdaQueryWrapperX() + .eq(CrmPermissionDO::getBizType, bizType) + .in(CrmPermissionDO::getBizId, bizIds)); + } + default List selectListByBizTypeAndUserId(Integer bizType, Long userId) { return selectList(new LambdaQueryWrapperX() .eq(CrmPermissionDO::getBizType, bizType) diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/aop/CrmPermissionAspect.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/aop/CrmPermissionAspect.java index b6fef8c62..03ce96fc3 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/aop/CrmPermissionAspect.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/aop/CrmPermissionAspect.java @@ -18,12 +18,10 @@ import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.anyMatch; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CRM_PERMISSION_DENIED; @@ -46,16 +44,29 @@ public class CrmPermissionAspect { Map expressionValues = parseExpressions(joinPoint, crmPermission); Integer bizType = StrUtil.isEmpty(crmPermission.bizTypeValue()) ? crmPermission.bizType()[0].getType() : (Integer) expressionValues.get(crmPermission.bizTypeValue()); // 模块类型 - Long bizId = (Long) expressionValues.get(crmPermission.bizId()); // 模块数据编号 + // 处理兼容多个 bizId 的情况 + Object object = expressionValues.get(crmPermission.bizId());// 模块数据编号 + Set bizIds = new HashSet<>(); + if (object instanceof Collection) { + bizIds.addAll(convertSet((Collection) object, item -> Long.parseLong(item.toString()))); + } else { + bizIds.add(Long.parseLong(object.toString())); + } Integer permissionLevel = crmPermission.level().getLevel(); // 需要的权限级别 + List permissionList = crmPermissionService.getPermissionListByBiz(bizType, bizIds); + Map> multiMap = convertMultiMap(permissionList, CrmPermissionDO::getBizId); + bizIds.forEach(bizId -> { + validatePermission(bizType, multiMap.get(bizId), permissionLevel); + }); + } - // 1.1 如果是超级管理员则直接通过 + private void validatePermission(Integer bizType, List bizPermissions, Integer permissionLevel) { + // 1. 如果是超级管理员则直接通过 if (CrmPermissionUtils.isCrmAdmin()) { return; } - // 1.2 获取数据权限 - List bizPermissions = crmPermissionService.getPermissionListByBiz(bizType, bizId); - if (CollUtil.isEmpty(bizPermissions)) { // 没有数据权限的情况 + // 1.1 没有数据权限的情况 + if (CollUtil.isEmpty(bizPermissions)) { // 公海数据如果没有团队成员大家也因该有读权限才对 if (CrmPermissionLevelEnum.isRead(permissionLevel)) { return; @@ -63,7 +74,7 @@ public class CrmPermissionAspect { // 没有数据权限的情况下超出了读权限直接报错,避免后面校验空指针 throw exception(CRM_PERMISSION_DENIED, CrmBizTypeEnum.getNameByType(bizType)); - } else { // 有数据权限但是没有负责人的情况 + } else { // 1.2 有数据权限但是没有负责人的情况 if (!anyMatch(bizPermissions, item -> CrmPermissionLevelEnum.isOwner(item.getLevel()))) { if (CrmPermissionLevelEnum.isRead(permissionLevel)) { return; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java index 1b6779916..12df6c8ba 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java @@ -86,7 +86,7 @@ public interface CrmCustomerService { * 锁定/解锁客户 * * @param lockReqVO 更新信息 - * @param userId 用户编号 + * @param userId 用户编号 */ void lockCustomer(@Valid CrmCustomerLockReqVO lockReqVO, Long userId); @@ -104,15 +104,8 @@ public interface CrmCustomerService { * * @param ids 要领取的客户编号数组 * @param ownerUserId 负责人 + * @param isReceive 是/否领取 */ - void receiveCustomer(List ids, Long ownerUserId); - - /** - * 获取客户列表 - * - * @return 客户列表 - * @author zyna - */ - List getCustomerList(); + void receiveCustomer(List ids, Long ownerUserId, Boolean isReceive); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java index c8e5f4e92..898a966a4 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java @@ -16,9 +16,11 @@ import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper; import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 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.util.CrmPermissionUtils; import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; 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.dto.AdminUserRespDTO; import com.mzt.logapi.context.LogRecordContext; import com.mzt.logapi.service.impl.DiffParseFunction; import com.mzt.logapi.starter.annotation.LogRecord; @@ -35,8 +37,7 @@ import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; -import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.CRM_CUSTOMER; -import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.TRANSFER_CUSTOMER_LOG_SUCCESS; +import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; import static cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum.CUSTOMER_LOCK_LIMIT; import static cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum.CUSTOMER_OWNER_LIMIT; import static java.util.Collections.singletonList; @@ -63,8 +64,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @Override @Transactional(rollbackFor = Exception.class) - @LogRecord(type = CRM_CUSTOMER, subType = "创建客户", bizNo = "{{#customerId}}", success = "创建了客户") - // TODO @puhui999:创建了客户【客户名】,要记录进去;不然在展示操作日志的全列表,看不清楚是哪个客户哈; + @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_CREATE_SUB_TYPE, bizNo = "{{#customer.id}}", success = CRM_CUSTOMER_CREATE_SUCCESS) public Long createCustomer(CrmCustomerSaveReqVO createReqVO, Long userId) { createReqVO.setId(null); // 1. 校验拥有客户是否到达上限 @@ -81,14 +81,14 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()) .setBizId(customer.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人 - // 4. 记录操作日志 - LogRecordContext.putVariable("customerId", customer.getId()); + // 4. 记录操作日志上下文 + LogRecordContext.putVariable("customer", customer); return customer.getId(); } @Override @Transactional(rollbackFor = Exception.class) - @LogRecord(type = CRM_CUSTOMER, subType = "更新客户", bizNo = "{{#updateReqVO.id}}", success = "更新了客户{_DIFF{#updateReqVO}}", extra = "{{#extra}}") + @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", success = CRM_CUSTOMER_UPDATE_SUCCESS) @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE) public void updateCustomer(CrmCustomerSaveReqVO updateReqVO) { Assert.notNull(updateReqVO.getId(), "客户编号不能为空"); @@ -101,17 +101,18 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { CrmCustomerDO updateObj = CrmCustomerConvert.INSTANCE.convert(updateReqVO); customerMapper.updateById(updateObj); - // 3. 记录操作日志 + // 3. 记录操作日志上下文 LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldCustomer, CrmCustomerSaveReqVO.class)); + LogRecordContext.putVariable("customerName", oldCustomer.getName()); } @Override @Transactional(rollbackFor = Exception.class) - @LogRecord(type = CRM_CUSTOMER, subType = "删除客户", bizNo = "{{#id}}", success = "删除了客户") + @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_DELETE_SUB_TYPE, bizNo = "{{#id}}", success = CRM_CUSTOMER_DELETE_SUCCESS) @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) public void deleteCustomer(Long id) { // 校验存在 - validateCustomerExists(id); + CrmCustomerDO customer = validateCustomerExists(id); // TODO @puhui999:如果有联系人、商机,则不允许删除; // 删除 @@ -119,16 +120,144 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { // 删除数据权限 permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), id); // TODO @puhui999:删除跟进记录 + + // 记录操作日志上下文 + LogRecordContext.putVariable("customerName", customer.getName()); } - private CrmCustomerDO validateCustomerExists(Long id) { - CrmCustomerDO customerDO = customerMapper.selectById(id); - if (customerDO == null) { + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.id}}", success = CRM_CUSTOMER_TRANSFER_SUCCESS) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER) + public void transferCustomer(CrmCustomerTransferReqVO reqVO, Long userId) { + // 1.1 校验客户是否存在 + CrmCustomerDO customer = validateCustomerExists(reqVO.getId()); + // 1.2 校验拥有客户是否到达上限 + validateCustomerExceedOwnerLimit(reqVO.getNewOwnerUserId(), 1); + + // 2.1 数据权限转移 + permissionService.transferPermission( + CrmCustomerConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())); + // 2.2 转移后重新设置负责人 + customerMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId()); + + // 3. TODO 记录转移日志 + // 记录操作日志上下文 + LogRecordContext.putVariable("crmCustomer", customer); + } + + @Override + @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_LOCK_SUB_TYPE, bizNo = "{{#lockReqVO.id}}", success = CRM_CUSTOMER_LOCK_SUCCESS) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#lockReqVO.id", level = CrmPermissionLevelEnum.OWNER) + public void lockCustomer(CrmCustomerLockReqVO lockReqVO, Long userId) { + // 1.1 校验当前客户是否存在 + CrmCustomerDO customer = validateCustomerExists(lockReqVO.getId()); + // 1.2 校验当前是否重复操作锁定/解锁状态 + if (customer.getLockStatus().equals(lockReqVO.getLockStatus())) { + throw exception(customer.getLockStatus() ? CUSTOMER_LOCK_FAIL_IS_LOCK : CUSTOMER_UNLOCK_FAIL_IS_UNLOCK); + } + // 1.3 校验锁定上限。 + if (lockReqVO.getLockStatus()) { + validateCustomerExceedLockLimit(userId); + } + + // 2. 更新锁定状态 + customerMapper.updateById(BeanUtils.toBean(lockReqVO, CrmCustomerDO.class)); + + // 3. 记录操作日志上下文 + LogRecordContext.putVariable("crmCustomer", customer); + } + + // ==================== 公海相关操作 ==================== + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_POOL_SUB_TYPE, bizNo = "{{#id}}", success = CRM_CUSTOMER_POOL_SUCCESS) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) + public void putCustomerPool(Long id) { + // 1. 校验存在 + CrmCustomerDO customer = customerMapper.selectById(id); + if (customer == null) { throw exception(CUSTOMER_NOT_EXISTS); } - return customerDO; + // 1.2. 校验是否为公海数据 + validateCustomerOwnerExists(customer, true); + // 1.3. 校验客户是否锁定 + validateCustomerIsLocked(customer, true); + + // 2. 设置负责人为 NULL + int updateOwnerUserIncr = customerMapper.updateOwnerUserIdById(customer.getId(), null); + if (updateOwnerUserIncr == 0) { + throw exception(CUSTOMER_UPDATE_OWNER_USER_FAIL); + } + // 3. 删除负责人数据权限 + permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), customer.getId(), + CrmPermissionLevelEnum.OWNER.getLevel()); + // TODO @puhui999:联系人的负责人,也要设置为 null;这块和领取是对应的;因为领取后,负责人也要关联过来; + + // 记录操作日志上下文 + LogRecordContext.putVariable("customerName", customer.getName()); } + @Override + @Transactional(rollbackFor = Exception.class) + public void receiveCustomer(List ids, Long ownerUserId, Boolean isReceive) { + if (!isReceive && !CrmPermissionUtils.isCrmAdmin()) { // 只有管理员可以分配 + throw exception(CRM_PERMISSION_DENIED, CrmBizTypeEnum.CRM_CUSTOMER.getName()); + } + + // 1.1 校验存在 + List customers = customerMapper.selectBatchIds(ids); + if (customers.size() != ids.size()) { + throw exception(CUSTOMER_NOT_EXISTS); + } + // 1.2. 校验负责人是否存在 + adminUserApi.validateUserList(singletonList(ownerUserId)); + // 1.3. 校验状态 + customers.forEach(customer -> { + // 校验是否已有负责人 + validateCustomerOwnerExists(customer, false); + // 校验是否锁定 + validateCustomerIsLocked(customer, false); + // 校验成交状态 + validateCustomerDeal(customer); + }); + // 1.4 校验负责人是否到达上限 + validateCustomerExceedOwnerLimit(ownerUserId, customers.size()); + + // 2.1 领取公海数据 + List updateCustomers = new ArrayList<>(); + List createPermissions = new ArrayList<>(); + customers.forEach(customer -> { + // 2.1. 设置负责人 + updateCustomers.add(new CrmCustomerDO().setId(customer.getId()).setOwnerUserId(ownerUserId)); + // 2.2. 创建负责人数据权限 + createPermissions.add(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()) + .setBizId(customer.getId()).setUserId(ownerUserId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); + }); + // 2.2 更新客户负责人 + customerMapper.updateBatch(updateCustomers); + // 2.3 创建负责人数据权限 + permissionService.createPermissionBatch(createPermissions); + // TODO @芋艿:要不要处理关联的联系人??? + + // 3. 记录操作日志 + AdminUserRespDTO user = null; + if (!isReceive) { + user = adminUserApi.getUser(ownerUserId); + } + for (CrmCustomerDO customer : customers) { + receiveCustomerLog(customer, user == null ? null : user.getNickname()); + } + } + + @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_RECEIVE_SUB_TYPE, bizNo = "{{#customer.id}}", success = CRM_CUSTOMER_RECEIVE_SUCCESS) + public void receiveCustomerLog(CrmCustomerDO customer, String ownerUserName) { + + } + + //======================= 查询相关 ======================= + @Override @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.READ) public CrmCustomerDO getCustomer(Long id) { @@ -148,6 +277,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { return customerMapper.selectPage(pageReqVO, userId); } + //======================= 校验相关 ======================= + /** * 校验客户是否存在 * @@ -158,47 +289,38 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { validateCustomerExists(customerId); } - @Override - @Transactional(rollbackFor = Exception.class) - @LogRecord(type = CRM_CUSTOMER, subType = "转移客户", bizNo = "{{#reqVO.id}}", success = TRANSFER_CUSTOMER_LOG_SUCCESS) - @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER) - public void transferCustomer(CrmCustomerTransferReqVO reqVO, Long userId) { - // 1.1 校验客户是否存在 - CrmCustomerDO customer = validateCustomerExists(reqVO.getId()); - // 1.2 校验拥有客户是否到达上限 - validateCustomerExceedOwnerLimit(reqVO.getNewOwnerUserId(), 1); - - // 2.1 数据权限转移 - permissionService.transferPermission( - CrmCustomerConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())); - // 2.2 转移后重新设置负责人 - customerMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId()); - - // 3. TODO 记录转移日志 - LogRecordContext.putVariable("crmCustomer", customer); + private void validateCustomerOwnerExists(CrmCustomerDO customer, Boolean pool) { + if (customer == null) { // 防御一下 + throw exception(CUSTOMER_NOT_EXISTS); + } + // 校验是否为公海数据 + if (pool && customer.getOwnerUserId() == null) { + throw exception(CUSTOMER_IN_POOL, customer.getName()); + } + // 负责人已存在 + if (customer.getOwnerUserId() != null) { + throw exception(CUSTOMER_OWNER_EXISTS, customer.getName()); + } } - @Override - // TODO @puhui999:看看这个能不能根据条件,写操作日志; - // TODO 如果是 锁定,则 subType 为 锁定客户;success 为 将客户【】锁定 - // TODO 如果是 解锁,则 subType 为 解锁客户;success 为 将客户【】解锁 - @LogRecord(type = CRM_CUSTOMER, subType = "锁定/解锁客户", bizNo = "{{#updateReqVO.id}}", success = "锁定了客户") - // TODO @puhui999:数据权限 - public void lockCustomer(CrmCustomerLockReqVO lockReqVO, Long userId) { - // 1.1 校验当前客户是否存在 - validateCustomerExists(lockReqVO.getId()); - // 1.2 校验当前是否重复操作锁定/解锁状态 - CrmCustomerDO customer = customerMapper.selectById(lockReqVO.getId()); - if (customer.getLockStatus().equals(lockReqVO.getLockStatus())) { - throw exception(customer.getLockStatus() ? CUSTOMER_LOCK_FAIL_IS_LOCK : CUSTOMER_UNLOCK_FAIL_IS_UNLOCK); - } - // 1.3 校验锁定上限。 - if (lockReqVO.getLockStatus()) { - validateCustomerExceedLockLimit(userId); + private CrmCustomerDO validateCustomerExists(Long id) { + CrmCustomerDO customerDO = customerMapper.selectById(id); + if (customerDO == null) { + throw exception(CUSTOMER_NOT_EXISTS); } + return customerDO; + } - // 2. 更新锁定状态 - customerMapper.updateById(BeanUtils.toBean(lockReqVO, CrmCustomerDO.class)); + private void validateCustomerIsLocked(CrmCustomerDO customer, Boolean pool) { + if (customer.getLockStatus()) { + throw exception(pool ? CUSTOMER_LOCKED_PUT_POOL_FAIL : CUSTOMER_LOCKED, customer.getName()); + } + } + + private void validateCustomerDeal(CrmCustomerDO customer) { + if (customer.getDealStatus()) { + throw exception(CUSTOMER_ALREADY_DEAL); + } } /** @@ -242,106 +364,4 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { } } - @Override - @Transactional(rollbackFor = Exception.class) - @LogRecord(type = CRM_CUSTOMER, subType = "客户放入公海", bizNo = "{{#id}}", success = "将客户放入了公海") - // TODO @puhui999:将客户【】放入了公海 - @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) - public void putCustomerPool(Long id) { - // 1. 校验存在 - CrmCustomerDO customer = customerMapper.selectById(id); - if (customer == null) { - throw exception(CUSTOMER_NOT_EXISTS); - } - // 1.2. 校验是否为公海数据 - validateCustomerOwnerExists(customer, true); - // 1.3. 校验客户是否锁定 - validateCustomerIsLocked(customer, true); - - // 2. 设置负责人为 NULL - int updateOwnerUserIncr = customerMapper.updateOwnerUserIdById(customer.getId(), null); - if (updateOwnerUserIncr == 0) { - throw exception(CUSTOMER_UPDATE_OWNER_USER_FAIL); - } - // 3. 删除负责人数据权限 - permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), customer.getId(), - CrmPermissionLevelEnum.OWNER.getLevel()); - // TODO @puhui999:联系人的负责人,也要设置为 null;这块和领取是对应的;因为领取后,负责人也要关联过来; - } - - @Override - @Transactional(rollbackFor = Exception.class) - // TODO @puhui999:权限校验 - - // TODO @puhui999:如果是分配,操作日志是 “将客户【】分配给【】” - // TODO @puhui999:如果是领取,操作日志是“领取客户【】”; - // TODO @puhui999:如果是多条,则需要记录多条操作日志;不然 bizId 不好关联 - public void receiveCustomer(List ids, Long ownerUserId) { - // 1.1 校验存在 - List customers = customerMapper.selectBatchIds(ids); - if (customers.size() != ids.size()) { - throw exception(CUSTOMER_NOT_EXISTS); - } - // 1.2. 校验负责人是否存在 - adminUserApi.validateUserList(singletonList(ownerUserId)); - // 1.3. 校验状态 - customers.forEach(customer -> { - // 校验是否已有负责人 - validateCustomerOwnerExists(customer, false); - // 校验是否锁定 - validateCustomerIsLocked(customer, false); - // 校验成交状态 - validateCustomerDeal(customer); - }); - // 1.4 校验负责人是否到达上限 - validateCustomerExceedOwnerLimit(ownerUserId, customers.size()); - - // 2.1 领取公海数据 - List updateCustomers = new ArrayList<>(); - List createPermissions = new ArrayList<>(); - customers.forEach(customer -> { - // 2.1. 设置负责人 - updateCustomers.add(new CrmCustomerDO().setId(customer.getId()).setOwnerUserId(ownerUserId)); - // 2.2. 创建负责人数据权限 - createPermissions.add(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()) - .setBizId(customer.getId()).setUserId(ownerUserId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); - }); - // 2.2 更新客户负责人 - customerMapper.updateBatch(updateCustomers); - // 2.3 创建负责人数据权限 - permissionService.createPermissionBatch(createPermissions); - // TODO @芋艿:要不要处理关联的联系人??? - } - - private void validateCustomerOwnerExists(CrmCustomerDO customer, Boolean pool) { - if (customer == null) { // 防御一下 - throw exception(CUSTOMER_NOT_EXISTS); - } - // 校验是否为公海数据 - if (pool && customer.getOwnerUserId() == null) { - throw exception(CUSTOMER_IN_POOL, customer.getName()); - } - // 负责人已存在 - if (customer.getOwnerUserId() != null) { - throw exception(CUSTOMER_OWNER_EXISTS, customer.getName()); - } - } - - private void validateCustomerIsLocked(CrmCustomerDO customer, Boolean pool) { - if (customer.getLockStatus()) { - throw exception(pool ? CUSTOMER_LOCKED_PUT_POOL_FAIL : CUSTOMER_LOCKED, customer.getName()); - } - } - - private void validateCustomerDeal(CrmCustomerDO customer) { - if (customer.getDealStatus()) { - throw exception(CUSTOMER_ALREADY_DEAL); - } - } - - @Override - public List getCustomerList() { - return customerMapper.selectList(); - } - } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionService.java index d2e3f80cb..e822ac2ff 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionService.java @@ -90,6 +90,15 @@ public interface CrmPermissionService { */ List getPermissionListByBiz(Integer bizType, Long bizId); + /** + * 获取数据权限列表,通过 数据类型 x 某个数据 + * + * @param bizType 数据类型,关联 {@link CrmBizTypeEnum} + * @param bizIds 数据编号,关联 {@link CrmBizTypeEnum} 对应模块 DO#getId() + * @return Crm 数据权限列表 + */ + List getPermissionListByBiz(Integer bizType, Collection bizIds); + /** * 获取用户参与的模块数据列表 * diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImpl.java index aaf319844..854bf90a1 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImpl.java @@ -187,6 +187,11 @@ public class CrmPermissionServiceImpl implements CrmPermissionService { return crmPermissionMapper.selectByBizTypeAndBizId(bizType, bizId); } + @Override + public List getPermissionListByBiz(Integer bizType, Collection bizIds) { + return crmPermissionMapper.selectByBizTypeAndBizIds(bizType, bizIds); + } + @Override public List getPermissionListByBizTypeAndUserId(Integer bizType, Long userId) { return crmPermissionMapper.selectListByBizTypeAndUserId(bizType, userId);