完善 CRM-数据权限校验方式

This commit is contained in:
puhui999 2023-10-30 16:29:24 +08:00
parent 19c9b6ae93
commit e6eaa3a24a
40 changed files with 399 additions and 392 deletions

View File

@ -11,17 +11,12 @@ public interface ErrorCodeConstants {
// ========== 合同管理 1-020-000-000 ==========
ErrorCode CONTRACT_NOT_EXISTS = new ErrorCode(1_020_000_000, "合同不存在");
ErrorCode CONTRACT_TRANSFER_FAIL_PERMISSION_DENIED = new ErrorCode(1_020_000_001, "合同转移失败,原因:没有转移权限"); // TODO @puhui999这个搞成 合同操作失败原因没有权限
ErrorCode CONTRACT_TRANSFER_FAIL_OWNER_USER_NOT_EXISTS = new ErrorCode(1_020_000_002, "合同转移失败,原因:负责人不存在");
// ========== 线索管理 1-020-001-000 ==========
ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "线索不存在");
// ========== 商机管理 1-020-002-000 ==========
ErrorCode BUSINESS_NOT_EXISTS = new ErrorCode(1_020_002_000, "商机不存在");
ErrorCode BUSINESS_TRANSFER_FAIL_PERMISSION_DENIED = new ErrorCode(1_020_002_001, "商机操作失败,原因:没有权限");
ErrorCode BUSINESS_TRANSFER_FAIL_OWNER_USER_NOT_EXISTS = new ErrorCode(1_020_002_002, "商机操作失败,原因:负责人不存在");
ErrorCode BUSINESS_TRANSFER_FAIL_OWNER_USER_EXISTS = new ErrorCode(1_020_002_003, "商机操作失败,原因:转移对象已经是该负责人");
// TODO @lilleo商机状态商机类型都单独错误码段
@ -30,8 +25,6 @@ public interface ErrorCodeConstants {
// ========== 联系人管理 1-020-003-000 ==========
ErrorCode CONTACT_NOT_EXISTS = new ErrorCode(1_020_003_000, "联系人不存在");
ErrorCode CONTACT_TRANSFER_FAIL_PERMISSION_DENIED = new ErrorCode(1_020_003_001, "联系人转移失败,原因:没有转移权限"); // TODO @puhui999这个搞成 联系人操作失败原因没有权限
ErrorCode CONTACT_TRANSFER_FAIL_OWNER_USER_NOT_EXISTS = new ErrorCode(1_020_003_002, "联系人转移失败,原因:负责人不存在");
// TODO @liuhongfeng错误码分段
ErrorCode RECEIVABLE_NOT_EXISTS = new ErrorCode(1_030_000_001, "回款管理不存在");
@ -43,5 +36,9 @@ public interface ErrorCodeConstants {
// ========== 客户管理 1_020_007_000 ==========
ErrorCode CRM_PERMISSION_NOT_EXISTS = new ErrorCode(1_020_007_000, "数据权限不存在");
ErrorCode CRM_PERMISSION_DENIED = new ErrorCode(1_020_007_001, "{}操作失败,原因:没有权限");
ErrorCode CRM_PERMISSION_MODEL_NOT_EXISTS = new ErrorCode(1_020_007_002, "{}不存在");
ErrorCode CRM_PERMISSION_MODEL_TRANSFER_FAIL_OWNER_USER_NOT_EXISTS = new ErrorCode(1_020_007_003, "{}操作失败,原因:负责人不存在");
ErrorCode CRM_PERMISSION_MODEL_TRANSFER_FAIL_OWNER_USER_EXISTS = new ErrorCode(1_020_007_004, "{}操作失败,原因:转移对象已经是该负责人");
}

View File

@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.crm.enums.common;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
@Getter
@AllArgsConstructor
public enum PermissionTypeEnum implements IntArrayValuable {
READONLY(1, "只读"),
READ_AND_WRITE(2, "读写");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PermissionTypeEnum::getType).toArray();
/**
* 类型
*/
private final Integer type;
/**
* 类型名
*/
private final String name;
@Override
public int[] array() {
return ARRAYS;
}
}

View File

@ -0,0 +1,36 @@
package cn.iocoder.yudao.module.crm.enums.common;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* Crm 负责人转移后原负责人的处理方式
*
* @author HUIHUI
*/
@Getter
@AllArgsConstructor
public enum TransferTypeEnum implements IntArrayValuable {
REMOVE(1, "移除"),
TEAM(2, "转为团队成员");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TransferTypeEnum::getType).toArray();
/**
* 类型
*/
private final Integer type;
/**
* 类型名
*/
private final String name;
@Override
public int[] array() {
return ARRAYS;
}
}

View File

@ -1,6 +1,5 @@
package cn.iocoder.yudao.module.crm.controller.admin.business;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
@ -8,7 +7,6 @@ import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.*;
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.framework.utils.CrmPermissionUtils;
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@ -40,7 +38,7 @@ public class CrmBusinessController {
@Operation(summary = "创建商机")
@PreAuthorize("@ss.hasPermission('crm:business:create')")
public CommonResult<Long> createBusiness(@Valid @RequestBody CrmBusinessCreateReqVO createReqVO) {
return success(businessService.createBusiness(createReqVO));
return success(businessService.createBusiness(createReqVO, getLoginUserId()));
}
@PutMapping("/update")
@ -65,7 +63,6 @@ public class CrmBusinessController {
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('crm:business:query')")
public CommonResult<CrmBusinessRespVO> getBusiness(@RequestParam("id") Long id) {
CrmPermissionUtils.setCrmTransferInfo(getLoginUserId(), UserTypeEnum.ADMIN.getValue());
CrmBusinessDO business = businessService.getBusiness(id);
return success(CrmBusinessConvert.INSTANCE.convert(business));
}
@ -95,7 +92,6 @@ public class CrmBusinessController {
@Operation(summary = "商机转移")
@PreAuthorize("@ss.hasPermission('crm:business:update')")
public CommonResult<Boolean> transfer(@Valid @RequestBody CrmTransferBusinessReqVO reqVO) {
CrmPermissionUtils.setCrmTransferInfo(getLoginUserId(), UserTypeEnum.ADMIN.getValue(), reqVO);
businessService.businessTransfer(reqVO, getLoginUserId());
return success(true);
}

View File

@ -1,11 +1,34 @@
package cn.iocoder.yudao.module.crm.controller.admin.business.vo;
import cn.iocoder.yudao.module.crm.framework.vo.CrmTransferBaseVO;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.crm.enums.common.PermissionTypeEnum;
import cn.iocoder.yudao.module.crm.enums.common.TransferTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotNull;
@Schema(description = "管理后台 - 商机转移 Request VO")
@Data
public class CrmTransferBusinessReqVO extends CrmTransferBaseVO {
public class CrmTransferBusinessReqVO {
@Schema(description = "商机编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
@NotNull(message = "联系人编号不能为空")
private Long id;
@Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
@NotNull(message = "新负责人的用户编号不能为空")
private Long ownerUserId;
@Schema(description = "原负责人移除方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
@InEnum(TransferTypeEnum.class)
@NotNull(message = "原负责人移除方式不能为空")
private Integer transferType;
@Schema(description = "权限类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
@InEnum(PermissionTypeEnum.class)
@NotNull(message = "权限类型不能为空")
private Integer permissionType;
}

View File

@ -42,10 +42,6 @@ public class CrmClueBaseVO {
@Schema(description = "地址", example = "北京市海淀区")
private String address;
@Schema(description = "负责人的用户编号", example = "27199")
@NotNull(message = "负责人不能为空")
private Long ownerUserId;
@Schema(description = "最后跟进时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime contactLastTime;

View File

@ -39,7 +39,7 @@ public class ContactController {
@Operation(summary = "创建crm联系人")
@PreAuthorize("@ss.hasPermission('crm:contact:create')")
public CommonResult<Long> createContact(@Valid @RequestBody ContactCreateReqVO createReqVO) {
return success(contactService.createContact(createReqVO));
return success(contactService.createContact(createReqVO, getLoginUserId()));
}
@PutMapping("/update")

View File

@ -5,18 +5,10 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.util.Set;
@Schema(description = "管理后台 - crm联系人创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ContactCreateReqVO extends ContactBaseVO {
@Schema(description = "只读权限的用户编号数组")
private Set<Long> roUserIds;
@Schema(description = "读写权限的用户编号数组")
private Set<Long> rwUserIds;
}

View File

@ -17,4 +17,12 @@ public class CrmContactTransferReqVO {
@NotNull(message = "新负责人的用户编号不能为空")
private Long ownerUserId;
@Schema(description = "原负责人移除方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
@NotNull(message = "原负责人移除方式不能为空")
private Integer transferType;
@Schema(description = "权限类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
@NotNull(message = "权限类型不能为空")
private Integer permissionType;
}

View File

@ -38,7 +38,7 @@ public class ContractController {
@Operation(summary = "创建合同")
@PreAuthorize("@ss.hasPermission('crm:contract:create')")
public CommonResult<Long> createContract(@Valid @RequestBody ContractCreateReqVO createReqVO) {
return success(contractService.createContract(createReqVO));
return success(contractService.createContract(createReqVO, getLoginUserId()));
}
@PutMapping("/update")

View File

@ -3,11 +3,13 @@ package cn.iocoder.yudao.module.crm.convert.business;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.*;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.service.permission.bo.TransferCrmPermissionBO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import java.util.List;
import java.util.Set;
/**
* 商机 Convert
@ -29,13 +31,10 @@ public interface CrmBusinessConvert {
List<CrmBusinessExcelVO> convertList02(List<CrmBusinessDO> list);
default CrmBusinessDO convert(CrmBusinessDO business, CrmTransferBusinessReqVO reqVO, Long userId) {
Set<Long> rwUserIds = business.getRwUserIds();
rwUserIds.remove(userId);
rwUserIds.add(reqVO.getOwnerUserId()); // 读写权限加入新的负责人
// TODO @puhui999对原负责人加个类似的处理移除转化为团队成员只读读写
return new CrmBusinessDO().setId(business.getId()).setOwnerUserId(reqVO.getOwnerUserId()) // 设置新负责人
.setRwUserIds(rwUserIds);
}
@Mappings({
@Mapping(target = "userId", source = "userId"),
@Mapping(target = "crmDataId", source = "reqVO.id")
})
TransferCrmPermissionBO convert(CrmTransferBusinessReqVO reqVO, Long userId);
}

View File

@ -1,14 +1,15 @@
package cn.iocoder.yudao.module.crm.convert.contact;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.*;
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.ContactDO;
import cn.iocoder.yudao.module.crm.service.permission.bo.TransferCrmPermissionBO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import java.util.List;
import java.util.Set;
/**
* crm联系人 Convert
@ -32,13 +33,10 @@ public interface ContactConvert {
List<ContactExcelVO> convertList02(List<ContactDO> list);
// TODO @puhui999参考 CrmBusinessConvert 的修改建议
default ContactDO convert(ContactDO contact, CrmContactTransferReqVO reqVO, Long userId) {
Set<Long> rwUserIds = contact.getRwUserIds();
rwUserIds.removeIf(item -> ObjUtil.equal(item, userId)); // 移除老负责人
rwUserIds.add(reqVO.getOwnerUserId()); // 读写权限加入新的负人
return new ContactDO().setId(contact.getId()).setOwnerUserId(reqVO.getOwnerUserId()) // 设置新负责人
.setRwUserIds(rwUserIds);
}
@Mappings({
@Mapping(target = "userId", source = "userId"),
@Mapping(target = "crmDataId", source = "reqVO.id")
})
TransferCrmPermissionBO convert(CrmContactTransferReqVO reqVO, Long userId);
}

View File

@ -1,14 +1,15 @@
package cn.iocoder.yudao.module.crm.convert.contract;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.*;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.ContractDO;
import cn.iocoder.yudao.module.crm.service.permission.bo.TransferCrmPermissionBO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import java.util.List;
import java.util.Set;
/**
* 合同 Convert
@ -32,13 +33,10 @@ public interface ContractConvert {
List<ContractExcelVO> convertList02(List<ContractDO> list);
// TODO @puhui999参考 CrmBusinessConvert 的修改建议
default ContractDO convert(ContractDO contract, CrmContractTransferReqVO reqVO, Long userId) {
Set<Long> rwUserIds = contract.getRwUserIds();
rwUserIds.removeIf(item -> ObjUtil.equal(item, userId)); // 移除老负责人
rwUserIds.add(reqVO.getOwnerUserId()); // 读写权限加入新的负人
return (ContractDO) new ContractDO().setId(contract.getId()).setOwnerUserId(reqVO.getOwnerUserId()) // 设置新负责人
.setRwUserIds(rwUserIds);
}
@Mappings({
@Mapping(target = "userId", source = "userId"),
@Mapping(target = "crmDataId", source = "reqVO.id")
})
TransferCrmPermissionBO convert(CrmContractTransferReqVO reqVO, Long userId);
}

View File

@ -1,8 +1,8 @@
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.businessstatus.CrmBusinessStatusDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.businessstatustype.CrmBusinessStatusTypeDO;
import cn.iocoder.yudao.module.crm.framework.dataobject.CrmPermissionBaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@ -23,7 +23,7 @@ import java.time.LocalDateTime;
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CrmBusinessDO extends CrmPermissionBaseDO {
public class CrmBusinessDO extends BaseDO {
/**
* 主键

View File

@ -64,12 +64,6 @@ public class CrmClueDO extends BaseDO {
* 地址
*/
private String address;
/**
* 负责人的用户编号
*
* 关联 AdminUserDO id 字段
*/
private Long ownerUserId;
/**
* 最后跟进时间 TODO 添加跟进记录时更新该值
*/

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.crm.dal.dataobject.contact;
import cn.iocoder.yudao.module.crm.framework.dataobject.CrmPermissionBaseDO;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@ -21,7 +21,7 @@ import java.time.LocalDateTime;
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ContactDO extends CrmPermissionBaseDO {
public class ContactDO extends BaseDO {
/**
* 主键

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.crm.dal.dataobject.contract;
import cn.iocoder.yudao.module.crm.framework.dataobject.CrmPermissionBaseDO;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@ -21,7 +21,7 @@ import java.time.LocalDateTime;
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ContractDO extends CrmPermissionBaseDO {
public class ContractDO extends BaseDO {
/**
* 合同编号

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.crm.dal.dataobject.customer;
import cn.iocoder.yudao.module.crm.framework.dataobject.CrmPermissionBaseDO;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@ -23,7 +23,7 @@ import java.time.LocalDateTime;
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CrmCustomerDO extends CrmPermissionBaseDO {
public class CrmCustomerDO extends BaseDO {
/**
* 编号

View File

@ -36,9 +36,9 @@ public class CrmPermissionDO extends BaseDO {
*/
private Integer crmType;
/**
* 数据编号 关联 {@link CrmEnum} 对应模块 DO#id
* 数据编号 关联 {@link CrmEnum} 对应模块 DO#getId()
*/
private Integer crmDataId;
private Long crmDataId;
/**
* 负责人的用户编号 关联 AdminUser#id
*/

View File

@ -36,10 +36,7 @@ public interface CrmBusinessMapper extends BaseMapperX<CrmBusinessDO> {
.eqIfPresent(CrmBusinessDO::getDiscountPercent, reqVO.getDiscountPercent())
.eqIfPresent(CrmBusinessDO::getProductPrice, reqVO.getProductPrice())
.eqIfPresent(CrmBusinessDO::getRemark, reqVO.getRemark())
.eqIfPresent(CrmBusinessDO::getOwnerUserId, reqVO.getOwnerUserId())
.betweenIfPresent(CrmBusinessDO::getCreateTime, reqVO.getCreateTime())
.eqIfPresent(CrmBusinessDO::getRoUserIds, reqVO.getRoUserIds())
.eqIfPresent(CrmBusinessDO::getRwUserIds, reqVO.getRwUserIds())
.eqIfPresent(CrmBusinessDO::getEndStatus, reqVO.getEndStatus())
.eqIfPresent(CrmBusinessDO::getEndRemark, reqVO.getEndRemark())
.betweenIfPresent(CrmBusinessDO::getContactLastTime, reqVO.getContactLastTime())

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.crm.dal.mysql.permission;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
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;
@ -11,4 +12,10 @@ import org.apache.ibatis.annotations.Mapper;
*/
@Mapper
public interface CrmPermissionMapper extends BaseMapperX<CrmPermissionDO> {
default CrmPermissionDO selectByCrmTypeAndCrmDataId(Integer crmType, Long crmDataId) {
return selectOne(new LambdaQueryWrapperX<CrmPermissionDO>()
.eq(CrmPermissionDO::getCrmType, crmType).eq(CrmPermissionDO::getCrmDataId, crmDataId));
}
}

View File

@ -2,19 +2,11 @@ package cn.iocoder.yudao.module.crm.framework.core.aop;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.ContactDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.ContractDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission;
import cn.iocoder.yudao.module.crm.framework.enums.CrmEnum;
import cn.iocoder.yudao.module.crm.framework.enums.OperationTypeEnum;
import cn.iocoder.yudao.module.crm.framework.vo.CrmTransferBaseVO;
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
import cn.iocoder.yudao.module.crm.service.contact.ContactService;
import cn.iocoder.yudao.module.crm.service.contract.ContractService;
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
@ -22,11 +14,11 @@ import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Collection;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
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;
/**
* Crm 数据权限校验 AOP 切面
@ -38,48 +30,16 @@ import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
@Slf4j
public class CrmPermissionAspect {
/**
* 用户编号
*/
private static final ThreadLocal<Long> USER_ID = new ThreadLocal<>();
/**
* 用户类型
*/
private static final ThreadLocal<Integer> USER_TYPE = new ThreadLocal<>();
/**
* 操作数据编号
*/
private static final ThreadLocal<Long> DATA_ID = new ThreadLocal<>();
/**
* Crm 转换数据 VO 数据
*/
private static final ThreadLocal<CrmTransferBaseVO> CRM_TRANSFER_VO = new ThreadLocal<>();
@Resource
private CrmBusinessService crmBusinessService;
@Resource
private ContactService contactService;
@Resource
private ContractService contractService;
@Resource
private CrmCustomerService crmCustomerService;
private CrmPermissionService crmPermissionService;
public static void setCrmTransferInfo(Long userId, Integer userType, Object crmTransferBaseVO) {
USER_ID.set(userId);
USER_TYPE.set(userType);
CRM_TRANSFER_VO.set((CrmTransferBaseVO) crmTransferBaseVO);
}
public static void setCrmTransferInfo(Long userId, Integer userType) {
USER_ID.set(userId);
USER_TYPE.set(userType);
}
private static void clear() {
USER_ID.remove();
USER_TYPE.remove();
DATA_ID.remove();
CRM_TRANSFER_VO.remove();
/**
* 获得用户编号
*
* @return 用户编号
*/
private static Long getUserId() {
return WebFrameworkUtils.getLoginUserId();
}
@Before("@annotation(crmPermission)")
@ -87,78 +47,46 @@ public class CrmPermissionAspect {
try {
Integer crmType = crmPermission.crmType().getType();
Integer operationType = crmPermission.operationType().getType();
Long id = DATA_ID.get();// 获取操作数据的编号
KeyValue<Collection<Long>, Collection<Long>> keyValue = new KeyValue<>(); // 数据权限 key 只读value 读写
// 客户
if (ObjUtil.equal(crmType, CrmEnum.CRM_CUSTOMER.getType())) {
CrmCustomerDO customer = crmCustomerService.getCustomer(id);
if (customer == null) {
throw exception(CUSTOMER_NOT_EXISTS);
}
// 如果是自己则直接过
if (ObjUtil.equal(customer.getOwnerUserId(), USER_ID.get())) {
return;
}
new KeyValue<>(customer.getRoUserIds(), customer.getRwUserIds());
Long id = (Long) joinPoint.getArgs()[0];// 获取操作数据的编号
// 1. 获取数据权限
CrmPermissionDO permission = crmPermissionService.getCrmPermissionByCrmTypeAndCrmDataId(crmType, id);
if (permission == null) {
// 不存在说明数据也不存在
throw exception(CRM_PERMISSION_MODEL_NOT_EXISTS, crmPermission.crmType().getName());
}
// 联系人
if (ObjUtil.equal(crmType, CrmEnum.CRM_CONTACTS.getType())) {
ContactDO contact = contactService.getContact(id);
if (contact == null) {
throw exception(CONTACT_NOT_EXISTS);
}
// 如果是自己则直接过
if (ObjUtil.equal(contact.getOwnerUserId(), USER_ID.get())) {
return;
}
new KeyValue<>(contact.getRoUserIds(), contact.getRwUserIds());
// 1.2. 校验是否为公海数据
if (permission.getOwnerUserId() == null) {
return;
}
// 商机
if (ObjUtil.equal(crmType, CrmEnum.CRM_BUSINESS.getType())) {
CrmBusinessDO business = crmBusinessService.getBusiness(id);
if (business == null) {
throw exception(BUSINESS_NOT_EXISTS);
}
// 如果是自己则直接过
if (ObjUtil.equal(business.getOwnerUserId(), USER_ID.get())) {
return;
}
new KeyValue<>(business.getRoUserIds(), business.getRwUserIds());
// 1.3. 校验当前负责人是不是自己
if (ObjUtil.equal(permission.getOwnerUserId(), getUserId())) {
return;
}
// 合同
if (ObjUtil.equal(crmType, CrmEnum.CRM_CONTRACT.getType())) {
ContractDO contract = contractService.getContract(id);
if (contract == null) {
throw exception(CONTRACT_NOT_EXISTS);
}
// 如果是自己则直接过
if (ObjUtil.equal(contract.getOwnerUserId(), USER_ID.get())) {
return;
}
new KeyValue<>(contract.getRoUserIds(), contract.getRwUserIds());
}
// 1. 校验是否有读权限
// 1.4 TODO 校验是否为超级管理员
// 2. 校验是否有读权限
if (OperationTypeEnum.isRead(operationType)) {
// 校验该数据当前用户是否可读
boolean isRead = CollUtil.contains(keyValue.getKey(), item -> ObjUtil.equal(id, USER_ID.get()))
|| CollUtil.contains(keyValue.getValue(), item -> ObjUtil.equal(id, USER_ID.get()));
boolean isRead = CollUtil.contains(permission.getRoUserIds(), item -> ObjUtil.equal(item, getUserId()))
|| CollUtil.contains(permission.getRwUserIds(), item -> ObjUtil.equal(item, getUserId()));
if (isRead) {
return;
}
throw exception(CONTRACT_NOT_EXISTS);
}
// 2. 校验是否有编辑权限
// 3. 校验是否有编辑权限
if (OperationTypeEnum.isEdit(operationType)) {
// 校验该数据当前用户是否可读写
if (CollUtil.contains(keyValue.getValue(), item -> ObjUtil.equal(id, USER_ID.get()))) {
if (CollUtil.contains(permission.getRwUserIds(), item -> ObjUtil.equal(item, getUserId()))) {
return;
}
throw exception(CONTRACT_NOT_EXISTS);
}
// 4. 没通过结束报错 {}操作失败原因没有权限
throw exception(CRM_PERMISSION_DENIED, crmPermission.crmType().getName());
} catch (Exception ex) {
log.error("[doBefore][crmPermission({}) 数据校验错误]", toJsonString(crmPermission), ex);
} finally {
clear();
}
}

View File

@ -1,37 +0,0 @@
package cn.iocoder.yudao.module.crm.framework.dataobject;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.framework.mybatis.core.type.JsonLongSetTypeHandler;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.util.Set;
/**
* crm 数据权限基础实体对象
*
* @author HUIHUI
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CrmPermissionBaseDO extends BaseDO {
/**
* 负责人的用户编号 关联 AdminUser#id
*/
private Long ownerUserId;
/**
* 只读权限的用户编号数组
*/
@TableField(typeHandler = JsonLongSetTypeHandler.class)
private Set<Long> roUserIds;
/**
* 读写权限的用户编号数组
*/
@TableField(typeHandler = JsonLongSetTypeHandler.class)
private Set<Long> rwUserIds;
}

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.crm.framework.enums;
import cn.hutool.core.util.ObjUtil;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@ -15,12 +16,8 @@ public enum CrmEnum {
CRM_LEADS(1, "线索"),
CRM_CUSTOMER(2, "客户"),
CRM_CONTACTS(3, "联系人"),
CRM_PRODUCT(4, "产品"),
CRM_BUSINESS(5, "商机"),
CRM_CONTRACT(6, "合同"),
CRM_RECEIVABLES(7, "回款"),
CRM_RECEIVABLES_PLAN(8, "回款计划"),
CRM_CUSTOMER_POOL(9, "客户公海");
CRM_CONTRACT(6, "合同");
/**
* 类型
@ -31,4 +28,13 @@ public enum CrmEnum {
*/
private final String name;
public static String getNameByType(Integer type) {
for (CrmEnum crmEnum : CrmEnum.values()) {
if (ObjUtil.equal(crmEnum.type, type)) {
return crmEnum.name;
}
}
return "";
}
}

View File

@ -15,8 +15,7 @@ public enum OperationTypeEnum {
DELETE(1, "删除"),
UPDATE(2, "修改"),
READ(3, "查询"),
TRANSFER(4, "转移");
READ(3, "查询");
/**
* 类型
@ -33,7 +32,7 @@ public enum OperationTypeEnum {
}
public static boolean isEdit(Integer type) {
return ObjUtil.equal(type, UPDATE.getType()) || ObjUtil.equal(type, DELETE.getType()) || ObjUtil.equal(type, TRANSFER.getType());
return ObjUtil.equal(type, UPDATE.getType()) || ObjUtil.equal(type, DELETE.getType());
}
}

View File

@ -1,49 +0,0 @@
package cn.iocoder.yudao.module.crm.framework.utils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.module.crm.framework.core.aop.CrmPermissionAspect;
import java.util.Collection;
/**
* 数据读写权限校验工具类
*
* @author HUIHUI
*/
public class CrmPermissionUtils {
// TODO @puhui999负责人是单独的字段哈
// TODO @puhui999额外校验如果是管理员可以查看所有看着要做成有状态的了可能要搞个 CrmPermissionService
/**
* 判断当前数据对用户来说是否是只读的
*
* @param roUserIds 当前操作数据的只读权限的用户编号数组
* @param userId 当前操作数据的用户编号
* @return boolean /
*/
public static boolean isReadOnly(Collection<Long> roUserIds, Long userId) {
return CollUtil.contains(roUserIds, id -> ObjUtil.equal(id, userId));
}
/**
* 判断当前数据对用户来说是否是可读写的
*
* @param rwUserIds 当前操作数据的读写权限的用户编号数组
* @param userId 当前操作数据的用户编号
* @return boolean /
*/
public static boolean isReadAndWrite(Collection<Long> rwUserIds, Long userId) {
return CollUtil.contains(rwUserIds, id -> ObjUtil.equal(id, userId));
}
public static void setCrmTransferInfo(Long userId, Integer userType, Object crmTransferBaseVO) {
CrmPermissionAspect.setCrmTransferInfo(userId, userType, crmTransferBaseVO);
}
public static void setCrmTransferInfo(Long userId, Integer userType) {
CrmPermissionAspect.setCrmTransferInfo(userId, userType);
}
}

View File

@ -1,32 +0,0 @@
package cn.iocoder.yudao.module.crm.framework.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
* Crm 数据转移 Base VO提供给转移的子 VO 使用
*
* @author HUIHUI
*/
@Data
public class CrmTransferBaseVO {
@Schema(description = "商机编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
@NotNull(message = "联系人编号不能为空")
private Long id;
@Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
@NotNull(message = "新负责人的用户编号不能为空")
private Long ownerUserId;
@Schema(description = "原负责人移除方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
@NotNull(message = "原负责人移除方式不能为空")
private Integer transferType;
@Schema(description = "权限类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
@NotNull(message = "权限类型不能为空")
private Integer permissionType;
}

View File

@ -19,9 +19,10 @@ public interface CrmBusinessService {
* 创建商机
*
* @param createReqVO 创建信息
* @param userId 用户编号
* @return 编号
*/
Long createBusiness(@Valid CrmBusinessCreateReqVO createReqVO);
Long createBusiness(@Valid CrmBusinessCreateReqVO createReqVO, Long userId);
/**
* 更新商机
@ -76,5 +77,5 @@ public interface CrmBusinessService {
* @param userId 用户编号
*/
void businessTransfer(CrmTransferBusinessReqVO reqVO, Long userId);
}

View File

@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.crm.service.business;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.*;
import cn.iocoder.yudao.module.crm.convert.business.CrmBusinessConvert;
@ -11,9 +10,11 @@ import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessMapper;
import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission;
import cn.iocoder.yudao.module.crm.framework.enums.CrmEnum;
import cn.iocoder.yudao.module.crm.framework.enums.OperationTypeEnum;
import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateBO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
@ -21,7 +22,7 @@ import java.util.Collection;
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.ErrorCodeConstants.BUSINESS_NOT_EXISTS;
/**
* 商机 Service 实现类
@ -37,17 +38,27 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
@Resource
private AdminUserApi adminUserApi;
@Resource
private CrmPermissionService crmPermissionService;
@Override
public Long createBusiness(CrmBusinessCreateReqVO createReqVO) {
@Transactional(rollbackFor = Exception.class)
public Long createBusiness(CrmBusinessCreateReqVO createReqVO, Long userId) {
// 插入
CrmBusinessDO business = CrmBusinessConvert.INSTANCE.convert(createReqVO);
businessMapper.insert(business);
// 创建数据权限
crmPermissionService.createCrmPermission(new CrmPermissionCreateBO().setCrmType(CrmEnum.CRM_BUSINESS.getType())
.setCrmDataId(business.getId()).setOwnerUserId(userId)); // 设置当前操作的人为负责人
// 返回
return business.getId();
}
@Override
@Transactional(rollbackFor = Exception.class)
@CrmPermission(crmType = CrmEnum.CRM_BUSINESS, operationType = OperationTypeEnum.UPDATE)
public void updateBusiness(CrmBusinessUpdateReqVO updateReqVO) {
// 校验存在
validateBusinessExists(updateReqVO.getId());
@ -57,6 +68,8 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
}
@Override
@Transactional(rollbackFor = Exception.class)
@CrmPermission(crmType = CrmEnum.CRM_BUSINESS, operationType = OperationTypeEnum.DELETE)
public void deleteBusiness(Long id) {
// 校验存在
validateBusinessExists(id);
@ -97,25 +110,15 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
}
@Override
@CrmPermission(crmType = CrmEnum.CRM_BUSINESS, operationType = OperationTypeEnum.TRANSFER)
@Transactional(rollbackFor = Exception.class)
public void businessTransfer(CrmTransferBusinessReqVO reqVO, Long userId) {
// 1.1 校验商机是否存在
CrmBusinessDO business = getBusiness(reqVO.getId());
// 1.3 校验转移对象是否已经是该负责人
if (ObjUtil.equal(business.getOwnerUserId(), reqVO.getOwnerUserId())) {
throw exception(BUSINESS_TRANSFER_FAIL_OWNER_USER_EXISTS);
}
// 1.4 校验新负责人是否存在
AdminUserRespDTO user = adminUserApi.getUser(reqVO.getOwnerUserId());
if (user == null) {
throw exception(BUSINESS_TRANSFER_FAIL_OWNER_USER_NOT_EXISTS);
}
// 1 校验商机是否存在
validateBusinessExists(reqVO.getId());
// 2. 更新新的负责人
CrmBusinessDO updateBusiness = CrmBusinessConvert.INSTANCE.convert(business, reqVO, userId);
businessMapper.updateById(updateBusiness);
// 2. 数据权限转移
crmPermissionService.transferCrmPermission(
CrmBusinessConvert.INSTANCE.convert(reqVO, userId).setCrmType(CrmEnum.CRM_BUSINESS.getType()));
// 3. TODO 记录商机转移日志
}
}

View File

@ -19,9 +19,10 @@ public interface ContactService {
* 创建crm联系人
*
* @param createReqVO 创建信息
* @param userId 用户编号
* @return 编号
*/
Long createContact(@Valid ContactCreateReqVO createReqVO);
Long createContact(@Valid ContactCreateReqVO createReqVO, Long userId);
/**
* 更新crm联系人
@ -76,5 +77,5 @@ public interface ContactService {
* @param userId 用户编号
*/
void contactTransfer(CrmContactTransferReqVO reqVO, Long userId);
}

View File

@ -7,9 +7,13 @@ import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.*;
import cn.iocoder.yudao.module.crm.convert.contact.ContactConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.ContactDO;
import cn.iocoder.yudao.module.crm.dal.mysql.contact.ContactMapper;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission;
import cn.iocoder.yudao.module.crm.framework.enums.CrmEnum;
import cn.iocoder.yudao.module.crm.framework.enums.OperationTypeEnum;
import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateBO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
@ -17,8 +21,7 @@ import java.util.Collection;
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.framework.utils.CrmPermissionUtils.isReadAndWrite;
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CONTACT_NOT_EXISTS;
/**
* crm联系人 Service 实现类
@ -33,19 +36,26 @@ public class ContactServiceImpl implements ContactService {
private ContactMapper contactMapper;
@Resource
private AdminUserApi adminUserApi;
private CrmPermissionService crmPermissionService;
@Override
public Long createContact(ContactCreateReqVO createReqVO) {
public Long createContact(ContactCreateReqVO createReqVO, Long userId) {
// TODO @customerId需要校验存在
// 插入
ContactDO contact = ContactConvert.INSTANCE.convert(createReqVO);
contactMapper.insert(contact);
// 创建数据权限
crmPermissionService.createCrmPermission(new CrmPermissionCreateBO().setCrmType(CrmEnum.CRM_BUSINESS.getType())
.setCrmDataId(contact.getId()).setOwnerUserId(userId)); // 设置当前操作的人为负责人
// 返回
return contact.getId();
}
@Override
@Transactional(rollbackFor = Exception.class)
@CrmPermission(crmType = CrmEnum.CRM_CONTACTS, operationType = OperationTypeEnum.UPDATE)
public void updateContact(ContactUpdateReqVO updateReqVO) {
// 校验存在
validateContactExists(updateReqVO.getId());
@ -57,6 +67,8 @@ public class ContactServiceImpl implements ContactService {
}
@Override
@Transactional(rollbackFor = Exception.class)
@CrmPermission(crmType = CrmEnum.CRM_CONTACTS, operationType = OperationTypeEnum.DELETE)
public void deleteContact(Long id) {
// 校验存在
validateContactExists(id);
@ -73,6 +85,7 @@ public class ContactServiceImpl implements ContactService {
}
@Override
@CrmPermission(crmType = CrmEnum.CRM_CONTACTS, operationType = OperationTypeEnum.READ)
public ContactDO getContact(Long id) {
return contactMapper.selectById(id);
}
@ -95,26 +108,14 @@ public class ContactServiceImpl implements ContactService {
return contactMapper.selectList(exportReqVO);
}
// TODO @puhui999参考 CrmBusinessServiceImpl 修改建议
@Override
public void contactTransfer(CrmContactTransferReqVO reqVO, Long userId) {
// 1. 校验联系人是否存在
ContactDO contact = validateContactExists(reqVO.getId());
// 1.2. 校验用户是否拥有读写权限
if (!isReadAndWrite(contact.getRwUserIds(), userId)) {
throw exception(CONTACT_TRANSFER_FAIL_PERMISSION_DENIED);
}
// 2. 校验新负责人是否存在
AdminUserRespDTO user = adminUserApi.getUser(reqVO.getOwnerUserId());
if (user == null) {
throw exception(CONTACT_TRANSFER_FAIL_OWNER_USER_NOT_EXISTS);
}
// 1 校验联系人是否存在
validateContactExists(reqVO.getId());
// 3. 更新新的负责人
ContactDO updateContact = ContactConvert.INSTANCE.convert(contact, reqVO, userId);
contactMapper.updateById(updateContact);
// 4. TODO 记录联系人转移日志
// 2. 数据权限转移
crmPermissionService.transferCrmPermission(
ContactConvert.INSTANCE.convert(reqVO, userId).setCrmType(CrmEnum.CRM_CONTACTS.getType()));
}

View File

@ -19,9 +19,10 @@ public interface ContractService {
* 创建合同
*
* @param createReqVO 创建信息
* @param userId 用户编号
* @return 编号
*/
Long createContract(@Valid ContractCreateReqVO createReqVO);
Long createContract(@Valid ContractCreateReqVO createReqVO, Long userId);
/**
* 更新合同

View File

@ -10,9 +10,10 @@ import cn.iocoder.yudao.module.crm.dal.mysql.contract.ContractMapper;
import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission;
import cn.iocoder.yudao.module.crm.framework.enums.CrmEnum;
import cn.iocoder.yudao.module.crm.framework.enums.OperationTypeEnum;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateBO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
@ -20,8 +21,7 @@ import java.util.Collection;
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.framework.utils.CrmPermissionUtils.isReadAndWrite;
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CONTRACT_NOT_EXISTS;
/**
* 合同 Service 实现类
@ -36,18 +36,25 @@ public class ContractServiceImpl implements ContractService {
private ContractMapper contractMapper;
@Resource
private AdminUserApi adminUserApi;
private CrmPermissionService crmPermissionService;
@Override
public Long createContract(ContractCreateReqVO createReqVO) {
public Long createContract(ContractCreateReqVO createReqVO, Long userId) {
// 插入
ContractDO contract = ContractConvert.INSTANCE.convert(createReqVO);
contractMapper.insert(contract);
// 创建数据权限
crmPermissionService.createCrmPermission(new CrmPermissionCreateBO().setCrmType(CrmEnum.CRM_CONTRACT.getType())
.setCrmDataId(contract.getId()).setOwnerUserId(userId)); // 设置当前操作的人为负责人
// 返回
return contract.getId();
}
@Override
@Transactional(rollbackFor = Exception.class)
@CrmPermission(crmType = CrmEnum.CRM_CONTRACT, operationType = OperationTypeEnum.DELETE)
public void updateContract(ContractUpdateReqVO updateReqVO) {
// 校验存在
validateContractExists(updateReqVO.getId());
@ -57,6 +64,8 @@ public class ContractServiceImpl implements ContractService {
}
@Override
@Transactional(rollbackFor = Exception.class)
@CrmPermission(crmType = CrmEnum.CRM_CONTRACT, operationType = OperationTypeEnum.DELETE)
public void deleteContract(Long id) {
// 校验存在
validateContractExists(id);
@ -73,6 +82,7 @@ public class ContractServiceImpl implements ContractService {
}
@Override
@CrmPermission(crmType = CrmEnum.CRM_CONTRACT, operationType = OperationTypeEnum.READ)
public ContractDO getContract(Long id) {
return contractMapper.selectById(id);
}
@ -95,27 +105,15 @@ public class ContractServiceImpl implements ContractService {
return contractMapper.selectList(exportReqVO);
}
// TODO @puhui999参考 CrmBusinessServiceImpl 修改建议
@Override
@CrmPermission(crmType = CrmEnum.CRM_CONTRACT, operationType = OperationTypeEnum.TRANSFER)
@Transactional(rollbackFor = Exception.class)
public void contractTransfer(CrmContractTransferReqVO reqVO, Long userId) {
// 1. 校验合同是否存在
ContractDO contract = validateContractExists(reqVO.getId());
// 1.2. 校验用户是否拥有读写权限
if (!isReadAndWrite(contract.getRwUserIds(), userId)) {
throw exception(CONTRACT_TRANSFER_FAIL_PERMISSION_DENIED);
}
// 2. 校验新负责人是否存在
AdminUserRespDTO user = adminUserApi.getUser(reqVO.getOwnerUserId());
if (user == null) {
throw exception(CONTRACT_TRANSFER_FAIL_OWNER_USER_NOT_EXISTS);
}
// 1 校验合同是否存在
validateContractExists(reqVO.getId());
// 3. 更新新的负责人
ContractDO updateContract = ContractConvert.INSTANCE.convert(contract, reqVO, userId);
contractMapper.updateById(updateContract);
// 4. TODO 记录合同转移日志
// 2. 数据权限转移
crmPermissionService.transferCrmPermission(
ContractConvert.INSTANCE.convert(reqVO, userId).setCrmType(CrmEnum.CRM_CONTRACT.getType()));
}

View File

@ -2,8 +2,10 @@ package cn.iocoder.yudao.module.crm.service.permission;
import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
import cn.iocoder.yudao.module.crm.framework.enums.CrmEnum;
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateBO;
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionUpdateBO;
import cn.iocoder.yudao.module.crm.service.permission.bo.TransferCrmPermissionBO;
import javax.validation.Valid;
@ -39,9 +41,17 @@ public interface CrmPermissionService {
/**
* 获得数据权限
*
* @param id 编号
* @param crmType 数据类型 关联 {@link CrmEnum}
* @param crmDataId 数据编号 关联 {@link CrmEnum} 对应模块 DO#getId()
* @return 数据权限
*/
CrmPermissionDO getCrmPermission(Long id);
CrmPermissionDO getCrmPermissionByCrmTypeAndCrmDataId(Integer crmType, Long crmDataId);
/**
* 数据权限转移
*
* @param transferCrmPermissionBO 数据权限转移请求
*/
void transferCrmPermission(@Valid TransferCrmPermissionBO transferCrmPermissionBO);
}

View File

@ -1,18 +1,27 @@
package cn.iocoder.yudao.module.crm.service.permission;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.module.crm.convert.permission.CrmPermissionConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
import cn.iocoder.yudao.module.crm.dal.mysql.permission.CrmPermissionMapper;
import cn.iocoder.yudao.module.crm.enums.common.PermissionTypeEnum;
import cn.iocoder.yudao.module.crm.enums.common.TransferTypeEnum;
import cn.iocoder.yudao.module.crm.framework.enums.CrmEnum;
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateBO;
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionUpdateBO;
import cn.iocoder.yudao.module.crm.service.permission.bo.TransferCrmPermissionBO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CRM_PERMISSION_NOT_EXISTS;
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
/**
* crm 数据权限 Service 接口实现类
@ -26,6 +35,8 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
@Resource
private CrmPermissionMapper crmPermissionMapper;
@Resource
private AdminUserApi adminUserApi;
@Override
@Transactional(rollbackFor = Exception.class)
@ -59,8 +70,52 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
}
@Override
public CrmPermissionDO getCrmPermission(Long id) {
return crmPermissionMapper.selectById(id);
public CrmPermissionDO getCrmPermissionByCrmTypeAndCrmDataId(Integer crmType, Long crmDataId) {
return crmPermissionMapper.selectByCrmTypeAndCrmDataId(crmType, crmDataId);
}
@Override
public void transferCrmPermission(TransferCrmPermissionBO transferCrmPermissionBO) {
// 1 校验商机是否存在
CrmPermissionDO permission = getCrmPermissionByCrmTypeAndCrmDataId(transferCrmPermissionBO.getCrmType(),
transferCrmPermissionBO.getCrmDataId());
String crmName = CrmEnum.getNameByType(transferCrmPermissionBO.getCrmType());
if (permission == null) {
throw exception(CRM_PERMISSION_MODEL_NOT_EXISTS, crmName);
}
// 1.2 校验转移对象是否已经是该负责人
if (ObjUtil.equal(permission.getOwnerUserId(), permission.getOwnerUserId())) {
throw exception(CRM_PERMISSION_MODEL_TRANSFER_FAIL_OWNER_USER_NOT_EXISTS, crmName);
}
// 1.3 校验新负责人是否存在
AdminUserRespDTO user = adminUserApi.getUser(permission.getOwnerUserId());
if (user == null) {
throw exception(CRM_PERMISSION_MODEL_TRANSFER_FAIL_OWNER_USER_EXISTS, crmName);
}
// TODO 校验是否为超级管理员 || 1.4
// 1.4 校验是否有写权限
if (!CollUtil.contains(permission.getRwUserIds(), id -> ObjUtil.equal(id, transferCrmPermissionBO.getUserId()))) {
throw exception(CRM_PERMISSION_DENIED, crmName);
}
// 2 权限转移
CrmPermissionDO updateCrmPermission = new CrmPermissionDO().setId(permission.getId())
.setOwnerUserId(transferCrmPermissionBO.getOwnerUserId());
if (ObjUtil.equal(TransferTypeEnum.TEAM.getType(), transferCrmPermissionBO.getTransferType())) {
if (ObjUtil.equal(PermissionTypeEnum.READONLY.getType(), transferCrmPermissionBO.getPermissionType())) {
Set<Long> roUserIds = permission.getRoUserIds();
roUserIds.add(permission.getOwnerUserId()); // 老负责人加入团队有只读权限
updateCrmPermission.setRoUserIds(roUserIds);
}
if (ObjUtil.equal(PermissionTypeEnum.READ_AND_WRITE.getType(), transferCrmPermissionBO.getPermissionType())) {
Set<Long> rwUserIds = permission.getRwUserIds();
rwUserIds.add(permission.getOwnerUserId()); // 老负责人加入团队有读写权限
updateCrmPermission.setRoUserIds(rwUserIds);
}
}
crmPermissionMapper.updateById(updateCrmPermission);
// 3. TODO 记录机转移日志
}
}

View File

@ -23,7 +23,7 @@ public class CrmPermissionCreateBO {
* 数据编号 关联 {@link CrmEnum} 对应模块 DO#getId()
*/
@NotNull(message = "Crm 数据编号不能为空")
private Integer crmDataId;
private Long crmDataId;
/**
* 负责人的用户编号 关联 AdminUser#id, null 则为公海数据
*/

View File

@ -30,7 +30,7 @@ public class CrmPermissionUpdateBO {
* 数据编号 关联 {@link CrmEnum} 对应模块 DO#getId()
*/
@NotNull(message = "Crm 数据编号不能为空")
private Integer crmDataId;
private Long crmDataId;
/**
* 负责人的用户编号 关联 AdminUser#id, null 则为公海数据
*/

View File

@ -0,0 +1,48 @@
package cn.iocoder.yudao.module.crm.service.permission.bo;
import cn.iocoder.yudao.module.crm.enums.common.PermissionTypeEnum;
import cn.iocoder.yudao.module.crm.enums.common.TransferTypeEnum;
import cn.iocoder.yudao.module.crm.framework.enums.CrmEnum;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
* 数据权限转移 BO
*
* @author HUIHUI
*/
@Data
public class TransferCrmPermissionBO {
@NotNull(message = "用户编号不能为空")
private Long userId;
/**
* Crm 类型 关联 {@link CrmEnum}
*/
@NotNull(message = "Crm 类型不能为空")
private Integer crmType;
/**
* 数据编号 关联 {@link CrmEnum} 对应模块 DO#getId()
*/
@NotNull(message = "Crm 数据编号不能为空")
private Long crmDataId;
@NotNull(message = "新负责人的用户编号不能为空")
private Long ownerUserId;
/**
* 原负责人移除方式, 关联 {@link TransferTypeEnum}
*/
@NotNull(message = "原负责人移除方式不能为空")
private Integer transferType;
/**
* 权限类型, 关联 {@link PermissionTypeEnum}
*/
@NotNull(message = "权限类型不能为空")
private Integer permissionType;
}

View File

@ -15,6 +15,7 @@ import java.util.List;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
@ -42,7 +43,7 @@ public class CrmBusinessServiceImplTest extends BaseDbUnitTest {
CrmBusinessCreateReqVO reqVO = randomPojo(CrmBusinessCreateReqVO.class);
// 调用
Long businessId = businessService.createBusiness(reqVO);
Long businessId = businessService.createBusiness(reqVO, getLoginUserId());
// 断言
assertNotNull(businessId);
// 校验记录的属性是否正确

View File

@ -17,6 +17,7 @@ import java.util.List;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
@ -44,7 +45,7 @@ public class ContractServiceImplTest extends BaseDbUnitTest {
ContractCreateReqVO reqVO = randomPojo(ContractCreateReqVO.class);
// 调用
Long contractId = contractService.createContract(reqVO);
Long contractId = contractService.createContract(reqVO, getLoginUserId());
// 断言
assertNotNull(contractId);
// 校验记录的属性是否正确