From 42e8accd973a75d11f01c949c8aadbe13437545b Mon Sep 17 00:00:00 2001 From: Owen <595466820@qq.com> Date: Fri, 27 Dec 2024 11:51:42 +0800 Subject: [PATCH] =?UTF-8?q?Feat:=20Api=20=E8=81=94=E9=80=9A=20+=20?= =?UTF-8?q?=E5=9C=B0=E5=8C=BA=E6=95=B4=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../haoka/api/liantong/area/AreaUtils.java | 212 ------------- .../area/{Area.java => LianTongArea.java} | 45 ++- .../api/liantong/area/LianTongAreaUtils.java | 284 ++++++++++++++++++ .../haoka/api/liantong/area/TestMain.java | 4 +- 4 files changed, 325 insertions(+), 220 deletions(-) delete mode 100644 yudao-module-haoka/yudao-module-haoka-biz/src/main/java/cn/iocoder/yudao/module/haoka/api/liantong/area/AreaUtils.java rename yudao-module-haoka/yudao-module-haoka-biz/src/main/java/cn/iocoder/yudao/module/haoka/api/liantong/area/{Area.java => LianTongArea.java} (53%) create mode 100644 yudao-module-haoka/yudao-module-haoka-biz/src/main/java/cn/iocoder/yudao/module/haoka/api/liantong/area/LianTongAreaUtils.java diff --git a/yudao-module-haoka/yudao-module-haoka-biz/src/main/java/cn/iocoder/yudao/module/haoka/api/liantong/area/AreaUtils.java b/yudao-module-haoka/yudao-module-haoka-biz/src/main/java/cn/iocoder/yudao/module/haoka/api/liantong/area/AreaUtils.java deleted file mode 100644 index 4d7c7c94f3..0000000000 --- a/yudao-module-haoka/yudao-module-haoka-biz/src/main/java/cn/iocoder/yudao/module/haoka/api/liantong/area/AreaUtils.java +++ /dev/null @@ -1,212 +0,0 @@ -package cn.iocoder.yudao.module.haoka.api.liantong.area; - -import cn.hutool.core.io.resource.ResourceUtil; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.text.csv.CsvRow; -import cn.hutool.core.text.csv.CsvUtil; -import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; -import lombok.NonNull; -import lombok.extern.slf4j.Slf4j; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Function; - -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.findFirst; - -/** - * 区域工具类 - * - * @author 芋道源码 - */ -@Slf4j -public class AreaUtils { - - /** - * 初始化 SEARCHER - */ - @SuppressWarnings("InstantiationOfUtilityClass") - private final static AreaUtils INSTANCE = new AreaUtils(); - - /** - * Area 内存缓存,提升访问速度 - */ - private static Map areas; - - private AreaUtils() { - long now = System.currentTimeMillis(); - areas = new HashMap<>(); - areas.put(Area.ID_GLOBAL, new Area(Area.ID_GLOBAL, "全球", 0, - null, new ArrayList<>())); - // 从 csv 中加载数据 - List rows = CsvUtil.getReader().read(ResourceUtil.getUtf8Reader("area.csv")).getRows(); - rows.remove(0); // 删除 header - for (CsvRow row : rows) { - // 创建 Area 对象 - Area area = new Area(Integer.valueOf(row.get(0)), row.get(1), Integer.valueOf(row.get(2)), - null, new ArrayList<>()); - // 添加到 areas 中 - areas.put(area.getId(), area); - } - - // 构建父子关系:因为 Area 中没有 parentId 字段,所以需要重复读取 - for (CsvRow row : rows) { - Area area = areas.get(Integer.valueOf(row.get(0))); // 自己 - Area parent = areas.get(Integer.valueOf(row.get(3))); // 父 - Assert.isTrue(area != parent, "{}:父子节点相同", area.getName()); - area.setParent(parent); - parent.getChildren().add(area); - } - log.info("启动加载 AreaUtils 成功,耗时 ({}) 毫秒", System.currentTimeMillis() - now); - } - - /** - * 获得指定编号对应的区域 - * - * @param id 区域编号 - * @return 区域 - */ - public static Area getArea(Integer id) { - return areas.get(id); - } - - /** - * 获得指定区域对应的编号 - * - * @param pathStr 区域路径,例如说:河南省/石家庄市/新华区 - * @return 区域 - */ - public static Area parseArea(String pathStr) { - String[] paths = pathStr.split("/"); - Area area = null; - for (String path : paths) { - if (area == null) { - area = findFirst(areas.values(), item -> item.getName().equals(path)); - } else { - area = findFirst(area.getChildren(), item -> item.getName().equals(path)); - } - } - return area; - } - - /** - * 获取所有节点的全路径名称如:河南省/石家庄市/新华区 - * - * @param areas 地区树 - * @return 所有节点的全路径名称 - */ - public static List getAreaNodePathList(List areas) { - List paths = new ArrayList<>(); - areas.forEach(area -> getAreaNodePathList(area, "", paths)); - return paths; - } - - /** - * 构建一棵树的所有节点的全路径名称,并将其存储为 "祖先/父级/子级" 的形式 - * - * @param node 父节点 - * @param path 全路径名称 - * @param paths 全路径名称列表,省份/城市/地区 - */ - private static void getAreaNodePathList(Area node, String path, List paths) { - if (node == null) { - return; - } - // 构建当前节点的路径 - String currentPath = path.isEmpty() ? node.getName() : path + "/" + node.getName(); - paths.add(currentPath); - // 递归遍历子节点 - for (Area child : node.getChildren()) { - getAreaNodePathList(child, currentPath, paths); - } - } - - /** - * 格式化区域 - * - * @param id 区域编号 - * @return 格式化后的区域 - */ - public static String format(Integer id) { - return format(id, " "); - } - - /** - * 格式化区域 - * - * 例如说: - * 1. id = “静安区”时:上海 上海市 静安区 - * 2. id = “上海市”时:上海 上海市 - * 3. id = “上海”时:上海 - * 4. id = “美国”时:美国 - * 当区域在中国时,默认不显示中国 - * - * @param id 区域编号 - * @param separator 分隔符 - * @return 格式化后的区域 - */ - public static String format(Integer id, String separator) { - // 获得区域 - Area area = areas.get(id); - if (area == null) { - return null; - } - - // 格式化 - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < AreaTypeEnum.values().length; i++) { // 避免死循环 - sb.insert(0, area.getName()); - // “递归”父节点 - area = area.getParent(); - if (area == null - || ObjectUtils.equalsAny(area.getId(), Area.ID_GLOBAL, Area.ID_CHINA)) { // 跳过父节点为中国的情况 - break; - } - sb.insert(0, separator); - } - return sb.toString(); - } - - /** - * 获取指定类型的区域列表 - * - * @param type 区域类型 - * @param func 转换函数 - * @param 结果类型 - * @return 区域列表 - */ - public static List getByType(AreaTypeEnum type, Function func) { - return convertList(areas.values(), func, area -> type.getType().equals(area.getType())); - } - - /** - * 根据区域编号、上级区域类型,获取上级区域编号 - * - * @param id 区域编号 - * @param type 区域类型 - * @return 上级区域编号 - */ - public static Integer getParentIdByType(Integer id, @NonNull AreaTypeEnum type) { - for (int i = 0; i < Byte.MAX_VALUE; i++) { - Area area = AreaUtils.getArea(id); - if (area == null) { - return null; - } - // 情况一:匹配到,返回它 - if (type.getType().equals(area.getType())) { - return area.getId(); - } - // 情况二:找到根节点,返回空 - if (area.getParent() == null || area.getParent().getId() == null) { - return null; - } - // 其它:继续向上查找 - id = area.getParent().getId(); - } - return null; - } - -} diff --git a/yudao-module-haoka/yudao-module-haoka-biz/src/main/java/cn/iocoder/yudao/module/haoka/api/liantong/area/Area.java b/yudao-module-haoka/yudao-module-haoka-biz/src/main/java/cn/iocoder/yudao/module/haoka/api/liantong/area/LianTongArea.java similarity index 53% rename from yudao-module-haoka/yudao-module-haoka-biz/src/main/java/cn/iocoder/yudao/module/haoka/api/liantong/area/Area.java rename to yudao-module-haoka/yudao-module-haoka-biz/src/main/java/cn/iocoder/yudao/module/haoka/api/liantong/area/LianTongArea.java index 1989f1bf96..50cb9ee1f2 100644 --- a/yudao-module-haoka/yudao-module-haoka-biz/src/main/java/cn/iocoder/yudao/module/haoka/api/liantong/area/Area.java +++ b/yudao-module-haoka/yudao-module-haoka-biz/src/main/java/cn/iocoder/yudao/module/haoka/api/liantong/area/LianTongArea.java @@ -20,21 +20,21 @@ import java.util.List; @AllArgsConstructor @NoArgsConstructor @ToString(exclude = {"parent"}) // 参见 https://gitee.com/yudaocode/yudao-cloud-mini/pulls/2 原因 -public class Area { +public class LianTongArea { /** * 编号 - 全球,即根目录 */ - public static final Integer ID_GLOBAL = 0; + public static final String ID_GLOBAL = "0"; /** * 编号 - 中国 */ - public static final Integer ID_CHINA = 1; + public static final String ID_CHINA = "1"; /** * 编号 */ - private Integer id; + private String id; /** * 名字 */ @@ -46,15 +46,48 @@ public class Area { */ private Integer type; + /** + * 收货地址省份编码 + */ + private String provinceCode; + /** + * 收货地址省份名称 + */ + private String provinceName; + /** + * 收货地址地市编码 + */ + private String cityCode; + /** + * 收货地址地市名称 + */ + private String cityName; + /** + * 收货地址区县编码 + */ + private String districtCode; + /** + * 收货地址区县名称 + */ + private String districtName; + /** + * 对应号码归属省份编码 + */ + private String phoneProvinceCode; + /** + * 对应号码归属地市编码 + */ + private String phoneCityCode; + /** * 父节点 */ @JsonManagedReference - private Area parent; + private LianTongArea parent; /** * 子节点 */ @JsonBackReference - private List children; + private List children; } diff --git a/yudao-module-haoka/yudao-module-haoka-biz/src/main/java/cn/iocoder/yudao/module/haoka/api/liantong/area/LianTongAreaUtils.java b/yudao-module-haoka/yudao-module-haoka-biz/src/main/java/cn/iocoder/yudao/module/haoka/api/liantong/area/LianTongAreaUtils.java new file mode 100644 index 0000000000..655fc27d73 --- /dev/null +++ b/yudao-module-haoka/yudao-module-haoka-biz/src/main/java/cn/iocoder/yudao/module/haoka/api/liantong/area/LianTongAreaUtils.java @@ -0,0 +1,284 @@ +package cn.iocoder.yudao.module.haoka.api.liantong.area; + +import cn.hutool.core.io.resource.ResourceUtil; +import cn.hutool.core.text.csv.CsvRow; +import cn.hutool.core.text.csv.CsvUtil; +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; + +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.findFirst; + +/** + * 区域工具类 + * + * @author 芋道源码 + */ +@Slf4j +public class LianTongAreaUtils { + + /** + * 初始化 SEARCHER + */ + @SuppressWarnings("InstantiationOfUtilityClass") + private final static LianTongAreaUtils INSTANCE = new LianTongAreaUtils(); + + /** + * Area 内存缓存,提升访问速度 + */ + private static Map areas; + + private LianTongAreaUtils() { + areas = new HashMap<>(); + + LianTongArea china = new LianTongArea(LianTongArea.ID_GLOBAL, "中国", 1, null, null, null, null, null, null, null, null, null, new ArrayList<>()); + areas.put(LianTongArea.ID_CHINA, china); + + + // 从 csv 中加载数据 + List rows = CsvUtil.getReader().read(ResourceUtil.getUtf8Reader("api/liantong/收货地址省市区编码20200427.csv")).getRows(); + rows.remove(0); // 删除 header + for (CsvRow row : rows) { + LianTongArea area = new LianTongArea( + row.get(4), // 区县编码作为ID + row.get(5), // 区县名称 + AreaTypeEnum.DISTRICT.getType(), // 类型为区县 + row.get(0), // 省份编码 + row.get(1), // 省份名称 + row.get(2), // 地市编码 + row.get(3), // 地市名称 + row.get(4), // 区县编码 + row.get(5), // 区县名称 + row.get(6), // 号码归属省份编码 + row.get(7), // 号码归属地市编码 + null, // 父节点 + new ArrayList<>() // 子节点 + ); + + + // 处理省 + LianTongArea belongProvince = areas.get(area.getProvinceCode()); + if (belongProvince == null) { + belongProvince = new LianTongArea( + row.get(0), // 省份编码 + row.get(1), // 省份名称 + AreaTypeEnum.PROVINCE.getType(), // 类型为区县 + row.get(0), // 省份编码 + row.get(1), // 省份名称 + null, // 地市编码 + null, // 地市名称 + null, // 区县编码 + null, // 区县名称 + null, // 号码归属省份编码 + null, // 号码归属地市编码 + china, // 父节点 + new ArrayList<>() // 子节点 + ); + china.getChildren().add(belongProvince); + areas.put(area.getProvinceCode(), belongProvince); + } + + + // 处理市 + Map cityMap = belongProvince.getChildren().stream().collect(Collectors.toMap(LianTongArea::getId, it -> it)); + LianTongArea belongCity = cityMap.get(area.getCityCode()); + if (belongCity == null) { + belongCity = new LianTongArea( + row.get(2), // 地市编码 + row.get(3), // 地市名称 + AreaTypeEnum.CITY.getType(), // 类型为区县 + row.get(0), // 省份编码 + row.get(1), // 省份名称 + row.get(2), // 地市编码 + row.get(3), // 地市名称 + null, // 区县编码 + null, // 区县名称 + row.get(6), // 号码归属省份编码 + row.get(7), // 号码归属地市编码 + belongProvince, // 父节点 + new ArrayList<>() // 子节点 + ); + belongProvince.getChildren().add(belongCity); + areas.put(belongCity.getId(), belongCity); + } + // 处理县 创建 Area 对象 县 + Map areaAreaMap = belongCity.getChildren().stream().collect(Collectors.toMap(LianTongArea::getId, it -> it)); + LianTongArea areaInfo = cityMap.get(area.getId()); + if (areaInfo == null) { + areaInfo = new LianTongArea( + row.get(4), // 区县编码作为ID + row.get(5), // 区县名称 + AreaTypeEnum.DISTRICT.getType(), // 类型为区县 + row.get(0), // 省份编码 + row.get(1), // 省份名称 + row.get(2), // 地市编码 + row.get(3), // 地市名称 + row.get(4), // 区县编码 + row.get(5), // 区县名称 + row.get(6), // 号码归属省份编码 + row.get(7), // 号码归属地市编码 + belongCity, // 父节点 + new ArrayList<>() // 子节点 + ); + belongCity.getChildren().add(areaInfo); + areas.put(areaInfo.getId(), areaInfo); + } + } + } + + /** + * 获得指定编号对应的区域 + * + * @param id 区域编号 + * @return 区域 + */ + public static LianTongArea getArea(String id) { + return areas.get(id); + } + + /** + * 获得指定区域对应的编号 + * + * @param pathStr 区域路径,例如说:河南省/石家庄市/新华区 + * @return 区域 + */ + public static LianTongArea parseArea(String pathStr) { + String[] paths = pathStr.split("/"); + LianTongArea area = null; + for (String path : paths) { + if (area == null) { + area = findFirst(areas.values(), item -> item.getName().equals(path)); + } else { + area = findFirst(area.getChildren(), item -> item.getName().equals(path)); + } + } + return area; + } + + /** + * 获取所有节点的全路径名称如:河南省/石家庄市/新华区 + * + * @param areas 地区树 + * @return 所有节点的全路径名称 + */ + public static List getAreaNodePathList(List areas) { + List paths = new ArrayList<>(); + areas.forEach(area -> getAreaNodePathList(area, "", paths)); + return paths; + } + + /** + * 构建一棵树的所有节点的全路径名称,并将其存储为 "祖先/父级/子级" 的形式 + * + * @param node 父节点 + * @param path 全路径名称 + * @param paths 全路径名称列表,省份/城市/地区 + */ + private static void getAreaNodePathList(LianTongArea node, String path, List paths) { + if (node == null) { + return; + } + // 构建当前节点的路径 + String currentPath = path.isEmpty() ? node.getName() : path + "/" + node.getName(); + paths.add(currentPath); + // 递归遍历子节点 + for (LianTongArea child : node.getChildren()) { + getAreaNodePathList(child, currentPath, paths); + } + } + + /** + * 格式化区域 + * + * @param id 区域编号 + * @return 格式化后的区域 + */ + public static String format(String id) { + return format(id, " "); + } + + /** + * 格式化区域 + *

+ * 例如说: + * 1. id = “静安区”时:上海 上海市 静安区 + * 2. id = “上海市”时:上海 上海市 + * 3. id = “上海”时:上海 + * 4. id = “美国”时:美国 + * 当区域在中国时,默认不显示中国 + * + * @param id 区域编号 + * @param separator 分隔符 + * @return 格式化后的区域 + */ + public static String format(String id, String separator) { + // 获得区域 + LianTongArea area = areas.get(id); + if (area == null) { + return null; + } + + // 格式化 + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < AreaTypeEnum.values().length; i++) { // 避免死循环 + sb.insert(0, area.getName()); + // “递归”父节点 + area = area.getParent(); + if (area == null + || ObjectUtils.equalsAny(area.getId(), LianTongArea.ID_GLOBAL, LianTongArea.ID_CHINA)) { // 跳过父节点为中国的情况 + break; + } + sb.insert(0, separator); + } + return sb.toString(); + } + + /** + * 获取指定类型的区域列表 + * + * @param type 区域类型 + * @param func 转换函数 + * @param 结果类型 + * @return 区域列表 + */ + public static List getByType(AreaTypeEnum type, Function func) { + return convertList(areas.values(), func, area -> type.getType().equals(area.getType())); + } + + /** + * 根据区域编号、上级区域类型,获取上级区域编号 + * + * @param id 区域编号 + * @param type 区域类型 + * @return 上级区域编号 + */ + public static String getParentIdByType(String id, @NonNull AreaTypeEnum type) { + for (int i = 0; i < Byte.MAX_VALUE; i++) { + LianTongArea area = LianTongAreaUtils.getArea(id); + if (area == null) { + return null; + } + // 情况一:匹配到,返回它 + if (type.getType().equals(area.getType())) { + return area.getId(); + } + // 情况二:找到根节点,返回空 + if (area.getParent() == null || area.getParent().getId() == null) { + return null; + } + // 其它:继续向上查找 + id = area.getParent().getId(); + } + return null; + } + +} diff --git a/yudao-module-haoka/yudao-module-haoka-biz/src/main/java/cn/iocoder/yudao/module/haoka/api/liantong/area/TestMain.java b/yudao-module-haoka/yudao-module-haoka-biz/src/main/java/cn/iocoder/yudao/module/haoka/api/liantong/area/TestMain.java index 5015977e0a..ab435d907d 100644 --- a/yudao-module-haoka/yudao-module-haoka-biz/src/main/java/cn/iocoder/yudao/module/haoka/api/liantong/area/TestMain.java +++ b/yudao-module-haoka/yudao-module-haoka-biz/src/main/java/cn/iocoder/yudao/module/haoka/api/liantong/area/TestMain.java @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.haoka.api.liantong.area; public class TestMain { public static void main(String[] args) { - Area area = AreaUtils.getArea(0); + LianTongArea area = LianTongAreaUtils.getArea("1"); - System.out.println("----"); + System.out.println("----"+ LianTongAreaUtils.format("120221")); } }