📖 CRM:code review 客户管理的数据权限、操作权限

This commit is contained in:
YunaiV
2023-12-17 10:57:49 +08:00
parent bada32750c
commit ddb6fe7ec8
19 changed files with 63 additions and 64 deletions

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.crm.enums;
// TODO 芋艿:操作日志;看看这个类怎么搞个好点的规范;
/**
* CRM 操作日志枚举
*
@ -9,18 +10,18 @@ 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-回款计划";
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 回款计划";
//======================= 客户转移操作日志 =======================
String TRANSFER_CUSTOMER_LOG_SUCCESS = "把客户【{{#crmCustomer.name}}】的负责人从【{getAdminUserById{#crmCustomer.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】";
String TRANSFER_CUSTOMER_LOG_FAIL = "";
String TRANSFER_CUSTOMER_LOG_FAIL = ""; // TODO @puhui999这个可以删除哈一般不搞失败的日志
}

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
@ -13,19 +14,12 @@ public class CrmClueTransferReqVO {
@NotNull(message = "线索编号不能为空")
private Long id;
/**
* 新负责人的用户编号
*/
@Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
@NotNull(message = "新负责人的用户编号不能为空")
private Long newOwnerUserId;
/**
* 老负责人加入团队后的权限级别。如果 null 说明移除
*
* 关联 {@link CrmPermissionLevelEnum}
*/
@Schema(description = "老负责人加入团队后的权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
private Integer oldOwnerPermissionLevel;
@InEnum(value = CrmPermissionLevelEnum.class)
private Integer oldOwnerPermissionLevel; // 老负责人加入团队后的权限级别。如果 null 说明移除
}

View File

@ -135,18 +135,22 @@ public class CrmCustomerController {
return success(true);
}
// TODO @puhui999operate-log-list 或者 operate-log-page 如果分页
@GetMapping("/operate-log")
@Operation(summary = "获得客户操作日志")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('crm:customer:query')")
// TODO @puhui999最好有读权限方法名改成 getCustomerOperateLog
public CommonResult<List<OperateLogV2RespDTO>> getOperateLog(@RequestParam("id") Long id) {
// 1. 获取客户
// TODO @puhui999这个校验可以去掉哈
CrmCustomerDO customer = customerService.getCustomer(id);
if (customer == null) {
return success(null);
}
// 2. 获取操作日志
// TODO @puhui999操作日志返回可能要分页哈
return success(operateLogApi.getOperateLogByModuleAndBizId(CRM_CUSTOMER, id));
}

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
@ -13,19 +14,12 @@ public class CrmReceivablePlanTransferReqVO {
@NotNull(message = "回款计划编号不能为空")
private Long id;
/**
* 新负责人的用户编号
*/
@Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
@NotNull(message = "新负责人的用户编号不能为空")
private Long newOwnerUserId;
/**
* 老负责人加入团队后的权限级别。如果 null 说明移除
*
* 关联 {@link CrmPermissionLevelEnum}
*/
@Schema(description = "老负责人加入团队后的权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
private Integer oldOwnerPermissionLevel;
@InEnum(value = CrmPermissionLevelEnum.class)
private Integer oldOwnerPermissionLevel; // 老负责人加入团队后的权限级别。如果 null 说明移除
}

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
@ -13,19 +14,12 @@ public class CrmReceivableTransferReqVO {
@NotNull(message = "回款编号不能为空")
private Long id;
/**
* 新负责人的用户编号
*/
@Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
@NotNull(message = "新负责人的用户编号不能为空")
private Long newOwnerUserId;
/**
* 老负责人加入团队后的权限级别。如果 null 说明移除
*
* 关联 {@link CrmPermissionLevelEnum}
*/
@Schema(description = "老负责人加入团队后的权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
private Integer oldOwnerPermissionLevel;
@InEnum(value = CrmPermissionLevelEnum.class)
private Integer oldOwnerPermissionLevel; // 老负责人加入团队后的权限级别。如果 null 说明移除
}

View File

@ -8,6 +8,7 @@ import org.springframework.stereotype.Component;
import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY;
// TODO @puhui999包名使用 operatelog 更合适哈;
/**
* 自定义函数-通过行业编号获取行业信息
*

View File

@ -26,6 +26,7 @@ import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CRM_PERMISSION_DENIED;
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CRM_PERMISSION_MODEL_NOT_EXISTS;
// TODO 这个包,改成 permission然后搞 config 和 core 包,这个类在 core 包里目的是framework 最好分类下
/**
* Crm 数据权限校验 AOP 切面
*

View File

@ -137,12 +137,14 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
@Override
@Transactional(rollbackFor = Exception.class)
// TODO @puhui999@LogRecord(type = CRM_CUSTOMER, subType = "客户转移", bizNo = "{{#reqVO.id}}", success = TRANSFER_CUSTOMER_LOG_SUCCESS)
@LogRecord(success = TRANSFER_CUSTOMER_LOG_SUCCESS, type = CRM_CUSTOMER, subType = "客户转移", bizNo = "{{#reqVO.id}}")
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER)
public void transferCustomer(CrmCustomerTransferReqVO reqVO, Long userId) {
// 1. 校验客户是否存在
validateCustomer(reqVO.getId());
// 添加 crmCustomer 到日志上下文 TODO 日志记录放在 service 里是因为已经过了权限校验查询时不用走两次校验
// TODO @puhui999customer 不用查询,从 1. 拿到哈;然后 put这个动作可以放到 3.;这样逻辑结构就是,校验、逻辑、日志,更加清晰
LogRecordContext.putVariable("crmCustomer", customerMapper.selectById(reqVO.getId()));
// 2.1 数据权限转移
crmPermissionService.transferPermission(

View File

@ -138,11 +138,10 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
}
@Override
@Transactional(rollbackFor = Exception.class)
@Transactional(rollbackFor = Exception.class) // TODO @puhui999这里不用加的就一个操作哈
public void deletePermission(Integer bizType, Long bizId) {
// 删除数据权限
int deletedCol = crmPermissionMapper.deletePermission(bizType, bizId);
if (deletedCol == 0) {
int deletedCount = crmPermissionMapper.deletePermission(bizType, bizId);
if (deletedCount == 0) {
throw exception(CRM_PERMISSION_NOT_EXISTS);
}
}

View File

@ -61,9 +61,9 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
receivablePlanMapper.insert(receivablePlan);
// 创建数据权限
crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType())
.setBizId(receivablePlan.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
// 返回
crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(userId)
.setBizType(CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType()).setBizId(receivablePlan.getId())
.setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
return receivablePlan.getId();
}

View File

@ -35,14 +35,14 @@ public class CrmQueryWrapperUtils {
* @param pool 公海
* @return 是否 (是:需要执行查询,否:不需要查询调用方法直接返回空)
*/
// TODO @puhui999bizId 直接传递会不会简单点 回复:还是需要 SFunction 因为分页连表时不知道 bizId 是多少
// TODO @puhui999bizId 直接传递会不会简单点 回复:还是需要 SFunction 因为分页连表时不知道 bizId 是多少;是不是把 bizId 传入就好啦?
public static <T extends MPJLambdaWrapper<?>, S> boolean appendPermissionCondition(T query, Integer bizType, SFunction<S, ?> bizId,
Long userId, Integer sceneType, Boolean pool) {
// 1. 构建数据权限连表条件
if (ObjUtil.notEqual(validateAdminUser(userId), Boolean.TRUE)) { // 管理员不需要数据权限
query.innerJoin(CrmPermissionDO.class, on ->
on.eq(CrmPermissionDO::getBizType, bizType).eq(CrmPermissionDO::getBizId, bizId)
.eq(CrmPermissionDO::getUserId, userId));
query.innerJoin(CrmPermissionDO.class, on -> on.eq(CrmPermissionDO::getBizType, bizType)
.eq(CrmPermissionDO::getBizId, bizId)
.eq(CrmPermissionDO::getUserId, userId));
}
// 2.1 场景一:我负责的数据
if (CrmSceneTypeEnum.isOwner(sceneType)) {
@ -50,15 +50,15 @@ public class CrmQueryWrapperUtils {
}
// 2.2 场景二:我参与的数据
if (CrmSceneTypeEnum.isInvolved(sceneType)) {
query
.ne("owner_user_id", userId)
query.ne("owner_user_id", userId)
// TODO @puhui999IN 是不是更合适哈;
.and(q -> q.eq(CrmPermissionDO::getLevel, CrmPermissionLevelEnum.READ.getLevel())
.or()
.eq(CrmPermissionDO::getLevel, CrmPermissionLevelEnum.WRITE.getLevel()));
}
// 2.3 场景三:下属负责的数据
if (CrmSceneTypeEnum.isSubordinate(sceneType)) {
// TODO @puhui999要不如果没有下属拼一个 owner_user_id in null不返回结果就好啦
List<AdminUserRespDTO> subordinateUsers = getAdminUserApi().getUserListBySubordinate(userId);
if (CollUtil.isEmpty(subordinateUsers)) {
return false;
@ -72,7 +72,6 @@ public class CrmQueryWrapperUtils {
} else { // 情况二:不是公海
query.isNotNull("owner_user_id");
}
return true;
}
@ -106,6 +105,7 @@ public class CrmQueryWrapperUtils {
*/
private static boolean validateAdminUser(Long userId) {
// TODO 查询权限配置表用户的角色信息
// TODO @puhui999查询用户的角色;CRM_ADMIN("crm_admin", "CRM 管理员"),
//CrmPermissionConfig permissionConfig = crmPermissionConfigService.getPermissionConfigByUserId(userId);
//if (permissionConfig == null) {
// return false;