package com.tcm.training.admin.app.register.service;
|
|
import com.tcm.training.admin.app.register.entity.SysRegion;
|
import com.tcm.training.admin.app.register.mapper.SysRegionMapper;
|
import com.tcm.training.admin.app.register.vo.AppRegisterRegionDatasetVO;
|
import com.tcm.training.admin.app.register.vo.AppRegisterRegionOptionVO;
|
import com.tcm.training.admin.app.register.vo.AppRegisterRegionPickerOptionVO;
|
import lombok.Getter;
|
import lombok.NoArgsConstructor;
|
import lombok.Setter;
|
import org.springframework.stereotype.Service;
|
|
import java.util.ArrayList;
|
import java.util.Collections;
|
import java.util.LinkedHashMap;
|
import java.util.List;
|
import java.util.Map;
|
|
/**
|
* APP 注册行政区划服务,负责三级联动查询与海诊通地区数据归一化。
|
*/
|
@Service
|
public class AppRegisterRegionService {
|
|
private static final long ROOT_PARENT_ID = 0L;
|
|
private final SysRegionMapper sysRegionMapper;
|
|
/**
|
* 注入行政区划查询 Mapper。
|
*/
|
public AppRegisterRegionService(SysRegionMapper sysRegionMapper) {
|
this.sysRegionMapper = sysRegionMapper;
|
}
|
|
/**
|
* 按父级地区编码查询下一级行政区划。
|
*/
|
public List<AppRegisterRegionOptionVO> listRegions(String parentCode) {
|
Long parentId = resolveParentId(parentCode);
|
if (parentId == null) {
|
return Collections.emptyList();
|
}
|
List<SysRegion> regions = sysRegionMapper.selectByParentId(parentId);
|
if (regions == null || regions.isEmpty()) {
|
return Collections.emptyList();
|
}
|
List<AppRegisterRegionOptionVO> options = new ArrayList<AppRegisterRegionOptionVO>(regions.size());
|
for (SysRegion region : regions) {
|
options.add(toRegionOption(region));
|
}
|
return options;
|
}
|
|
/**
|
* 构造兼容 APP 滚轮地区选择器的数据集。
|
*/
|
public AppRegisterRegionDatasetVO buildRegionDataset() {
|
List<SysRegion> regions = sysRegionMapper.selectAllOrdered();
|
AppRegisterRegionDatasetVO dataset = new AppRegisterRegionDatasetVO();
|
if (regions == null || regions.isEmpty()) {
|
return dataset;
|
}
|
|
Map<Long, List<SysRegion>> childrenByParentId = new LinkedHashMap<Long, List<SysRegion>>();
|
for (SysRegion region : regions) {
|
Long parentId = region.getParentId() == null ? ROOT_PARENT_ID : region.getParentId();
|
List<SysRegion> children = childrenByParentId.get(parentId);
|
if (children == null) {
|
children = new ArrayList<SysRegion>();
|
childrenByParentId.put(parentId, children);
|
}
|
children.add(region);
|
}
|
|
List<SysRegion> provinces = childrenByParentId.get(ROOT_PARENT_ID);
|
if (provinces == null || provinces.isEmpty()) {
|
return dataset;
|
}
|
|
List<AppRegisterRegionPickerOptionVO> provinceOptions = new ArrayList<AppRegisterRegionPickerOptionVO>(provinces.size());
|
List<List<AppRegisterRegionPickerOptionVO>> cityOptions = new ArrayList<List<AppRegisterRegionPickerOptionVO>>(provinces.size());
|
List<List<List<AppRegisterRegionPickerOptionVO>>> areaOptions = new ArrayList<List<List<AppRegisterRegionPickerOptionVO>>>(provinces.size());
|
|
for (SysRegion province : provinces) {
|
provinceOptions.add(toPickerOption(province, 1));
|
|
List<SysRegion> cities = childrenByParentId.get(province.getId());
|
if (cities == null) {
|
cities = Collections.emptyList();
|
}
|
List<AppRegisterRegionPickerOptionVO> currentCityOptions = new ArrayList<AppRegisterRegionPickerOptionVO>(cities.size());
|
List<List<AppRegisterRegionPickerOptionVO>> currentAreaOptions = new ArrayList<List<AppRegisterRegionPickerOptionVO>>(cities.size());
|
|
for (SysRegion city : cities) {
|
currentCityOptions.add(toPickerOption(city, 2));
|
|
List<SysRegion> districts = childrenByParentId.get(city.getId());
|
if (districts == null) {
|
districts = Collections.emptyList();
|
}
|
List<AppRegisterRegionPickerOptionVO> currentDistrictOptions = new ArrayList<AppRegisterRegionPickerOptionVO>(districts.size());
|
for (SysRegion district : districts) {
|
if (shouldHideInAppPicker(district)) {
|
continue;
|
}
|
currentDistrictOptions.add(toPickerOption(district, 3));
|
}
|
currentAreaOptions.add(currentDistrictOptions);
|
}
|
|
cityOptions.add(currentCityOptions);
|
areaOptions.add(currentAreaOptions);
|
}
|
|
dataset.setProvinces(provinceOptions);
|
dataset.setCities(cityOptions);
|
dataset.setAreas(areaOptions);
|
return dataset;
|
}
|
|
/**
|
* APP 地区选择器不展示行政区划占位节点。
|
*/
|
private boolean shouldHideInAppPicker(SysRegion region) {
|
return region.getName() != null && region.getName().contains("市辖区");
|
}
|
|
/**
|
* 将海诊通诊所地区编码或名称归一化为本地三级行政区划。
|
*/
|
public RegionSelection resolveRegionSelection(String provinceCode,
|
String provinceName,
|
String cityCode,
|
String cityName,
|
String districtCode,
|
String districtName) {
|
SysRegion province = resolveRegionByCode(provinceCode);
|
SysRegion city = resolveRegionByCode(cityCode);
|
SysRegion district = resolveRegionByCode(districtCode);
|
|
if (district != null && city == null) {
|
city = findRegionById(district.getParentId());
|
}
|
if (city != null && province == null) {
|
province = findRegionById(city.getParentId());
|
}
|
|
province = resolveProvinceRegion(province, provinceName);
|
city = resolveCityRegion(city, province, cityName);
|
if (city != null && province == null) {
|
province = findRegionById(city.getParentId());
|
}
|
district = resolveDistrictRegion(district, city, districtName);
|
if (district != null && city == null) {
|
city = findRegionById(district.getParentId());
|
}
|
if (city != null && province == null) {
|
province = findRegionById(city.getParentId());
|
}
|
|
if (!isSameParent(city, province)) {
|
city = null;
|
district = null;
|
}
|
if (!isSameParent(district, city)) {
|
district = null;
|
}
|
|
RegionSelection selection = new RegionSelection();
|
if (province != null) {
|
selection.setProvinceCode(province.getCode());
|
selection.setProvinceName(province.getName());
|
}
|
if (city != null) {
|
selection.setCityCode(city.getCode());
|
selection.setCityName(city.getName());
|
}
|
if (district != null) {
|
selection.setDistrictCode(district.getCode());
|
selection.setDistrictName(district.getName());
|
}
|
return selection;
|
}
|
|
/**
|
* 将实体转换为前端下拉选项。
|
*/
|
private AppRegisterRegionOptionVO toRegionOption(SysRegion region) {
|
AppRegisterRegionOptionVO option = new AppRegisterRegionOptionVO();
|
option.setId(region.getId());
|
option.setCode(region.getCode());
|
option.setName(region.getName());
|
option.setParentId(region.getParentId());
|
option.setLevel(resolveRegionLevel(region.getCode()));
|
return option;
|
}
|
|
/**
|
* 将实体转换为滚轮选择器使用的标签和值。
|
*/
|
private AppRegisterRegionPickerOptionVO toPickerOption(SysRegion region, int level) {
|
AppRegisterRegionPickerOptionVO option = new AppRegisterRegionPickerOptionVO();
|
option.setLabel(region.getName());
|
option.setValue(resolvePickerValue(region.getCode(), level));
|
return option;
|
}
|
|
/**
|
* 将数据库 6 位行政区编码转换为前端滚轮选择器的分级编码。
|
*/
|
private String resolvePickerValue(String code, int level) {
|
if (code == null || code.trim().isEmpty()) {
|
return "";
|
}
|
if (level == 1 && code.length() >= 2) {
|
return code.substring(0, 2);
|
}
|
if (level == 2 && code.length() >= 4) {
|
return code.substring(0, 4);
|
}
|
return code.length() >= 6 ? code.substring(0, 6) : code;
|
}
|
|
/**
|
* 计算当前编码所属的行政区层级。
|
*/
|
private Integer resolveRegionLevel(String code) {
|
if (code == null || code.length() != 6) {
|
return null;
|
}
|
if (code.endsWith("0000")) {
|
return 1;
|
}
|
if (code.endsWith("00")) {
|
return 2;
|
}
|
return 3;
|
}
|
|
/**
|
* 将父级地区编码转换为父级 ID。
|
*/
|
private Long resolveParentId(String parentCode) {
|
String normalizedParentCode = trimToNull(parentCode);
|
if (normalizedParentCode == null) {
|
return ROOT_PARENT_ID;
|
}
|
SysRegion parent = sysRegionMapper.selectByCode(normalizedParentCode);
|
return parent == null ? null : parent.getId();
|
}
|
|
/**
|
* 优先按编码解析省份,编码缺失时再按名称匹配根节点。
|
*/
|
private SysRegion resolveProvinceRegion(SysRegion province, String provinceName) {
|
if (province != null) {
|
return province;
|
}
|
return resolveRegionByName(ROOT_PARENT_ID, provinceName);
|
}
|
|
/**
|
* 优先按编码解析城市,编码无效时回退到省份下的名称匹配。
|
*/
|
private SysRegion resolveCityRegion(SysRegion city, SysRegion province, String cityName) {
|
if (city != null && (province == null || isSameParent(city, province))) {
|
return city;
|
}
|
if (province == null) {
|
return null;
|
}
|
return resolveRegionByName(province.getId(), cityName);
|
}
|
|
/**
|
* 优先按编码解析区县,编码无效时回退到城市下的名称匹配。
|
*/
|
private SysRegion resolveDistrictRegion(SysRegion district, SysRegion city, String districtName) {
|
if (district != null && (city == null || isSameParent(district, city))) {
|
return district;
|
}
|
if (city == null) {
|
return null;
|
}
|
return resolveRegionByName(city.getId(), districtName);
|
}
|
|
/**
|
* 按编码查询行政区划,空值直接返回空。
|
*/
|
private SysRegion resolveRegionByCode(String code) {
|
String normalizedCode = trimToNull(code);
|
if (normalizedCode == null) {
|
return null;
|
}
|
return sysRegionMapper.selectByCode(normalizedCode);
|
}
|
|
/**
|
* 在父节点下按名称精确匹配行政区划。
|
*/
|
private SysRegion resolveRegionByName(Long parentId, String name) {
|
String normalizedName = trimToNull(name);
|
if (parentId == null || normalizedName == null) {
|
return null;
|
}
|
return sysRegionMapper.selectByParentIdAndName(parentId, normalizedName);
|
}
|
|
/**
|
* 按主键回查行政区划实体。
|
*/
|
private SysRegion findRegionById(Long regionId) {
|
if (regionId == null || regionId <= 0) {
|
return null;
|
}
|
return sysRegionMapper.selectById(regionId);
|
}
|
|
/**
|
* 判断子节点是否归属于指定父节点。
|
*/
|
private boolean isSameParent(SysRegion child, SysRegion parent) {
|
if (child == null || parent == null) {
|
return true;
|
}
|
return parent.getId() != null && parent.getId().equals(child.getParentId());
|
}
|
|
/**
|
* 将空白字符串统一转换为 null。
|
*/
|
private String trimToNull(String value) {
|
if (value == null) {
|
return null;
|
}
|
String trimmed = value.trim();
|
return trimmed.isEmpty() ? null : trimmed;
|
}
|
|
/**
|
* 归一化后的三级行政区划结果。
|
*/
|
@Getter
|
@Setter
|
@NoArgsConstructor
|
public static class RegionSelection {
|
|
private String provinceCode;
|
|
private String provinceName;
|
|
private String cityCode;
|
|
private String cityName;
|
|
private String districtCode;
|
|
private String districtName;
|
}
|
}
|