Commit 41b8575b8d878d872639e7be4917241ddc4899ac

Authored by puhui999
1 parent b26eb626

CRM:完善客户导入

yudao-framework/yudao-spring-boot-starter-biz-ip/src/main/java/cn/iocoder/yudao/framework/ip/core/utils/AreaUtils.java
@@ -4,7 +4,7 @@ import cn.hutool.core.io.resource.ResourceUtil; @@ -4,7 +4,7 @@ import cn.hutool.core.io.resource.ResourceUtil;
4 import cn.hutool.core.lang.Assert; 4 import cn.hutool.core.lang.Assert;
5 import cn.hutool.core.text.csv.CsvRow; 5 import cn.hutool.core.text.csv.CsvRow;
6 import cn.hutool.core.text.csv.CsvUtil; 6 import cn.hutool.core.text.csv.CsvUtil;
7 -import cn.hutool.core.util.StrUtil; 7 +import cn.hutool.core.util.ObjUtil;
8 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; 8 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
9 import cn.iocoder.yudao.framework.ip.core.Area; 9 import cn.iocoder.yudao.framework.ip.core.Area;
10 import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum; 10 import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum;
@@ -18,6 +18,7 @@ import java.util.Map; @@ -18,6 +18,7 @@ import java.util.Map;
18 import java.util.function.Function; 18 import java.util.function.Function;
19 19
20 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; 20 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  21 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.findFirst;
21 22
22 /** 23 /**
23 * 区域工具类 24 * 区域工具类
@@ -76,29 +77,55 @@ public class AreaUtils { @@ -76,29 +77,55 @@ public class AreaUtils {
76 } 77 }
77 78
78 /** 79 /**
  80 + * 获得指定区域对应的编号
  81 + *
  82 + * @param path 区域编号
  83 + * @return 编号
  84 + */
  85 + public static Area getArea(String path) {
  86 + String[] paths = path.split("/");
  87 + Area area = null;
  88 + for (int i = 0; i < paths.length; i++) {
  89 + final int finalI = i;
  90 + if (area == null) {
  91 + area = findFirst(convertList(areas.values(), a -> a), item -> ObjUtil.equal(paths[finalI], item.getName()));
  92 + continue;
  93 + }
  94 + area = findFirst(area.getChildren(), item -> ObjUtil.equal(paths[finalI], item.getName()));
  95 + }
  96 + return area;
  97 + }
  98 +
  99 + /**
79 * 获取所有节点的全路径名称如:河南省/石家庄市/新华区 100 * 获取所有节点的全路径名称如:河南省/石家庄市/新华区
80 * 101 *
81 * @param areas 地区树 102 * @param areas 地区树
82 * @return 所有节点的全路径名称 103 * @return 所有节点的全路径名称
83 */ 104 */
84 public static List<String> getAllAreaNodePaths(List<Area> areas) { 105 public static List<String> getAllAreaNodePaths(List<Area> areas) {
85 - return convertList(areas, AreaUtils::buildTreePath); 106 + List<String> paths = new ArrayList<>();
  107 + areas.forEach(area -> traverse(area, "", paths));
  108 + return paths;
86 } 109 }
87 110
88 - // TODO @puhui999: 展开树再构建全路径  
89 - private static String buildTreePath(Area node) {  
90 - if (node.getParent() == null || Area.ID_CHINA.equals(node.getParent().getId())) { // 忽略中国  
91 - // 已经是根节点,直接返回节点名称  
92 - return node.getName();  
93 - } else {  
94 - // 递归拼接上级节点的名称  
95 - Area parent = getArea(node.getParent().getId());  
96 - if (parent != null) {  
97 - String parentPath = buildTreePath(parent);  
98 - return parentPath + "/" + node.getName();  
99 - } 111 + /**
  112 + * 构建一棵树的所有节点的全路径名称,并将其存储为 "祖先/父级/子级" 的形式
  113 + *
  114 + * @param node 父节点
  115 + * @param path 全路径名称
  116 + * @param paths 全路径名称列表
  117 + */
  118 + private static void traverse(Area node, String path, List<String> paths) {
  119 + if (node == null) {
  120 + return;
  121 + }
  122 + // 构建当前节点的路径
  123 + String currentPath = path.isEmpty() ? node.getName() : path + "/" + node.getName();
  124 + paths.add(currentPath);
  125 + // 递归遍历子节点
  126 + for (Area child : node.getChildren()) {
  127 + traverse(child, currentPath, paths);
100 } 128 }
101 - return StrUtil.EMPTY;  
102 } 129 }
103 130
104 /** 131 /**
yudao-framework/yudao-spring-boot-starter-excel/pom.xml
@@ -46,6 +46,11 @@ @@ -46,6 +46,11 @@
46 <groupId>com.alibaba</groupId> 46 <groupId>com.alibaba</groupId>
47 <artifactId>easyexcel</artifactId> 47 <artifactId>easyexcel</artifactId>
48 </dependency> 48 </dependency>
  49 + <dependency>
  50 + <groupId>cn.iocoder.boot</groupId>
  51 + <artifactId>yudao-spring-boot-starter-biz-ip</artifactId>
  52 + <scope>provided</scope> <!-- 设置为 provided,只有 ExcelUtils 使用 -->
  53 + </dependency>
49 </dependencies> 54 </dependencies>
50 55
51 </project> 56 </project>
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/AreaConvert.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.excel.core.convert;
  2 +
  3 +import cn.hutool.core.convert.Convert;
  4 +import cn.iocoder.yudao.framework.ip.core.Area;
  5 +import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
  6 +import com.alibaba.excel.converters.Converter;
  7 +import com.alibaba.excel.enums.CellDataTypeEnum;
  8 +import com.alibaba.excel.metadata.GlobalConfiguration;
  9 +import com.alibaba.excel.metadata.data.ReadCellData;
  10 +import com.alibaba.excel.metadata.property.ExcelContentProperty;
  11 +import lombok.extern.slf4j.Slf4j;
  12 +
  13 +/**
  14 + * Excel 数据地区转换器
  15 + *
  16 + * @author HUIHUI
  17 + */
  18 +@Slf4j
  19 +public class AreaConvert implements Converter<Object> {
  20 +
  21 + @Override
  22 + public Class<?> supportJavaTypeKey() {
  23 + throw new UnsupportedOperationException("暂不支持,也不需要");
  24 + }
  25 +
  26 + @Override
  27 + public CellDataTypeEnum supportExcelTypeKey() {
  28 + throw new UnsupportedOperationException("暂不支持,也不需要");
  29 + }
  30 +
  31 + @Override
  32 + public Object convertToJavaData(ReadCellData readCellData, ExcelContentProperty contentProperty,
  33 + GlobalConfiguration globalConfiguration) {
  34 + // 解析地区编号
  35 + String label = readCellData.getStringValue();
  36 + Area area = AreaUtils.getArea(label);
  37 + if (area == null) {
  38 + log.error("[convertToJavaData][label({}) 解析不掉]", label);
  39 + return null;
  40 + }
  41 + // 将 value 转换成对应的属性
  42 + Class<?> fieldClazz = contentProperty.getField().getType();
  43 + return Convert.convert(fieldClazz, area.getId());
  44 + }
  45 +
  46 +}
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java
@@ -9,6 +9,7 @@ import org.apache.poi.hssf.usermodel.HSSFDataValidation; @@ -9,6 +9,7 @@ import org.apache.poi.hssf.usermodel.HSSFDataValidation;
9 import org.apache.poi.ss.usermodel.*; 9 import org.apache.poi.ss.usermodel.*;
10 import org.apache.poi.ss.util.CellRangeAddressList; 10 import org.apache.poi.ss.util.CellRangeAddressList;
11 11
  12 +import java.util.Comparator;
12 import java.util.List; 13 import java.util.List;
13 14
14 /** 15 /**
@@ -18,12 +19,13 @@ import java.util.List; @@ -18,12 +19,13 @@ import java.util.List;
18 */ 19 */
19 public class SelectSheetWriteHandler implements SheetWriteHandler { 20 public class SelectSheetWriteHandler implements SheetWriteHandler {
20 21
21 - private List<KeyValue<Integer, List<String>>> selectMap; 22 + private final List<KeyValue<Integer, List<String>>> selectMap;
22 23
23 - private final char[] alphabet = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 24 + private static final char[] ALPHABET = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
24 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}; 25 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
25 26
26 public SelectSheetWriteHandler(List<KeyValue<Integer, List<String>>> selectMap) { 27 public SelectSheetWriteHandler(List<KeyValue<Integer, List<String>>> selectMap) {
  28 + selectMap.sort(Comparator.comparing(item -> item.getValue().size())); // 升序不然创建下拉会报错
27 this.selectMap = selectMap; 29 this.selectMap = selectMap;
28 } 30 }
29 31
@@ -86,17 +88,17 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { @@ -86,17 +88,17 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
86 */ 88 */
87 private String getExcelColumn(int num) { 89 private String getExcelColumn(int num) {
88 String column = ""; 90 String column = "";
89 - int len = alphabet.length - 1; 91 + int len = ALPHABET.length - 1;
90 int first = num / len; 92 int first = num / len;
91 int second = num % len; 93 int second = num % len;
92 if (num <= len) { 94 if (num <= len) {
93 - column = alphabet[num] + ""; 95 + column = ALPHABET[num] + "";
94 } else { 96 } else {
95 - column = alphabet[first - 1] + ""; 97 + column = ALPHABET[first - 1] + "";
96 if (second == 0) { 98 if (second == 0) {
97 - column = column + alphabet[len] + ""; 99 + column = column + ALPHABET[len] + "";
98 } else { 100 } else {
99 - column = column + alphabet[second - 1] + ""; 101 + column = column + ALPHABET[second - 1] + "";
100 } 102 }
101 } 103 }
102 return column; 104 return column;
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java
@@ -37,7 +37,10 @@ import org.springframework.web.bind.annotation.*; @@ -37,7 +37,10 @@ import org.springframework.web.bind.annotation.*;
37 37
38 import java.io.IOException; 38 import java.io.IOException;
39 import java.time.LocalDateTime; 39 import java.time.LocalDateTime;
40 -import java.util.*; 40 +import java.util.ArrayList;
  41 +import java.util.Arrays;
  42 +import java.util.List;
  43 +import java.util.Map;
41 import java.util.stream.Stream; 44 import java.util.stream.Stream;
42 45
43 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 46 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -332,8 +335,6 @@ public class CrmCustomerController { @@ -332,8 +335,6 @@ public class CrmCustomerController {
332 // 获取客户来源 335 // 获取客户来源
333 List<String> customerSources = dictDataApi.getDictDataLabelList(CRM_CUSTOMER_SOURCE); 336 List<String> customerSources = dictDataApi.getDictDataLabelList(CRM_CUSTOMER_SOURCE);
334 selectMap.add(new KeyValue<>(10, customerSources)); 337 selectMap.add(new KeyValue<>(10, customerSources));
335 - // 升序不然创建下拉会报错  
336 - selectMap.sort(Comparator.comparing(item -> item.getValue().size()));  
337 return selectMap; 338 return selectMap;
338 } 339 }
339 340
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportExcelVO.java
1 package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer; 1 package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer;
2 2
3 import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; 3 import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
  4 +import cn.iocoder.yudao.framework.excel.core.convert.AreaConvert;
4 import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; 5 import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
5 import com.alibaba.excel.annotation.ExcelProperty; 6 import com.alibaba.excel.annotation.ExcelProperty;
6 import lombok.AllArgsConstructor; 7 import lombok.AllArgsConstructor;
@@ -39,14 +40,12 @@ public class CrmCustomerImportExcelVO { @@ -39,14 +40,12 @@ public class CrmCustomerImportExcelVO {
39 @ExcelProperty("邮箱") 40 @ExcelProperty("邮箱")
40 private String email; 41 private String email;
41 42
42 - // TODO @puhui999:需要选择省市区,需要研究下,怎么搞合理点;  
43 - @ExcelProperty("地区编号") 43 + @ExcelProperty(value = "地区", converter = AreaConvert.class)
44 private Integer areaId; 44 private Integer areaId;
45 45
46 @ExcelProperty("详细地址") 46 @ExcelProperty("详细地址")
47 private String detailAddress; 47 private String detailAddress;
48 48
49 - // TODO @puhui999:industryId、level、source 字段,可以研究下怎么搞下拉框  
50 @ExcelProperty(value = "所属行业", converter = DictConvert.class) 49 @ExcelProperty(value = "所属行业", converter = DictConvert.class)
51 @DictFormat(CRM_CUSTOMER_INDUSTRY) 50 @DictFormat(CRM_CUSTOMER_INDUSTRY)
52 private Integer industryId; 51 private Integer industryId;