diff --git a/backend/src/main/java/io/dataease/auth/service/impl/ShiroServiceImpl.java b/backend/src/main/java/io/dataease/auth/service/impl/ShiroServiceImpl.java index 5c8209fefd..dca84d3a04 100644 --- a/backend/src/main/java/io/dataease/auth/service/impl/ShiroServiceImpl.java +++ b/backend/src/main/java/io/dataease/auth/service/impl/ShiroServiceImpl.java @@ -54,6 +54,7 @@ public class ShiroServiceImpl implements ShiroService { // 验证链接 filterChainDefinitionMap.put("/api/link/validate**", ANON); filterChainDefinitionMap.put("/api/map/areaEntitys/**", ANON); + filterChainDefinitionMap.put("/api/map/globalEntitys/**", ANON); filterChainDefinitionMap.put("/linkJump/queryPanelJumpInfo/**", ANON); filterChainDefinitionMap.put("/linkJump/queryTargetPanelJumpInfo", ANON); diff --git a/backend/src/main/java/io/dataease/config/WebMvcConfig.java b/backend/src/main/java/io/dataease/config/WebMvcConfig.java index 5653245ee1..bca35898fb 100644 --- a/backend/src/main/java/io/dataease/config/WebMvcConfig.java +++ b/backend/src/main/java/io/dataease/config/WebMvcConfig.java @@ -9,7 +9,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebMvcConfig implements WebMvcConfigurer { - @Value("${geo.rootpath:file:/opt/dataease/data/feature/full/}") + @Value("${geo.rootpath:file:/opt/dataease/data/feature/}") private String geoPath; @Override diff --git a/backend/src/main/java/io/dataease/listener/GlobalMapTransferListener.java b/backend/src/main/java/io/dataease/listener/GlobalMapTransferListener.java new file mode 100644 index 0000000000..736c59d40d --- /dev/null +++ b/backend/src/main/java/io/dataease/listener/GlobalMapTransferListener.java @@ -0,0 +1,20 @@ +package io.dataease.listener; + +import io.dataease.map.service.MapTransferService; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +@Component +public class GlobalMapTransferListener implements ApplicationListener { + + @Resource + private MapTransferService mapTransferService; + + @Override + public void onApplicationEvent(ApplicationReadyEvent event) { + mapTransferService.execute(); + } +} diff --git a/backend/src/main/java/io/dataease/map/api/MapApi.java b/backend/src/main/java/io/dataease/map/api/MapApi.java index 58a839443f..616e1b2491 100644 --- a/backend/src/main/java/io/dataease/map/api/MapApi.java +++ b/backend/src/main/java/io/dataease/map/api/MapApi.java @@ -1,9 +1,9 @@ package io.dataease.map.api; import io.dataease.map.dto.entity.AreaEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; +import io.dataease.map.dto.request.MapNodeRequest; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import java.util.List; @@ -19,4 +19,9 @@ public interface MapApi { @GetMapping("/globalEntitys/{pcode}") List globalEntitys(@PathVariable String pcode); + @PostMapping(value = "/saveMapNode", consumes = {"multipart/form-data"}) + void saveMapNode(MapNodeRequest request, MultipartFile file) throws Exception; + + @PostMapping("/delMapNode") + void delMapNode(MapNodeRequest request); } diff --git a/backend/src/main/java/io/dataease/map/dto/request/MapNodeRequest.java b/backend/src/main/java/io/dataease/map/dto/request/MapNodeRequest.java new file mode 100644 index 0000000000..71cee7f957 --- /dev/null +++ b/backend/src/main/java/io/dataease/map/dto/request/MapNodeRequest.java @@ -0,0 +1,20 @@ +package io.dataease.map.dto.request; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +@Data +public class MapNodeRequest implements Serializable { + @ApiModelProperty("区域代码") + private String code; + @ApiModelProperty("区域名称") + private String name; + @ApiModelProperty("上级代码") + private String pcode; + @ApiModelProperty("上级级别") + private Integer plevel; + + +} diff --git a/backend/src/main/java/io/dataease/map/dto/response/MapNodeReadReponse.java b/backend/src/main/java/io/dataease/map/dto/response/MapNodeReadReponse.java new file mode 100644 index 0000000000..606e863844 --- /dev/null +++ b/backend/src/main/java/io/dataease/map/dto/response/MapNodeReadReponse.java @@ -0,0 +1,21 @@ +package io.dataease.map.dto.response; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +@Data +public class MapNodeReadReponse implements Serializable { + + @ApiModelProperty("区域代码") + private String code; + @ApiModelProperty("区域名称") + private String name; + @ApiModelProperty("区域级别") + private Integer level; + @ApiModelProperty("上级区域") + private MapNodeReadReponse parent; + @ApiModelProperty("geoGson") + private String json; +} diff --git a/backend/src/main/java/io/dataease/map/server/MapServer.java b/backend/src/main/java/io/dataease/map/server/MapServer.java index 82e54caca8..7c7ffd610c 100644 --- a/backend/src/main/java/io/dataease/map/server/MapServer.java +++ b/backend/src/main/java/io/dataease/map/server/MapServer.java @@ -1,15 +1,17 @@ package io.dataease.map.server; -import io.dataease.commons.utils.LogUtil; import io.dataease.map.api.MapApi; import io.dataease.map.dto.entity.AreaEntity; +import io.dataease.map.dto.request.MapNodeRequest; import io.dataease.map.service.MapService; -import io.dataease.map.utils.MapUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + import javax.annotation.Resource; -import java.util.ArrayList; import java.util.List; @RestController @@ -35,4 +37,15 @@ public class MapServer implements MapApi { } return mapService.entitysByPid(areaEntities, pcode); } + + + @Override + public void saveMapNode(@RequestPart("request") MapNodeRequest request, @RequestPart(value = "file") MultipartFile file) throws Exception{ + mapService.saveMapNode(request, file); + } + + @Override + public void delMapNode(@RequestBody MapNodeRequest request) { + mapService.delMapNode(request); + } } diff --git a/backend/src/main/java/io/dataease/map/service/MapService.java b/backend/src/main/java/io/dataease/map/service/MapService.java index ac21dc9052..e4d395eee7 100644 --- a/backend/src/main/java/io/dataease/map/service/MapService.java +++ b/backend/src/main/java/io/dataease/map/service/MapService.java @@ -1,14 +1,27 @@ package io.dataease.map.service; import cn.hutool.core.collection.CollectionUtil; -import cn.hutool.core.io.file.FileReader; +import cn.hutool.core.io.FileUtil; +import io.dataease.commons.exception.DEException; +import io.dataease.commons.utils.CommonBeanFactory; +import io.dataease.listener.util.CacheUtils; import io.dataease.map.dto.entity.AreaEntity; +import io.dataease.map.dto.request.MapNodeRequest; import io.dataease.map.utils.MapUtils; +import io.dataease.plugins.common.base.domain.AreaMappingGlobal; +import io.dataease.plugins.common.base.domain.AreaMappingGlobalExample; import org.apache.commons.lang3.StringUtils; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; +import java.io.File; +import java.util.Comparator; import java.util.List; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Collectors; @Service public class MapService { @@ -21,7 +34,7 @@ public class MapService { return areaEntities; } - @Cacheable("sys_map_areas") + @Cacheable("sys_map_areas_global") public List globalEntities() { List areaEntities = MapUtils.readGlobalAreaEntity(); return areaEntities; @@ -45,4 +58,293 @@ public class MapService { } + public String generateAreaCode(String pCode) { + Long pValue = Long.parseLong(pCode); + MapService mapService = CommonBeanFactory.getBean(MapService.class); + List areaEntities = mapService.globalEntities(); + List brothers = entitysByPid(areaEntities, pCode); + + brothers.sort(Comparator.comparing(item -> Long.parseLong(item.getCode()))); + AreaEntity lastBrother = brothers.get(brothers.size() - 1); + + Long lastCode = Long.parseLong(lastBrother.getCode()); + + long areaCodeSuffix = lastCode - pValue; + + int step = 0; + + while (areaCodeSuffix % 10 == 0) { + step++; + areaCodeSuffix /= 10; + } + + areaCodeSuffix++; + + while (step > 0) { + areaCodeSuffix *= 10; + step--; + } + + long targetCode = areaCodeSuffix + pValue; + return String.valueOf(targetCode); + } + + private void delFileByNodes(List nodes, Integer pLevel) { + Set sets = nodes.stream().flatMap(node -> codesByNode(node, pLevel).stream()).collect(Collectors.toSet()); + sets.forEach(code -> { + String countryCode = code.substring(0, 3); + String path = dirPath + "/full/" + countryCode + "/" + code +"_full.json"; + if (FileUtil.exist(path)) { + FileUtil.del(path); + } + }); + } + + private Set codesByNode(AreaMappingGlobal node, Integer pLevel) { + Set sets = new TreeSet<>(); + + if (pLevel == 2) { + if(StringUtils.isNotBlank(node.getProvinceCode())) sets.add(node.getProvinceCode()); + if(StringUtils.isNotBlank(node.getCityCode())) sets.add(node.getCityCode()); + if(StringUtils.isNotBlank(node.getCountyCode())) sets.add(node.getCountyCode()); + } else if (pLevel == 3) { + if(StringUtils.isNotBlank(node.getCityCode())) sets.add(node.getCityCode()); + if(StringUtils.isNotBlank(node.getCountyCode())) sets.add(node.getCountyCode()); + } else if (pLevel == 4) { + if(StringUtils.isNotBlank(node.getCountyCode())) sets.add(node.getCountyCode()); + } else { + if(StringUtils.isNotBlank(node.getCountryCode())) sets.add(node.getCountryCode()); + if(StringUtils.isNotBlank(node.getProvinceCode())) sets.add(node.getProvinceCode()); + if(StringUtils.isNotBlank(node.getCityCode())) sets.add(node.getCityCode()); + if(StringUtils.isNotBlank(node.getCountyCode())) sets.add(node.getCountyCode()); + } + return sets; + } + + @Transactional + public void delMapNode(MapNodeRequest request) { + String pCode = request.getPcode(); + Integer pLevel = request.getPlevel(); + String code = request.getCode(); + AreaMappingGlobalExample example = new AreaMappingGlobalExample(); + AreaMappingGlobal curRoot = new AreaMappingGlobal(); + List nodes = null; + if(pLevel == 0) { + nodes = MapUtils.selectByExample(example); + MapUtils.deleteByExample(example); + delFileByNodes(nodes, pLevel); + } else if (pLevel == 1) { + example.createCriteria().andCountryCodeEqualTo(code); + nodes = MapUtils.selectByExample(example); + MapUtils.deleteByExample(example); + delFileByNodes(nodes, pLevel); + } else if (pLevel == 2) { + example.createCriteria().andCountryCodeEqualTo(pCode).andProvinceCodeEqualTo(code); + nodes = MapUtils.selectByExample(example); + MapUtils.deleteByExample(example); + example.clear(); + example.createCriteria().andCountryCodeEqualTo(pCode); + if (!MapUtils.exampleExist(example) && CollectionUtil.isNotEmpty(nodes)) { + AreaMappingGlobal template = nodes.get(0); + curRoot.setCountryCode(template.getCountryCode()); + curRoot.setCountryName(template.getCountryName()); + MapUtils.addNode(curRoot); + } + delFileByNodes(nodes, pLevel); + } else if (pLevel == 3) { + + example.createCriteria().andProvinceCodeEqualTo(pCode).andCityCodeEqualTo(code); + nodes = MapUtils.selectByExample(example); + + MapUtils.deleteByExample(example); + example.clear(); + example.createCriteria().andProvinceCodeEqualTo(pCode); + + if (!MapUtils.exampleExist(example) && CollectionUtil.isNotEmpty(nodes)) { + AreaMappingGlobal template = nodes.get(0); + curRoot.setCountryCode(template.getCountryCode()); + curRoot.setCountryName(template.getCountryName()); + curRoot.setProvinceCode(template.getProvinceCode()); + curRoot.setProvinceName(template.getProvinceName()); + MapUtils.addNode(curRoot); + } + delFileByNodes(nodes, pLevel); + } else if (pLevel == 4) { + example.createCriteria().andCityCodeEqualTo(pCode).andCountyCodeEqualTo(code); + nodes = MapUtils.selectByExample(example); + + MapUtils.deleteByExample(example); + example.clear(); + example.createCriteria().andProvinceCodeEqualTo(pCode); + + if (!MapUtils.exampleExist(example) && CollectionUtil.isNotEmpty(nodes)) { + AreaMappingGlobal template = nodes.get(0); + curRoot.setCountryCode(template.getCountryCode()); + curRoot.setCountryName(template.getCountryName()); + curRoot.setProvinceCode(template.getProvinceCode()); + curRoot.setProvinceName(template.getProvinceName()); + curRoot.setCityCode(template.getCityCode()); + curRoot.setCityName(template.getCityName()); + MapUtils.addNode(curRoot); + } + delFileByNodes(nodes, pLevel); + } + CacheUtils.removeAll("sys_map_areas_global"); + } + + private void validateFile(MultipartFile file) { + long size = file.getSize(); + String name = file.getName(); + if (size / 1024 / 1024 > 30) { + DEException.throwException("large file that exceed 30M is not supported"); + } + if (!StringUtils.endsWith(name, ".json")) { + DEException.throwException("only json file supported"); + } + } + @Transactional + public void saveMapNode(MapNodeRequest request, MultipartFile file) throws Exception{ + validateFile(file); + String pCode = request.getPcode(); + Integer plevel = request.getPlevel(); + String code = request.getCode(); + + if(StringUtils.isBlank(code)) { + String newAreaCode = generateAreaCode(pCode); + request.setCode(newAreaCode); + } + + AreaMappingGlobalExample example = new AreaMappingGlobalExample(); + + + if (plevel == 1) { + example.createCriteria().andCountryCodeEqualTo(code); + } + else if (plevel == 2) { + example.createCriteria().andCountryCodeEqualTo(pCode).andProvinceCodeEqualTo(code); + } + else if (plevel == 3) { + example.createCriteria().andProvinceCodeEqualTo(pCode).andCityCodeEqualTo(code); + }else if (plevel == 4) { + example.createCriteria().andCityCodeEqualTo(pCode).andCountyCodeEqualTo(code); + } else { + DEException.throwException("只支持3级行政区"); + } + List lists = MapUtils.selectByExample(example); + if (CollectionUtil.isNotEmpty(lists)) { + DEException.throwException("区域代码已存在"); + } + + example.clear(); + AreaMappingGlobalExample pExample = new AreaMappingGlobalExample(); + if (plevel == 1) { + pExample.createCriteria().andCountryCodeIsNull().andProvinceCodeIsNull().andCityCodeIsNull().andCountyCodeIsNull(); + List existLists = MapUtils.selectByExample(pExample); + if (CollectionUtil.isNotEmpty(existLists)) { + AreaMappingGlobal node = existLists.get(0); + node.setCountryCode(code); + node.setCountryName(request.getName()); + MapUtils.update(node); + }else { + AreaMappingGlobal node = new AreaMappingGlobal(); + node.setCountryCode(code); + node.setCountryName(request.getName()); + MapUtils.addNode(node); + } + } + else if (plevel == 2) { + pExample.createCriteria().andCountryCodeEqualTo(pCode).andProvinceCodeIsNull().andCityCodeIsNull().andCountyCodeIsNull(); + List existLists = MapUtils.selectByExample(pExample); + if (CollectionUtil.isNotEmpty(existLists)) { + AreaMappingGlobal node = existLists.get(0); + node.setProvinceCode(code); + node.setProvinceName(request.getName()); + MapUtils.update(node); + }else { + AreaMappingGlobal country = country(pCode); + AreaMappingGlobal node = new AreaMappingGlobal(); + node.setCountryCode(pCode); + node.setCountryName(country.getCountryName()); + node.setProvinceCode(code); + node.setProvinceName(request.getName()); + MapUtils.addNode(node); + } + } + else if (plevel == 3) { + pExample.createCriteria().andProvinceCodeEqualTo(pCode).andCityCodeIsNull(); + List existLists = MapUtils.selectByExample(pExample); + if (CollectionUtil.isNotEmpty(existLists)) { + AreaMappingGlobal node = existLists.get(0); + node.setCityCode(code); + node.setCityName(request.getName()); + MapUtils.update(node); + }else { + AreaMappingGlobal province = province(pCode); + AreaMappingGlobal node = new AreaMappingGlobal(); + node.setCountryCode(province.getCountryCode()); + node.setCountryName(province.getCountryName()); + node.setProvinceCode(pCode); + node.setProvinceName(province.getProvinceName()); + node.setCityCode(code); + node.setCityName(request.getName()); + MapUtils.addNode(node); + } + } else if (plevel == 4) { + pExample.createCriteria().andCountryCodeEqualTo(pCode).andCountyCodeIsNull(); + List existLists = MapUtils.selectByExample(pExample); + if (CollectionUtil.isNotEmpty(existLists)) { + AreaMappingGlobal node = existLists.get(0); + node.setCountyCode(code); + node.setCountyName(request.getName()); + MapUtils.update(node); + }else { + AreaMappingGlobal city = city(pCode); + AreaMappingGlobal node = new AreaMappingGlobal(); + node.setCountryCode(city.getCountryCode()); + node.setCountryName(city.getCountryName()); + node.setProvinceCode(city.getProvinceCode()); + node.setProvinceName(city.getProvinceName()); + node.setCityCode(pCode); + node.setCityName(request.getName()); + node.setCountyCode(code); + node.setCountyName(request.getName()); + MapUtils.addNode(node); + } + } else { + DEException.throwException("只支持3级行政区"); + } + uploadMapFile(file, code); + CacheUtils.removeAll("sys_map_areas_global"); + } + + public void uploadMapFile(MultipartFile file, String areaCode) throws Exception{ + String dir = dirPath + "full/"; + File fileDir = new File(dir); + if (!fileDir.exists()) { + fileDir.mkdirs(); + } + String countryCode = areaCode.substring(0, 3); + String targetPath = dir + countryCode + "/" + areaCode+"_full.json"; + File target = new File(targetPath); + file.transferTo(target); + } + + private AreaMappingGlobal country(String pCode) { + AreaMappingGlobalExample pExample = new AreaMappingGlobalExample(); + pExample.createCriteria().andCountryCodeEqualTo(pCode); + return MapUtils.selectByExample(pExample).stream().findFirst().get(); + } + + private AreaMappingGlobal province(String pCode) { + AreaMappingGlobalExample pExample = new AreaMappingGlobalExample(); + pExample.createCriteria().andProvinceCodeEqualTo(pCode); + return MapUtils.selectByExample(pExample).stream().findFirst().get(); + } + + private AreaMappingGlobal city(String pCode) { + AreaMappingGlobalExample pExample = new AreaMappingGlobalExample(); + pExample.createCriteria().andCityCodeEqualTo(pCode); + return MapUtils.selectByExample(pExample).stream().findFirst().get(); + } + } diff --git a/backend/src/main/java/io/dataease/map/service/MapTransferService.java b/backend/src/main/java/io/dataease/map/service/MapTransferService.java new file mode 100644 index 0000000000..7c3b857be9 --- /dev/null +++ b/backend/src/main/java/io/dataease/map/service/MapTransferService.java @@ -0,0 +1,107 @@ +package io.dataease.map.service; + +import cn.hutool.core.io.FileUtil; +import com.google.gson.Gson; + +import io.dataease.plugins.common.base.domain.ChartView; +import io.dataease.plugins.common.base.domain.ChartViewExample; +import io.dataease.plugins.common.base.domain.ChartViewWithBLOBs; +import io.dataease.plugins.common.base.mapper.ChartViewMapper; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.io.File; +import java.util.*; +import java.util.stream.Collectors; + +@Service +public class MapTransferService { + + @Value("${geo.rootpath:/opt/dataease/data/feature/}") + private String geoPath; + + private static final List MATCH_TYPES = new ArrayList<>(); + private static final Gson gson = new Gson(); + private static final String AREA_CODE_KEY = "areaCode"; + private static final String GLOBAL_CHINA_PREFIX = "156"; + + private static final String FILE_SEPARATOR = "/"; + private static final String FULL_KEY = "full"; + private static final String BORDER_KEY = "border"; + + private static final String FULL_FILE_SUFFIX = "_full.json"; + + + @PostConstruct + public void init() { + MATCH_TYPES.add("map"); + MATCH_TYPES.add("buddle-map"); + } + @Resource + private ChartViewMapper chartViewMapper; + + + + public void execute() { + ChartViewExample example = new ChartViewExample(); + List chartViews = chartViewMapper.selectByExampleWithBLOBs(example); + chartViews.forEach(view -> { + if (typeMatch(view) && StringUtils.isNotBlank(view.getCustomAttr())) { + Map customAttrMap = convert(view.getCustomAttr()); + if (customMatch(customAttrMap)) { + view.setCustomAttr(gson.toJson(customAttrMap)); + chartViewMapper.updateByPrimaryKeyWithBLOBs(view); + } + } + }); + moveMapFiles(); + } + + public void moveMapFiles() { + String chinaRootPath = geoPath + FULL_KEY + FILE_SEPARATOR; + File chinaRootDir = new File(chinaRootPath); + File[] files = chinaRootDir.listFiles(); + Map> listMap = Arrays.stream(files).filter(FileUtil::isFile).collect(Collectors.groupingBy(this::fileType)); + if (ObjectUtils.isEmpty(listMap)) return; + moveFiles(listMap, BORDER_KEY); + moveFiles(listMap, FULL_KEY); + } + + private void moveFiles(Map> listMap, String fileType) { + String dirPath = geoPath + fileType + FILE_SEPARATOR; + Optional.ofNullable(listMap.get(fileType)).ifPresent(files -> { + files.forEach(file -> { + String fileName = file.getName(); + String newFilePath = dirPath + GLOBAL_CHINA_PREFIX + FILE_SEPARATOR + GLOBAL_CHINA_PREFIX + fileName; + FileUtil.move(file, new File(newFilePath), true); + }); + }); + } + + private String fileType(File file) { + return file.getName().endsWith(FULL_FILE_SUFFIX) ? FULL_KEY : BORDER_KEY; + } + + private Boolean typeMatch(ChartView chartView) { + return MATCH_TYPES.stream().anyMatch(type -> StringUtils.equals(type, chartView.getType())); + } + + private Map convert(String customJson) { + return gson.fromJson(customJson, Map.class); + } + + private Boolean customMatch(Map customAttrMap) { + Object codeObj = null; + if((codeObj = customAttrMap.get(AREA_CODE_KEY)) != null) { + String code = codeObj.toString(); + boolean matych = code.length() == 6; + customAttrMap.put(AREA_CODE_KEY, GLOBAL_CHINA_PREFIX + code); + return matych; + } + return false; + } +} diff --git a/backend/src/main/java/io/dataease/map/utils/MapUtils.java b/backend/src/main/java/io/dataease/map/utils/MapUtils.java index 7a35805a96..4a5f664956 100644 --- a/backend/src/main/java/io/dataease/map/utils/MapUtils.java +++ b/backend/src/main/java/io/dataease/map/utils/MapUtils.java @@ -46,6 +46,15 @@ public class MapUtils { return mappingGlobals; } + public static List selectByExample(AreaMappingGlobalExample example) { + List mappingGlobals = areaMappingGlobalMapper.selectByExample(Optional.ofNullable(example).orElse(new AreaMappingGlobalExample())); + return mappingGlobals; + } + + public static Boolean exampleExist(AreaMappingGlobalExample example) { + return areaMappingGlobalMapper.countByExample(example) > 0; + } + public static List> readCodeList() { AreaMappingExample example = new AreaMappingExample(); List areaMappings = areaMappingMapper.selectByExample(example); @@ -75,30 +84,25 @@ public class MapUtils { String city_code = map.getCityCode(); String county_code = map.getCountyCode(); // 是否是跨级直辖 - Boolean isCrossLevel = StrUtil.equals(province_code, city_code) - && !StrUtil.equals(province_code, "156710000"); + Boolean isCrossLevel = StrUtil.equals(province_code, city_code) && !StrUtil.equals(province_code, "156710000"); if (!countryMap.containsKey(country_code)) { String country_name = map.getCountryName(); - AreaEntity child = AreaEntity.builder().code(country_code).name(country_name) - .pcode(globalRoot.getCode()).build(); - + AreaEntity child = AreaEntity.builder().code(country_code).name(country_name).pcode(globalRoot.getCode()).build(); countryMap.put(country_code, child); globalRoot.addChild(child); } - AreaEntity currentCountry = countryMap.get(country_code); - String province_name = map.getProvinceName(); - if (!provinceMap.containsKey(province_code)) { - AreaEntity child = AreaEntity.builder().code(province_code).name(province_name) - .pcode(currentCountry.getCode()).build(); + if (StringUtils.isNotBlank(province_code) && !provinceMap.containsKey(province_code)) { + AreaEntity currentCountry = countryMap.get(country_code); + String province_name = map.getProvinceName(); + AreaEntity child = AreaEntity.builder().code(province_code).name(province_name).pcode(currentCountry.getCode()).build(); provinceMap.put(province_code, child); currentCountry.addChild(child); } - // 当前省 - AreaEntity currentProvince = provinceMap.get(province_code); + String city_name = map.getCityName(); if (isCrossLevel) { @@ -106,8 +110,9 @@ public class MapUtils { city_name = map.getCountyName(); } if (StringUtils.isNotBlank(city_code) && !cityMap.containsKey(city_code)) { - AreaEntity child = AreaEntity.builder().code(city_code).name(city_name).pcode(currentProvince.getCode()) - .build(); + // 当前省 + AreaEntity currentProvince = provinceMap.get(province_code); + AreaEntity child = AreaEntity.builder().code(city_code).name(city_name).pcode(currentProvince.getCode()).build(); cityMap.put(city_code, child); currentProvince.addChild(child); } @@ -199,4 +204,16 @@ public class MapUtils { return AreaEntity.builder().code("000000000").name("地球村").build(); } + public static void addNode(AreaMappingGlobal node) { + areaMappingGlobalMapper.insert(node); + } + + public static void update(AreaMappingGlobal node) { + areaMappingGlobalMapper.updateByPrimaryKey(node); + } + + public static void deleteByExample(AreaMappingGlobalExample example) { + areaMappingGlobalMapper.deleteByExample(Optional.ofNullable(example).orElse(new AreaMappingGlobalExample())); + } + } diff --git a/backend/src/main/resources/db/migration/V38__1.13.sql b/backend/src/main/resources/db/migration/V38__1.13.sql index 44c035b200..3625246223 100644 --- a/backend/src/main/resources/db/migration/V38__1.13.sql +++ b/backend/src/main/resources/db/migration/V38__1.13.sql @@ -36,4 +36,31 @@ ADD COLUMN `phone_prefix` varchar(255) NULL COMMENT '手机号前缀' AFTER `sub INSERT INTO `my_plugin` (`name`, `store`, `free`, `cost`, `category`, `descript`, `version`, `creator`, `load_mybatis`, `install_time`, `module_name`, `ds_type`) VALUES ('Mongo 数据源插件', 'default', '0', '0', 'datasource', 'Mongo 数据源插件', '1.0-SNAPSHOT', 'DATAEASE', '0', - '1650765903630', 'mongo-backend', 'mongobi'); \ No newline at end of file + '1650765903630', 'mongo-backend', 'mongobi'); + + +DROP TABLE IF EXISTS `area_mapping_global`; +CREATE TABLE `area_mapping_global` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', + `country_code` varchar(255) DEFAULT NULL COMMENT '国家代码', + `country_name` varchar(255) DEFAULT NULL COMMENT '国家名称', + `province_name` varchar(255) DEFAULT NULL COMMENT '省名称', + `province_code` varchar(255) DEFAULT NULL COMMENT '省代码', + `city_name` varchar(255) DEFAULT NULL COMMENT '市名称', + `city_code` varchar(255) DEFAULT NULL COMMENT '市代码', + `county_name` varchar(255) DEFAULT NULL COMMENT '县名称', + `county_code` varchar(255) DEFAULT NULL COMMENT '县代码', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci; + +BEGIN; +insert into area_mapping_global (province_code, province_name, city_code, city_name, county_code, county_name) + select province_code, province_name, city_code, city_name, county_code, county_name from area_mapping; + +update area_mapping_global set + country_code = '156100000', + country_name = '中华人民共和国', + province_code = concat('156', province_code), + city_code = concat('156', city_code), + county_code = concat('156', county_code); +COMMIT; \ No newline at end of file diff --git a/backend/src/main/resources/ehcache/ehcache.xml b/backend/src/main/resources/ehcache/ehcache.xml index 5e9ea1bc1e..e4086c3e04 100644 --- a/backend/src/main/resources/ehcache/ehcache.xml +++ b/backend/src/main/resources/ehcache/ehcache.xml @@ -138,6 +138,15 @@ diskPersistent="false" /> + + { return request({ - url: '/api/map/areaEntitys/0', + url: '/api/map/globalEntitys/0', method: 'get', loading: true }) @@ -17,9 +17,28 @@ export const globalMapping = () => { } export function geoJson(areaCode) { + const countryCode = areaCode.substring(0, 3) return request({ - url: '/geo/' + areaCode + '_full.json', + url: '/geo/full/' + countryCode + '/' + areaCode + '_full.json', method: 'get', loading: true }) } + +export function saveMap(data) { + return request({ + url: '/api/map/saveMapNode', + method: 'post', + loading: true, + data + }) +} + +export function removeMap(data) { + return request({ + url: '/api/map/delMapNode', + method: 'post', + loading: true, + data + }) +} diff --git a/frontend/src/lang/en.js b/frontend/src/lang/en.js index e8da510e74..007644b0bd 100644 --- a/frontend/src/lang/en.js +++ b/frontend/src/lang/en.js @@ -475,7 +475,8 @@ export default { ldap: 'LDAP Setting', oidc: 'OIDC Setting', theme: 'Theme Setting', - cas: 'CAS Setting' + cas: 'CAS Setting', + map: 'MAP Setting' }, license: { i18n_no_license_record: 'No License Record', @@ -2130,5 +2131,19 @@ export default { }, plugin_style: { border: 'Border' + }, + + map_setting: { + area_level: 'Area Level', + area_code: 'Area Code', + please_input: 'please key', + parent_area: 'Parent Area', + area_code_tip: 'The format of area code is 9 digits', + area_name: 'Area Name', + parent_name: 'Parent Area', + geo_json: 'Geo Json', + fileplaceholder: 'Please upload the JSON format coordinate file', + delete_confirm: 'And child nodes will be deleted. Confirm to execute ?', + cur_node: 'Current node' } } diff --git a/frontend/src/lang/tw.js b/frontend/src/lang/tw.js index f0398bcb86..ef821701cc 100644 --- a/frontend/src/lang/tw.js +++ b/frontend/src/lang/tw.js @@ -475,7 +475,8 @@ export default { ldap: 'LDAP設置', oidc: 'OIDC設置', theme: '主題設置', - cas: 'CAS設置' + cas: 'CAS設置', + map: '地圖設置' }, license: { i18n_no_license_record: '沒有 License 記錄', @@ -2141,5 +2142,18 @@ export default { }, plugin_style: { border: '邊框' + }, + map_setting: { + area_level: '區域等級', + area_code: '區域代碼', + please_input: '請填寫', + parent_area: '上級區域', + area_code_tip: '區域代碼格式為9位數字', + area_name: '區域名稱', + parent_name: '上級區域', + geo_json: '坐標文件', + fileplaceholder: '請上傳json格式坐標文件', + delete_confirm: '及子節點都會被刪除,確認執行?', + cur_node: '當前節點' } } diff --git a/frontend/src/lang/zh.js b/frontend/src/lang/zh.js index e3ff9dcd52..a43a8665fc 100644 --- a/frontend/src/lang/zh.js +++ b/frontend/src/lang/zh.js @@ -476,7 +476,8 @@ export default { ldap: 'LDAP设置', oidc: 'OIDC设置', theme: '主题设置', - cas: 'CAS设置' + cas: 'CAS设置', + map: '地图设置' }, license: { i18n_no_license_record: '没有 License 记录', @@ -2153,5 +2154,18 @@ export default { }, sql_variable: { variable_mgm: '参数设置' + }, + map_setting: { + area_level: '区域等级', + area_code: '区域代码', + please_input: '请填写', + parent_area: '上级区域', + area_code_tip: '区域代码格式为9位数字', + area_name: '区域名称', + parent_name: '上级区域', + geo_json: '坐标文件', + fileplaceholder: '请上传json格式坐标文件', + delete_confirm: '及子节点都会被删除,确认执行?', + cur_node: '当前节点' } } diff --git a/frontend/src/views/system/SysParam/MapSetting/MapSettingLeft.vue b/frontend/src/views/system/SysParam/MapSetting/MapSettingLeft.vue new file mode 100644 index 0000000000..dc66a97c72 --- /dev/null +++ b/frontend/src/views/system/SysParam/MapSetting/MapSettingLeft.vue @@ -0,0 +1,259 @@ + + + + + diff --git a/frontend/src/views/system/SysParam/MapSetting/MapSettingRight.vue b/frontend/src/views/system/SysParam/MapSetting/MapSettingRight.vue new file mode 100644 index 0000000000..ae6a8613bd --- /dev/null +++ b/frontend/src/views/system/SysParam/MapSetting/MapSettingRight.vue @@ -0,0 +1,349 @@ + + + + + diff --git a/frontend/src/views/system/SysParam/MapSetting/index.vue b/frontend/src/views/system/SysParam/MapSetting/index.vue new file mode 100644 index 0000000000..4c6124f08b --- /dev/null +++ b/frontend/src/views/system/SysParam/MapSetting/index.vue @@ -0,0 +1,66 @@ + + + + + diff --git a/frontend/src/views/system/SysParam/index.vue b/frontend/src/views/system/SysParam/index.vue index de27236b76..540307ef54 100644 --- a/frontend/src/views/system/SysParam/index.vue +++ b/frontend/src/views/system/SysParam/index.vue @@ -42,11 +42,16 @@ + + + +