Commit 89fc83c41938d0fd88337507c0190fdcaba6ff41

Authored by YunaiV
1 parent e2ec426a

CRM:code review 客户权限的实现

Showing 21 changed files with 71 additions and 42 deletions
sql/mysql/ruoyi-vue-pro.sql
... ... @@ -11,7 +11,7 @@
11 11 Target Server Version : 80200 (8.2.0)
12 12 File Encoding : 65001
13 13  
14   - Date: 01/03/2024 19:39:45
  14 + Date: 05/03/2024 23:36:35
15 15 */
16 16  
17 17 SET NAMES utf8mb4;
... ...
yudao-dependencies/pom.xml
... ... @@ -627,11 +627,11 @@
627 627 <artifactId>ureport2-console</artifactId>
628 628 <version>${ureport2.version}</version>
629 629 <exclusions>
630   - <exclusion><!-- 包冲突 积木报表 导出Excel报错 -->
  630 + <exclusion><!-- 包冲突:解决积木报表导出 Excel 报错 -->
631 631 <groupId>org.apache.poi</groupId>
632 632 <artifactId>poi</artifactId>
633 633 </exclusion>
634   - <exclusion><!-- 包冲突 积木报表 导出Excel报错 -->
  634 + <exclusion><!-- 包冲突:解决积木报表导出 Excel 报错 -->
635 635 <groupId>org.apache.poi</groupId>
636 636 <artifactId>poi-ooxml</artifactId>
637 637 </exclusion>
... ...
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/MapUtils.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.common.util.collection;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.collection.CollectionUtil;
  5 +import cn.hutool.core.util.ObjUtil;
5 6 import cn.iocoder.yudao.framework.common.core.KeyValue;
6 7 import com.google.common.collect.Maps;
7 8 import com.google.common.collect.Multimap;
... ... @@ -40,6 +41,7 @@ public class MapUtils {
40 41  
41 42 /**
42 43 * 从哈希表查找到 key 对应的 value,然后进一步处理
  44 + * key 为 null 时, 不处理
43 45 * 注意,如果查找到的 value 为 null 时,不进行处理
44 46 *
45 47 * @param map 哈希表
... ... @@ -47,7 +49,7 @@ public class MapUtils {
47 49 * @param consumer 进一步处理的逻辑
48 50 */
49 51 public static <K, V> void findAndThen(Map<K, V> map, K key, Consumer<V> consumer) {
50   - if (CollUtil.isEmpty(map)) {
  52 + if (ObjUtil.isNull(key) || CollUtil.isEmpty(map)) {
51 53 return;
52 54 }
53 55 V value = map.get(key);
... ...
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/core/DictFrameworkUtils.java
... ... @@ -13,8 +13,6 @@ import lombok.extern.slf4j.Slf4j;
13 13 import java.time.Duration;
14 14 import java.util.List;
15 15  
16   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
17   -
18 16 /**
19 17 * 字典工具类
20 18 *
... ... @@ -27,6 +25,7 @@ public class DictFrameworkUtils {
27 25  
28 26 private static final DictDataRespDTO DICT_DATA_NULL = new DictDataRespDTO();
29 27  
  28 + // TODO @puhui999:GET_DICT_DATA_CACHE、GET_DICT_DATA_LIST_CACHE、PARSE_DICT_DATA_CACHE 这 3 个缓存是有点重叠,可以思考下,有没可能减少 1 个。微信讨论好私聊,再具体改哈
30 29 /**
31 30 * 针对 {@link #getDictDataLabel(String, String)} 的缓存
32 31 */
... ...
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/annotations/ExcelColumnSelect.java
... ... @@ -3,7 +3,9 @@ package cn.iocoder.yudao.framework.excel.core.annotations;
3 3 import java.lang.annotation.*;
4 4  
5 5 /**
6   - * 给列添加下拉选择数据
  6 + * 给 Excel 列添加下拉选择数据
  7 + *
  8 + * 其中 {@link #dictType()} 和 {@link #functionName()} 二选一
7 9 *
8 10 * @author HUIHUI
9 11 */
... ...
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java
1 1 package cn.iocoder.yudao.framework.excel.core.handler;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.lang.Assert;
4 5 import cn.hutool.core.map.MapUtil;
5   -import cn.hutool.core.util.ObjUtil;
  6 +import cn.hutool.core.util.ObjectUtil;
6 7 import cn.hutool.core.util.StrUtil;
7 8 import cn.hutool.extra.spring.SpringUtil;
8 9 import cn.hutool.poi.excel.ExcelUtil;
... ... @@ -59,7 +60,6 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
59 60 }
60 61  
61 62 // 解析下拉数据
62   - // TODO @puhui999:感觉可以 head 循环 field,如果有 ExcelColumnSelect 则进行处理;而 ExcelProperty 可能是非必须的。回答:主要是用于定位到列索引
63 63 Map<String, Field> excelPropertyFields = getFieldsWithAnnotation(head, ExcelProperty.class);
64 64 Map<String, Field> excelColumnSelectFields = getFieldsWithAnnotation(head, ExcelColumnSelect.class);
65 65 int colIndex = 0;
... ... @@ -71,39 +71,47 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
71 71 if (index != -1) {
72 72 colIndex = index;
73 73 }
74   - getSelectDataList(colIndex, field);
  74 + buildSelectDataList(colIndex, field);
75 75 }
76 76 colIndex++;
77 77 }
  78 + // TODO @puhui999:感觉可以 head 循环 field,如果有 ExcelColumnSelect 则进行处理;而 ExcelProperty 可能是非必须的。回答:主要是用于定位到列索引;补充:可以看看下面这样写?
  79 +// for (Field field : head.getDeclaredFields()) {
  80 +// if (field.isAnnotationPresent(ExcelColumnSelect.class)) {
  81 +// ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
  82 +// if (excelProperty != null) {
  83 +// colIndex = excelProperty.index();
  84 +// }
  85 +// getSelectDataList(colIndex, field);
  86 +// }
  87 +// colIndex++;
  88 +// }
78 89 }
79 90  
80 91 /**
81   - * 获得下拉数据
  92 + * 获得下拉数据,并添加到 {@link #selectMap} 中
82 93 *
83 94 * @param colIndex 列索引
84 95 * @param field 字段
85 96 */
86   - private void getSelectDataList(int colIndex, Field field) {
87   - // 获得下拉注解信息
  97 + private void buildSelectDataList(int colIndex, Field field) {
88 98 ExcelColumnSelect columnSelect = field.getAnnotation(ExcelColumnSelect.class);
89 99 String dictType = columnSelect.dictType();
  100 + String functionName = columnSelect.functionName();
  101 + Assert.isTrue(ObjectUtil.isNotEmpty(dictType) || ObjectUtil.isNotEmpty(functionName),
  102 + "Field({}) 的 @ExcelColumnSelect 注解,dictType 和 functionName 不能同时为空", field.getName());
  103 +
  104 + // 情况一:使用 dictType 获得下拉数据
90 105 if (StrUtil.isNotEmpty(dictType)) { // 情况一: 字典数据 (默认)
91 106 selectMap.put(colIndex, DictFrameworkUtils.getDictDataLabelList(dictType));
92 107 return;
93 108 }
94   - String functionName = columnSelect.functionName();
95   - if (StrUtil.isEmpty(functionName)) { // 情况二: 获取自定义数据
96   - log.warn("[getSelectDataList]解析下拉数据失败,参数信息 dictType[{}] functionName[{}]", dictType, functionName);
97   - return;
98   - }
99   - // 获得所有的下拉数据源获取方法
  109 +
  110 + // 情况二:使用 functionName 获得下拉数据
100 111 Map<String, ExcelColumnSelectFunction> functionMap = SpringUtil.getApplicationContext().getBeansOfType(ExcelColumnSelectFunction.class);
101   - functionMap.values().forEach(func -> {
102   - if (ObjUtil.notEqual(func.getName(), functionName)) {
103   - return;
104   - }
105   - selectMap.put(colIndex, func.getOptions());
106   - });
  112 + ExcelColumnSelectFunction function = CollUtil.findOne(functionMap.values(), item -> item.getName().equals(functionName));
  113 + Assert.notNull(function, "未找到对应的 function({})", functionName);
  114 + selectMap.put(colIndex, function.getOptions());
107 115 }
108 116  
109 117 @Override
... ... @@ -121,7 +129,7 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
121 129 Sheet dictSheet = workbook.createSheet(DICT_SHEET_NAME);
122 130 for (KeyValue<Integer, List<String>> keyValue : keyValues) {
123 131 int rowLength = keyValue.getValue().size();
124   - // 2.1 设置字典 sheet 页的值 每一列一个字典项
  132 + // 2.1 设置字典 sheet 页的值,每一列一个字典项
125 133 for (int i = 0; i < rowLength; i++) {
126 134 Row row = dictSheet.getRow(i);
127 135 if (row == null) {
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessTransferReqVO.java
... ... @@ -13,6 +13,7 @@ import lombok.NoArgsConstructor;
13 13 @AllArgsConstructor
14 14 public class CrmBusinessTransferReqVO {
15 15  
  16 + // TODO @puhui999:这里最好还是用 id 哈,主要还是在 Business 业务里
16 17 @Schema(description = "商机编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
17 18 @NotNull(message = "商机编号不能为空")
18 19 private Long bizId;
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportExcelVO.java
... ... @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
4 4 import cn.iocoder.yudao.framework.excel.core.annotations.ExcelColumnSelect;
5 5 import cn.iocoder.yudao.framework.excel.core.convert.AreaConvert;
6 6 import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
7   -import cn.iocoder.yudao.module.crm.framework.excel.core.AreaExcelColumnSelectFunctionImpl;
  7 +import cn.iocoder.yudao.module.crm.framework.excel.core.AreaExcelColumnSelectFunction;
8 8 import com.alibaba.excel.annotation.ExcelProperty;
9 9 import lombok.AllArgsConstructor;
10 10 import lombok.Builder;
... ... @@ -43,7 +43,7 @@ public class CrmCustomerImportExcelVO {
43 43 private String email;
44 44  
45 45 @ExcelProperty(value = "地区", converter = AreaConvert.class)
46   - @ExcelColumnSelect(functionName = AreaExcelColumnSelectFunctionImpl.NAME)
  46 + @ExcelColumnSelect(functionName = AreaExcelColumnSelectFunction.NAME)
47 47 private Integer areaId;
48 48  
49 49 @ExcelProperty("详细地址")
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerTransferReqVO.java
... ... @@ -31,7 +31,7 @@ public class CrmCustomerTransferReqVO {
31 31 private Integer oldOwnerPermissionLevel;
32 32  
33 33 /**
34   - * 转移客户时,需要额外有【联系人】【商机】【合同】的 checkbox 选择
  34 + * 转移客户时,需要额外有【联系人】【商机】【合同】的 checkbox 选择。选中时,也一起转移
35 35 */
36 36 @Schema(description = "同时转移", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
37 37 private List<Integer> toBizTypes;
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.java
... ... @@ -68,22 +68,24 @@ public class CrmPermissionController {
68 68 @Resource
69 69 private PostApi postApi;
70 70  
  71 + // TODO @puhui999:是不是还是叫 create 好点哈。
71 72 @PostMapping("/create")
72 73 @Operation(summary = "创建数据权限")
73 74 @Transactional(rollbackFor = Exception.class)
74 75 @PreAuthorize("@ss.hasPermission('crm:permission:create')")
75 76 @CrmPermission(bizTypeValue = "#reqVO.bizType", bizId = "#reqVO.bizId", level = CrmPermissionLevelEnum.OWNER)
76   - public CommonResult<Boolean> addPermission(@Valid @RequestBody CrmPermissionSaveReqVO reqVO) {
  77 + public CommonResult<Boolean> savePermission(@Valid @RequestBody CrmPermissionSaveReqVO reqVO) {
77 78 permissionService.createPermission(BeanUtils.toBean(reqVO, CrmPermissionCreateReqBO.class));
  79 + // 处理【同时添加至】的权限
78 80 if (CollUtil.isNotEmpty(reqVO.getToBizTypes())) {
79 81 createBizTypePermissions(reqVO);
80 82 }
81 83 return success(true);
82 84 }
83 85  
84   -
85 86 private void createBizTypePermissions(CrmPermissionSaveReqVO reqVO) {
86 87 List<CrmPermissionCreateReqBO> createPermissions = new ArrayList<>();
  88 + // TODO @puhui999:需要考虑,被添加人,是不是应该有对应的权限了;
87 89 if (reqVO.getToBizTypes().contains(CrmBizTypeEnum.CRM_CONTACT.getType())) {
88 90 List<CrmContactDO> contactList = contactService.getContactListByCustomerIdOwnerUserId(reqVO.getBizId(), getLoginUserId());
89 91 contactList.forEach(item -> {
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionSaveReqVO.java
... ... @@ -33,7 +33,8 @@ public class CrmPermissionSaveReqVO {
33 33 private Integer level;
34 34  
35 35 /**
36   - * 添加客户团队成员时,需要额外有【联系人】【商机】【合同】的 checkbox 选择
  36 + * 添加客户团队成员时,需要额外有【联系人】【商机】【合同】的 checkbox 选择。
  37 + * 选中时,同时添加对应的权限
37 38 */
38 39 @Schema(description = "同时添加", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
39 40 private List<Integer> toBizTypes;
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java
... ... @@ -74,6 +74,7 @@ public interface CrmContactMapper extends BaseMapperX&lt;CrmContactDO&gt; {
74 74 }
75 75  
76 76 default List<CrmContactDO> selectListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) {
  77 + // TODO @puhui999:父类有 selectList,查询 2 个字段的简化方法哈,可以用下
77 78 return selectList(new LambdaQueryWrapperX<CrmContactDO>()
78 79 .eq(CrmContactDO::getCustomerId, customerId)
79 80 .eq(CrmContactDO::getOwnerUserId, ownerUserId));
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java
... ... @@ -99,4 +99,8 @@ public interface CrmReceivableMapper extends BaseMapperX&lt;CrmReceivableDO&gt; {
99 99 return convertMap(result, obj -> (Long) obj.get("contract_id"), obj -> (BigDecimal) obj.get("total_price"));
100 100 }
101 101  
  102 + default Long selectCountByContractId(Long contractId) {
  103 + return selectCount(CrmReceivableDO::getContractId, contractId);
  104 + }
  105 +
102 106 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/core/AreaExcelColumnSelectFunctionImpl.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/core/AreaExcelColumnSelectFunction.java
... ... @@ -10,10 +10,12 @@ import java.util.List;
10 10 /**
11 11 * Excel 所属地区列下拉数据源获取接口实现类
12 12 *
  13 + * // TODO @puhui999:类名叫:地区下拉框数据源的 {@link ExcelColumnSelectFunction} 实现类,这样看起来会更简洁一点哈
  14 + *
13 15 * @author HUIHUI
14 16 */
15 17 @Service
16   -public class AreaExcelColumnSelectFunctionImpl implements ExcelColumnSelectFunction {
  18 +public class AreaExcelColumnSelectFunction implements ExcelColumnSelectFunction {
17 19  
18 20 public static final String NAME = "getCrmAreaNameList"; // 防止和别的模块重名
19 21  
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/package-info.java
  1 +/**
  2 + * crm 模块的 excel 拓展封装
  3 + */
1 4 package cn.iocoder.yudao.module.crm.framework.excel;
2 5 \ No newline at end of file
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/package-info.java
  1 +/**
  2 + * crm 模块的 operatelog 拓展封装
  3 + */
1 4 package cn.iocoder.yudao.module.crm.framework.operatelog;
2 5 \ No newline at end of file
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/package-info.java
  1 +/**
  2 + * crm 模块的 permission 拓展封装
  3 + */
1 4 package cn.iocoder.yudao.module.crm.framework.permission;
2 5 \ No newline at end of file
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/web/package-info.java
1 1 /**
2   - * trade 模块的 web 配置
  2 + * crm 模块的 web 拓展封装
3 3 */
4 4 package cn.iocoder.yudao.module.crm.framework.web;
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
... ... @@ -4,7 +4,6 @@ import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.collection.ListUtil;
5 5 import cn.hutool.core.lang.Assert;
6 6 import cn.hutool.core.util.ObjUtil;
7   -import cn.hutool.extra.spring.SpringUtil;
8 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
9 8 import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
10 9 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
... ... @@ -27,7 +26,6 @@ import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPerm
27 26 import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
28 27 import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
29 28 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
30   -import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerServiceImpl;
31 29 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
32 30 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
33 31 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
... ... @@ -231,7 +229,7 @@ public class CrmContractServiceImpl implements CrmContractService {
231 229 // 1.1 校验存在
232 230 CrmContractDO contract = validateContractExists(id);
233 231 // 1.2 如果被 CrmReceivableDO 所使用,则不允许删除
234   - if (receivableService.getReceivableByContractId(contract.getId()) != 0) {
  232 + if (receivableService.getReceivableCountByContractId(contract.getId()) > 0) {
235 233 throw exception(CONTRACT_DELETE_FAIL);
236 234 }
237 235  
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java
... ... @@ -123,11 +123,11 @@ public interface CrmReceivableService {
123 123 Map<Long, BigDecimal> getReceivablePriceMapByContractId(Collection<Long> contractIds);
124 124  
125 125 /**
126   - * 更具合同编号查询回款列表
  126 + * 根据合同编号查询回款数量
127 127 *
128 128 * @param contractId 合同编号
129   - * @return 回款
  129 + * @return 回款数量
130 130 */
131   - Long getReceivableByContractId(Long contractId);
  131 + Long getReceivableCountByContractId(Long contractId);
132 132  
133 133 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java
... ... @@ -302,8 +302,8 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
302 302 }
303 303  
304 304 @Override
305   - public Long getReceivableByContractId(Long contractId) {
306   - return receivableMapper.selectCount(CrmReceivableDO::getContractId, contractId);
  305 + public Long getReceivableCountByContractId(Long contractId) {
  306 + return receivableMapper.selectCountByContractId(contractId);
307 307 }
308 308  
309 309 }
... ...