CRM:code review 客户权限的实现
This commit is contained in:
parent
e2ec426a9f
commit
89fc83c419
@ -11,7 +11,7 @@
|
||||
Target Server Version : 80200 (8.2.0)
|
||||
File Encoding : 65001
|
||||
|
||||
Date: 01/03/2024 19:39:45
|
||||
Date: 05/03/2024 23:36:35
|
||||
*/
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
|
@ -627,11 +627,11 @@
|
||||
<artifactId>ureport2-console</artifactId>
|
||||
<version>${ureport2.version}</version>
|
||||
<exclusions>
|
||||
<exclusion><!-- 包冲突 积木报表 导出Excel报错 -->
|
||||
<exclusion><!-- 包冲突:解决积木报表导出 Excel 报错 -->
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi</artifactId>
|
||||
</exclusion>
|
||||
<exclusion><!-- 包冲突 积木报表 导出Excel报错 -->
|
||||
<exclusion><!-- 包冲突:解决积木报表导出 Excel 报错 -->
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
</exclusion>
|
||||
|
@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.common.util.collection;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Multimap;
|
||||
@ -40,6 +41,7 @@ public class MapUtils {
|
||||
|
||||
/**
|
||||
* 从哈希表查找到 key 对应的 value,然后进一步处理
|
||||
* key 为 null 时, 不处理
|
||||
* 注意,如果查找到的 value 为 null 时,不进行处理
|
||||
*
|
||||
* @param map 哈希表
|
||||
@ -47,7 +49,7 @@ public class MapUtils {
|
||||
* @param consumer 进一步处理的逻辑
|
||||
*/
|
||||
public static <K, V> void findAndThen(Map<K, V> map, K key, Consumer<V> consumer) {
|
||||
if (CollUtil.isEmpty(map)) {
|
||||
if (ObjUtil.isNull(key) || CollUtil.isEmpty(map)) {
|
||||
return;
|
||||
}
|
||||
V value = map.get(key);
|
||||
|
@ -13,8 +13,6 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
|
||||
/**
|
||||
* 字典工具类
|
||||
*
|
||||
@ -27,6 +25,7 @@ public class DictFrameworkUtils {
|
||||
|
||||
private static final DictDataRespDTO DICT_DATA_NULL = new DictDataRespDTO();
|
||||
|
||||
// TODO @puhui999:GET_DICT_DATA_CACHE、GET_DICT_DATA_LIST_CACHE、PARSE_DICT_DATA_CACHE 这 3 个缓存是有点重叠,可以思考下,有没可能减少 1 个。微信讨论好私聊,再具体改哈
|
||||
/**
|
||||
* 针对 {@link #getDictDataLabel(String, String)} 的缓存
|
||||
*/
|
||||
|
@ -3,7 +3,9 @@ package cn.iocoder.yudao.framework.excel.core.annotations;
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 给列添加下拉选择数据
|
||||
* 给 Excel 列添加下拉选择数据
|
||||
*
|
||||
* 其中 {@link #dictType()} 和 {@link #functionName()} 二选一
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
|
@ -1,8 +1,9 @@
|
||||
package cn.iocoder.yudao.framework.excel.core.handler;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.hutool.poi.excel.ExcelUtil;
|
||||
@ -59,7 +60,6 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
|
||||
}
|
||||
|
||||
// 解析下拉数据
|
||||
// TODO @puhui999:感觉可以 head 循环 field,如果有 ExcelColumnSelect 则进行处理;而 ExcelProperty 可能是非必须的。回答:主要是用于定位到列索引
|
||||
Map<String, Field> excelPropertyFields = getFieldsWithAnnotation(head, ExcelProperty.class);
|
||||
Map<String, Field> excelColumnSelectFields = getFieldsWithAnnotation(head, ExcelColumnSelect.class);
|
||||
int colIndex = 0;
|
||||
@ -71,39 +71,47 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
|
||||
if (index != -1) {
|
||||
colIndex = index;
|
||||
}
|
||||
getSelectDataList(colIndex, field);
|
||||
buildSelectDataList(colIndex, field);
|
||||
}
|
||||
colIndex++;
|
||||
}
|
||||
// TODO @puhui999:感觉可以 head 循环 field,如果有 ExcelColumnSelect 则进行处理;而 ExcelProperty 可能是非必须的。回答:主要是用于定位到列索引;补充:可以看看下面这样写?
|
||||
// for (Field field : head.getDeclaredFields()) {
|
||||
// if (field.isAnnotationPresent(ExcelColumnSelect.class)) {
|
||||
// ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
|
||||
// if (excelProperty != null) {
|
||||
// colIndex = excelProperty.index();
|
||||
// }
|
||||
// getSelectDataList(colIndex, field);
|
||||
// }
|
||||
// colIndex++;
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得下拉数据
|
||||
* 获得下拉数据,并添加到 {@link #selectMap} 中
|
||||
*
|
||||
* @param colIndex 列索引
|
||||
* @param field 字段
|
||||
*/
|
||||
private void getSelectDataList(int colIndex, Field field) {
|
||||
// 获得下拉注解信息
|
||||
private void buildSelectDataList(int colIndex, Field field) {
|
||||
ExcelColumnSelect columnSelect = field.getAnnotation(ExcelColumnSelect.class);
|
||||
String dictType = columnSelect.dictType();
|
||||
String functionName = columnSelect.functionName();
|
||||
Assert.isTrue(ObjectUtil.isNotEmpty(dictType) || ObjectUtil.isNotEmpty(functionName),
|
||||
"Field({}) 的 @ExcelColumnSelect 注解,dictType 和 functionName 不能同时为空", field.getName());
|
||||
|
||||
// 情况一:使用 dictType 获得下拉数据
|
||||
if (StrUtil.isNotEmpty(dictType)) { // 情况一: 字典数据 (默认)
|
||||
selectMap.put(colIndex, DictFrameworkUtils.getDictDataLabelList(dictType));
|
||||
return;
|
||||
}
|
||||
String functionName = columnSelect.functionName();
|
||||
if (StrUtil.isEmpty(functionName)) { // 情况二: 获取自定义数据
|
||||
log.warn("[getSelectDataList]解析下拉数据失败,参数信息 dictType[{}] functionName[{}]", dictType, functionName);
|
||||
return;
|
||||
}
|
||||
// 获得所有的下拉数据源获取方法
|
||||
|
||||
// 情况二:使用 functionName 获得下拉数据
|
||||
Map<String, ExcelColumnSelectFunction> functionMap = SpringUtil.getApplicationContext().getBeansOfType(ExcelColumnSelectFunction.class);
|
||||
functionMap.values().forEach(func -> {
|
||||
if (ObjUtil.notEqual(func.getName(), functionName)) {
|
||||
return;
|
||||
}
|
||||
selectMap.put(colIndex, func.getOptions());
|
||||
});
|
||||
ExcelColumnSelectFunction function = CollUtil.findOne(functionMap.values(), item -> item.getName().equals(functionName));
|
||||
Assert.notNull(function, "未找到对应的 function({})", functionName);
|
||||
selectMap.put(colIndex, function.getOptions());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -121,7 +129,7 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
|
||||
Sheet dictSheet = workbook.createSheet(DICT_SHEET_NAME);
|
||||
for (KeyValue<Integer, List<String>> keyValue : keyValues) {
|
||||
int rowLength = keyValue.getValue().size();
|
||||
// 2.1 设置字典 sheet 页的值 每一列一个字典项
|
||||
// 2.1 设置字典 sheet 页的值,每一列一个字典项
|
||||
for (int i = 0; i < rowLength; i++) {
|
||||
Row row = dictSheet.getRow(i);
|
||||
if (row == null) {
|
||||
|
@ -13,6 +13,7 @@ import lombok.NoArgsConstructor;
|
||||
@AllArgsConstructor
|
||||
public class CrmBusinessTransferReqVO {
|
||||
|
||||
// TODO @puhui999:这里最好还是用 id 哈,主要还是在 Business 业务里
|
||||
@Schema(description = "商机编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
|
||||
@NotNull(message = "商机编号不能为空")
|
||||
private Long bizId;
|
||||
|
@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
|
||||
import cn.iocoder.yudao.framework.excel.core.annotations.ExcelColumnSelect;
|
||||
import cn.iocoder.yudao.framework.excel.core.convert.AreaConvert;
|
||||
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
|
||||
import cn.iocoder.yudao.module.crm.framework.excel.core.AreaExcelColumnSelectFunctionImpl;
|
||||
import cn.iocoder.yudao.module.crm.framework.excel.core.AreaExcelColumnSelectFunction;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
@ -43,7 +43,7 @@ public class CrmCustomerImportExcelVO {
|
||||
private String email;
|
||||
|
||||
@ExcelProperty(value = "地区", converter = AreaConvert.class)
|
||||
@ExcelColumnSelect(functionName = AreaExcelColumnSelectFunctionImpl.NAME)
|
||||
@ExcelColumnSelect(functionName = AreaExcelColumnSelectFunction.NAME)
|
||||
private Integer areaId;
|
||||
|
||||
@ExcelProperty("详细地址")
|
||||
|
@ -31,7 +31,7 @@ public class CrmCustomerTransferReqVO {
|
||||
private Integer oldOwnerPermissionLevel;
|
||||
|
||||
/**
|
||||
* 转移客户时,需要额外有【联系人】【商机】【合同】的 checkbox 选择
|
||||
* 转移客户时,需要额外有【联系人】【商机】【合同】的 checkbox 选择。选中时,也一起转移
|
||||
*/
|
||||
@Schema(description = "同时转移", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
|
||||
private List<Integer> toBizTypes;
|
||||
|
@ -68,22 +68,24 @@ public class CrmPermissionController {
|
||||
@Resource
|
||||
private PostApi postApi;
|
||||
|
||||
// TODO @puhui999:是不是还是叫 create 好点哈。
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建数据权限")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@PreAuthorize("@ss.hasPermission('crm:permission:create')")
|
||||
@CrmPermission(bizTypeValue = "#reqVO.bizType", bizId = "#reqVO.bizId", level = CrmPermissionLevelEnum.OWNER)
|
||||
public CommonResult<Boolean> addPermission(@Valid @RequestBody CrmPermissionSaveReqVO reqVO) {
|
||||
public CommonResult<Boolean> savePermission(@Valid @RequestBody CrmPermissionSaveReqVO reqVO) {
|
||||
permissionService.createPermission(BeanUtils.toBean(reqVO, CrmPermissionCreateReqBO.class));
|
||||
// 处理【同时添加至】的权限
|
||||
if (CollUtil.isNotEmpty(reqVO.getToBizTypes())) {
|
||||
createBizTypePermissions(reqVO);
|
||||
}
|
||||
return success(true);
|
||||
}
|
||||
|
||||
|
||||
private void createBizTypePermissions(CrmPermissionSaveReqVO reqVO) {
|
||||
List<CrmPermissionCreateReqBO> createPermissions = new ArrayList<>();
|
||||
// TODO @puhui999:需要考虑,被添加人,是不是应该有对应的权限了;
|
||||
if (reqVO.getToBizTypes().contains(CrmBizTypeEnum.CRM_CONTACT.getType())) {
|
||||
List<CrmContactDO> contactList = contactService.getContactListByCustomerIdOwnerUserId(reqVO.getBizId(), getLoginUserId());
|
||||
contactList.forEach(item -> {
|
||||
|
@ -33,7 +33,8 @@ public class CrmPermissionSaveReqVO {
|
||||
private Integer level;
|
||||
|
||||
/**
|
||||
* 添加客户团队成员时,需要额外有【联系人】【商机】【合同】的 checkbox 选择
|
||||
* 添加客户团队成员时,需要额外有【联系人】【商机】【合同】的 checkbox 选择。
|
||||
* 选中时,同时添加对应的权限
|
||||
*/
|
||||
@Schema(description = "同时添加", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
|
||||
private List<Integer> toBizTypes;
|
||||
|
@ -74,6 +74,7 @@ public interface CrmContactMapper extends BaseMapperX<CrmContactDO> {
|
||||
}
|
||||
|
||||
default List<CrmContactDO> selectListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) {
|
||||
// TODO @puhui999:父类有 selectList,查询 2 个字段的简化方法哈,可以用下
|
||||
return selectList(new LambdaQueryWrapperX<CrmContactDO>()
|
||||
.eq(CrmContactDO::getCustomerId, customerId)
|
||||
.eq(CrmContactDO::getOwnerUserId, ownerUserId));
|
||||
|
@ -99,4 +99,8 @@ public interface CrmReceivableMapper extends BaseMapperX<CrmReceivableDO> {
|
||||
return convertMap(result, obj -> (Long) obj.get("contract_id"), obj -> (BigDecimal) obj.get("total_price"));
|
||||
}
|
||||
|
||||
default Long selectCountByContractId(Long contractId) {
|
||||
return selectCount(CrmReceivableDO::getContractId, contractId);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -10,10 +10,12 @@ import java.util.List;
|
||||
/**
|
||||
* Excel 所属地区列下拉数据源获取接口实现类
|
||||
*
|
||||
* // TODO @puhui999:类名叫:地区下拉框数据源的 {@link ExcelColumnSelectFunction} 实现类,这样看起来会更简洁一点哈
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
@Service
|
||||
public class AreaExcelColumnSelectFunctionImpl implements ExcelColumnSelectFunction {
|
||||
public class AreaExcelColumnSelectFunction implements ExcelColumnSelectFunction {
|
||||
|
||||
public static final String NAME = "getCrmAreaNameList"; // 防止和别的模块重名
|
||||
|
@ -1 +1,4 @@
|
||||
/**
|
||||
* crm 模块的 excel 拓展封装
|
||||
*/
|
||||
package cn.iocoder.yudao.module.crm.framework.excel;
|
@ -1 +1,4 @@
|
||||
/**
|
||||
* crm 模块的 operatelog 拓展封装
|
||||
*/
|
||||
package cn.iocoder.yudao.module.crm.framework.operatelog;
|
@ -1 +1,4 @@
|
||||
/**
|
||||
* crm 模块的 permission 拓展封装
|
||||
*/
|
||||
package cn.iocoder.yudao.module.crm.framework.permission;
|
@ -1,4 +1,4 @@
|
||||
/**
|
||||
* trade 模块的 web 配置
|
||||
* crm 模块的 web 拓展封装
|
||||
*/
|
||||
package cn.iocoder.yudao.module.crm.framework.web;
|
||||
|
@ -4,7 +4,6 @@ import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
@ -27,7 +26,6 @@ import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPerm
|
||||
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
|
||||
import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
|
||||
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
|
||||
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerServiceImpl;
|
||||
import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
|
||||
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
|
||||
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
|
||||
@ -231,7 +229,7 @@ public class CrmContractServiceImpl implements CrmContractService {
|
||||
// 1.1 校验存在
|
||||
CrmContractDO contract = validateContractExists(id);
|
||||
// 1.2 如果被 CrmReceivableDO 所使用,则不允许删除
|
||||
if (receivableService.getReceivableByContractId(contract.getId()) != 0) {
|
||||
if (receivableService.getReceivableCountByContractId(contract.getId()) > 0) {
|
||||
throw exception(CONTRACT_DELETE_FAIL);
|
||||
}
|
||||
|
||||
|
@ -123,11 +123,11 @@ public interface CrmReceivableService {
|
||||
Map<Long, BigDecimal> getReceivablePriceMapByContractId(Collection<Long> contractIds);
|
||||
|
||||
/**
|
||||
* 更具合同编号查询回款列表
|
||||
* 根据合同编号查询回款数量
|
||||
*
|
||||
* @param contractId 合同编号
|
||||
* @return 回款
|
||||
* @return 回款数量
|
||||
*/
|
||||
Long getReceivableByContractId(Long contractId);
|
||||
Long getReceivableCountByContractId(Long contractId);
|
||||
|
||||
}
|
||||
|
@ -302,8 +302,8 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getReceivableByContractId(Long contractId) {
|
||||
return receivableMapper.selectCount(CrmReceivableDO::getContractId, contractId);
|
||||
public Long getReceivableCountByContractId(Long contractId) {
|
||||
return receivableMapper.selectCountByContractId(contractId);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user