diff --git a/pom.xml b/pom.xml index e9dc294..80e8ff4 100644 --- a/pom.xml +++ b/pom.xml @@ -7,12 +7,12 @@ 17 17 UTF-8 - 3.0.5 1.2.27 42.7.8 1.18.42 1.82 2.0.60 + 3.5.9 @@ -53,11 +53,11 @@ spring-boot-starter-validation - + - org.mybatis.spring.boot - mybatis-spring-boot-starter - ${mybatis.spring.version} + com.baomidou + mybatis-plus-spring-boot3-starter + ${mybatis-plus.version} @@ -72,7 +72,7 @@ org.postgresql postgresql ${postgresql.version} - runtime + @@ -126,6 +126,47 @@ spring-boot-starter-test test + + + + org.locationtech.jts + jts-core + 1.19.0 + + + + + org.mybatis + mybatis-typehandlers-jsr310 + 1.0.2 + + + + + net.postgis + postgis-jdbc + 2021.1.0 + + + + org.hibernate.orm + hibernate-spatial + 6.2.0.Final + + + + + org.locationtech.proj4j + proj4j + 1.1.4 + + + + + org.apache.commons + commons-lang3 + + @@ -139,7 +180,7 @@ true - + prod @@ -173,7 +214,7 @@ - + org.apache.maven.plugins diff --git a/src/main/java/com/gis/xian/XianApplication.java b/src/main/java/com/gis/xian/XianApplication.java index 4311011..e274717 100644 --- a/src/main/java/com/gis/xian/XianApplication.java +++ b/src/main/java/com/gis/xian/XianApplication.java @@ -28,4 +28,4 @@ public class XianApplication { XianApplication app = context.getBean(XianApplication.class); System.out.println("后端服务启动成功!访问地址: http://localhost:" + app.port); } -} \ No newline at end of file +} diff --git a/src/main/java/com/gis/xian/config/DataSourceAspect.java b/src/main/java/com/gis/xian/config/DataSourceAspect.java index b88e425..09d8967 100644 --- a/src/main/java/com/gis/xian/config/DataSourceAspect.java +++ b/src/main/java/com/gis/xian/config/DataSourceAspect.java @@ -12,9 +12,12 @@ import org.springframework.stereotype.Component; @Order(1) @Slf4j public class DataSourceAspect { - + @Around("@annotation(dataSource) || @within(dataSource)") public Object around(ProceedingJoinPoint point, DataSource dataSource) throws Throwable { + if (dataSource == null) { + return point.proceed(); + } try { String dsName = dataSource.value(); log.debug("切换数据源: {}", dsName); diff --git a/src/main/java/com/gis/xian/config/DataSourceConfig.java b/src/main/java/com/gis/xian/config/DataSourceConfig.java index b5ed33c..bd29f36 100644 --- a/src/main/java/com/gis/xian/config/DataSourceConfig.java +++ b/src/main/java/com/gis/xian/config/DataSourceConfig.java @@ -12,19 +12,25 @@ import java.util.Map; @Configuration public class DataSourceConfig { - + @Bean @ConfigurationProperties("spring.datasource.master") public DataSource master() { return DruidDataSourceBuilder.create().build(); } - + @Bean @ConfigurationProperties("spring.datasource.slave") public DataSource slave() { return DruidDataSourceBuilder.create().build(); } - + + @Bean + @ConfigurationProperties("spring.datasource.slave1") + public DataSource slave1() { + return DruidDataSourceBuilder.create().build(); + } + @Bean @Primary public DataSource dataSource() { @@ -32,6 +38,7 @@ public class DataSourceConfig { Map map = new HashMap<>(); map.put("master", master()); map.put("slave", slave()); + map.put("slave1", slave1()); ds.setTargetDataSources(map); ds.setDefaultTargetDataSource(master()); return ds; diff --git a/src/main/java/com/gis/xian/config/typehandler/GeometryTypeHandler.java b/src/main/java/com/gis/xian/config/typehandler/GeometryTypeHandler.java new file mode 100644 index 0000000..719f525 --- /dev/null +++ b/src/main/java/com/gis/xian/config/typehandler/GeometryTypeHandler.java @@ -0,0 +1,138 @@ +package com.gis.xian.config.typehandler; + +import net.postgis.jdbc.PGgeometry; +import org.apache.ibatis.type.BaseTypeHandler; +import org.apache.ibatis.type.JdbcType; +import org.apache.ibatis.type.MappedJdbcTypes; +import org.apache.ibatis.type.MappedTypes; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.PrecisionModel; +import org.locationtech.jts.io.WKTReader; +import org.locationtech.jts.io.WKTWriter; +import org.postgresql.util.PGobject; +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * 修复编译报错版:PostgreSQL geometry → JTS Geometry 类型处理器 + * 解决 JTS Geometry 与 PostGIS Geometry 类的冲突问题 + */ +@MappedTypes(Geometry.class) // 实体类字段类型(JTS 的 Geometry) +@MappedJdbcTypes(JdbcType.OTHER) // 数据库 geometry 对应 JDBC 类型 OTHER +public class GeometryTypeHandler extends BaseTypeHandler { + + // WKT 解析器(JTS → 数据库,线程安全) + private static final WKTReader WKT_READER = new WKTReader(); + // WKT 生成器(数据库 → JTS,线程安全) + private static final WKTWriter WKT_WRITER = new WKTWriter(); + + private static final GeometryFactory GEOMETRY_FACTORY = new GeometryFactory( + new PrecisionModel(PrecisionModel.FLOATING), + 4490 + ); + + @Override + public void setNonNullParameter(PreparedStatement ps, int i, Geometry parameter, JdbcType jdbcType) throws SQLException { + try { + // 将 JTS 的 Geometry 转为标准 WKT 字符串 + String wktText = WKT_WRITER.write(parameter); + // 用 WKT 字符串创建 PGgeometry 对象 + PGgeometry pgGeometry = new PGgeometry(wktText); + // 存入 PreparedStatement + ps.setObject(i, pgGeometry); + } catch (Exception e) { + throw new SQLException("将 JTS Geometry 转为 PGgeometry 失败", e); + } + } + + @Override + public Geometry getNullableResult(ResultSet rs, String columnName) throws SQLException { + return convertToGeometry(rs.getObject(columnName)); + } + + @Override + public Geometry getNullableResult(ResultSet rs, int columnIndex) throws SQLException { + return convertToGeometry(rs.getObject(columnIndex)); + } + + @Override + public Geometry getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { + return convertToGeometry(cs.getObject(columnIndex)); + } + + private Geometry convertToGeometry(Object value) { + // 处理 null 值 + if (value == null) { + return null; + } + + String wktText = null; + try { + // 兼容 PostGIS 的 PGgeometry 格式 + if (value instanceof PGgeometry) { + PGgeometry pgGeometry = (PGgeometry) value; + // 转为标准 WKT 文本 + wktText = pgGeometry.toString(); + } + // 兼容 PostgreSQL 的 PGobject 格式 + else if (value instanceof PGobject) { + PGobject pgObject = (PGobject) value; + String pgValue = pgObject.getValue(); + if (pgValue != null && !pgValue.trim().isEmpty()) { + wktText = new PGgeometry(pgValue).toString(); + } + } + // 兼容纯 WKT 字符串格式 + else if (value instanceof String) { + wktText = (String) value; + } + // 未知格式直接返回 null + else { + return null; + } + + // 通用清洗 WKT 字符串 + String pureWkt = cleanWkt(wktText); + if (pureWkt != null && !pureWkt.trim().isEmpty() && !"NULL".equalsIgnoreCase(pureWkt)) { + Geometry geometry = WKT_READER.read(pureWkt); + // 给几何对象设置 SRID=4490 + geometry.setSRID(4490); + return geometry; + } + return null; + + } catch (Exception e) { + System.err.println("解析 geometry 字段失败,原始 WKT:" + wktText + ",异常:" + e.getMessage()); + return null; + } + } + + /** + * 通用版 WKT 清洗方法:支持 Point、LINESTRING 等常见几何类型 + */ + private String cleanWkt(String wkt) { + if (wkt == null) { + return null; + } + // 步骤1:去除所有不可见字符(换行、回车、制表符) + String cleaned = wkt.replaceAll("[\n\r\t]", ""); + // 步骤2:去除 SRID 前缀(如 "SRID=4326;") + cleaned = cleaned.replaceAll("^SRID=\\d+;", ""); + // 步骤3:将多个连续空格替换为单个空格(避免坐标间多空格干扰) + cleaned = cleaned.replaceAll("\\s+", " "); + // 步骤4:去除几何类型与括号间的多余空格(适配 Point、LINESTRING) + cleaned = cleaned.replaceAll("(POINT|LINESTRING|POLYGON)\\s*\\(", "$1("); + cleaned = cleaned.replaceAll("\\)\\s*$", ")"); + // 步骤5:去除首尾多余空格 + cleaned = cleaned.trim(); + // 步骤6:验证是否为支持的几何类型(可根据需求扩展) + if (!cleaned.startsWith("POINT(") && !cleaned.startsWith("LINESTRING(") && !cleaned.startsWith("POLYGON(")) { + System.err.println("不支持的几何类型,WKT:" + cleaned); + return null; + } + return cleaned; + } +} diff --git a/src/main/java/com/gis/xian/controller/QgisController.java b/src/main/java/com/gis/xian/controller/QgisController.java new file mode 100644 index 0000000..ee27d92 --- /dev/null +++ b/src/main/java/com/gis/xian/controller/QgisController.java @@ -0,0 +1,25 @@ +package com.gis.xian.controller; + +import com.gis.xian.service.IFeignService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +/** + * 专题图触发接口 + * 统一入口,接收 simulationId 和 type,调用 Python 端处理 + */ +@Slf4j +@RestController +@RequestMapping("/open") +public class QgisController { + + @Resource + private IFeignService feignService; + + @PostMapping("/qgis/trigger") + public void trigger(@RequestParam String simulationId, @RequestParam String type) { + log.info("收到专题图触发请求: simulationId={}, type={}", simulationId, type); + feignService.trigger(simulationId, type); + } +} diff --git a/src/main/java/com/gis/xian/service/IFeignService.java b/src/main/java/com/gis/xian/service/IFeignService.java new file mode 100644 index 0000000..4c10850 --- /dev/null +++ b/src/main/java/com/gis/xian/service/IFeignService.java @@ -0,0 +1,15 @@ +package com.gis.xian.service; + +/** + * 专题图触发接口 + * 只负责调用 Python 端,不返回结果 + */ +public interface IFeignService { + + /** + * 触发专题图生成 + * @param simulationId 模拟ID + * @param type 灾害类型(earthquake / rain) + */ + void trigger(String simulationId, String type); +} diff --git a/src/main/java/com/gis/xian/service/impl/FeignServiceImpl.java b/src/main/java/com/gis/xian/service/impl/FeignServiceImpl.java new file mode 100644 index 0000000..a848ece --- /dev/null +++ b/src/main/java/com/gis/xian/service/impl/FeignServiceImpl.java @@ -0,0 +1,49 @@ +package com.gis.xian.service.impl; + +import com.gis.xian.service.IFeignService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestClient; + +import java.util.Map; + +/** + * 专题图触发服务 + * 只负责调用 Python QGIS 服务,不处理计算和存库 + */ +@Slf4j +@Service +public class FeignServiceImpl implements IFeignService { + + @Resource + private RestClient.Builder restClientBuilder; + + @Value("${qgis.url}") + private String qgisUrl; + + @Override + public void trigger(String simulationId, String type) { + if (simulationId == null || simulationId.isBlank()) { + log.error("触发参数为空,simulationId={}", simulationId); + return; + } + + log.info("触发专题图生成: simulationId={}, type={}", simulationId, type); + + try { + RestClient client = restClientBuilder.build(); + String result = client.post() + .uri(qgisUrl) + .contentType(MediaType.APPLICATION_JSON) + .body(Map.of("simulationId", simulationId, "type", type)) + .retrieve() + .body(String.class); + log.info("Python 端响应: {}", result); + } catch (Exception e) { + log.error("调用 Python QGIS 服务失败: {}", e.getMessage(), e); + } + } +} diff --git a/src/main/java/com/gis/xian/task/InitializeData.java b/src/main/java/com/gis/xian/task/InitializeData.java index b0dc119..d0c1461 100644 --- a/src/main/java/com/gis/xian/task/InitializeData.java +++ b/src/main/java/com/gis/xian/task/InitializeData.java @@ -141,6 +141,7 @@ public class InitializeData { log.info("加载隐患点信息(崩塌)并写入redis完成"); }); + // 隐患点 - 泥石流 CompletableFuture debrisFlowFuture = CompletableFuture.runAsync(() -> { redisTemplate.opsForValue().set(debrisFlowKey, JSON.toJSONString( @@ -263,7 +264,7 @@ public class InitializeData { // 等待所有任务完成 CompletableFuture.allOf( - allFuture, landslideFuture, collapseFuture, debrisFlowFuture, + allFuture, landslideFuture, debrisFlowFuture, flashFloodFuture, waterLoggingFuture, riskFuture, hospitalsFuture, dangerousSourceFuture, emergencyShelterFuture, firefighterFuture, storePointsFuture, schoolFuture, diff --git a/src/main/java/com/gis/xian/utils/StringUtils.java b/src/main/java/com/gis/xian/utils/StringUtils.java new file mode 100644 index 0000000..0f14964 --- /dev/null +++ b/src/main/java/com/gis/xian/utils/StringUtils.java @@ -0,0 +1,682 @@ +package com.gis.xian.utils; + +import org.springframework.util.AntPathMatcher; + +import java.util.*; + +/** + * @author zzw + * @description: 字符串工具类 (基于RuoYi风格扩展) + * @date 2026/5/26 上午9:25 + */ +public class StringUtils extends org.apache.commons.lang3.StringUtils { + /** 空字符串 */ + private static final String NULLSTR = ""; + + /** 下划线 */ + private static final char SEPARATOR = '_'; + + /** 星号 */ + private static final char ASTERISK = '*'; + + /** + * 获取参数不为空值 + * + * @param value defaultValue 要判断的value + * @return value 返回值 + */ + public static T nvl(T value, T defaultValue) + { + return value != null ? value : defaultValue; + } + + /** + * * 判断一个Collection是否为空, 包含List,Set,Queue + * + * @param coll 要判断的Collection + * @return true:为空 false:非空 + */ + public static boolean isEmpty(Collection coll) + { + return isNull(coll) || coll.isEmpty(); + } + + /** + * * 判断一个Collection是否非空,包含List,Set,Queue + * + * @param coll 要判断的Collection + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Collection coll) + { + return !isEmpty(coll); + } + + /** + * * 判断一个对象数组是否为空 + * + * @param objects 要判断的对象数组 + ** @return true:为空 false:非空 + */ + public static boolean isEmpty(Object[] objects) + { + return isNull(objects) || (objects.length == 0); + } + + /** + * * 判断一个对象数组是否非空 + * + * @param objects 要判断的对象数组 + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Object[] objects) + { + return !isEmpty(objects); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * @return true:为空 false:非空 + */ + public static boolean isEmpty(Map map) + { + return isNull(map) || map.isEmpty(); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Map map) + { + return !isEmpty(map); + } + + /** + * * 判断一个字符串是否为空串 + * + * @param str String + * @return true:为空 false:非空 + */ + public static boolean isEmpty(String str) + { + return isNull(str) || NULLSTR.equals(str.trim()); + } + + /** + * * 判断一个字符串是否为非空串 + * + * @param str String + * @return true:非空串 false:空串 + */ + public static boolean isNotEmpty(String str) + { + return !isEmpty(str); + } + + /** + * * 判断一个对象是否为空 + * + * @param object Object + * @return true:为空 false:非空 + */ + public static boolean isNull(Object object) + { + return object == null; + } + + /** + * * 判断一个对象是否非空 + * + * @param object Object + * @return true:非空 false:空 + */ + public static boolean isNotNull(Object object) + { + return !isNull(object); + } + + /** + * * 判断一个对象是否是数组类型(Java基本型别的数组) + * + * @param object 对象 + * @return true:是数组 false:不是数组 + */ + public static boolean isArray(Object object) + { + return isNotNull(object) && object.getClass().isArray(); + } + + /** + * 去空格 + */ + public static String trim(String str) + { + return (str == null ? "" : str.trim()); + } + + /** + * 替换指定字符串的指定区间内字符为"*" + * + * @param str 字符串 + * @param startInclude 开始位置(包含) + * @param endExclude 结束位置(不包含) + * @return 替换后的字符串 + */ + public static String hide(CharSequence str, int startInclude, int endExclude) + { + if (isEmpty(str)) + { + return NULLSTR; + } + final int strLength = str.length(); + if (startInclude > strLength) + { + return NULLSTR; + } + if (endExclude > strLength) + { + endExclude = strLength; + } + if (startInclude > endExclude) + { + // 如果起始位置大于结束位置,不替换 + return NULLSTR; + } + final char[] chars = new char[strLength]; + for (int i = 0; i < strLength; i++) + { + if (i >= startInclude && i < endExclude) + { + chars[i] = ASTERISK; + } + else + { + chars[i] = str.charAt(i); + } + } + return new String(chars); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @return 结果 + */ + public static String substring(final String str, int start) + { + if (str == null) + { + return NULLSTR; + } + + if (start < 0) + { + start = str.length() + start; + } + + if (start < 0) + { + start = 0; + } + if (start > str.length()) + { + return NULLSTR; + } + + return str.substring(start); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @param end 结束 + * @return 结果 + */ + public static String substring(final String str, int start, int end) + { + if (str == null) + { + return NULLSTR; + } + + if (end < 0) + { + end = str.length() + end; + } + if (start < 0) + { + start = str.length() + start; + } + + if (end > str.length()) + { + end = str.length(); + } + + if (start > end) + { + return NULLSTR; + } + + if (start < 0) + { + start = 0; + } + if (end < 0) + { + end = 0; + } + + return str.substring(start, end); + } + + /** + * 在字符串中查找第一个出现的 `open` 和最后一个出现的 `close` 之间的子字符串 + * + * @param str 要截取的字符串 + * @param open 起始字符串 + * @param close 结束字符串 + * @return 截取结果 + */ + public static String substringBetweenLast(final String str, final String open, final String close) + { + if (isEmpty(str) || isEmpty(open) || isEmpty(close)) + { + return NULLSTR; + } + final int start = str.indexOf(open); + if (start != INDEX_NOT_FOUND) + { + final int end = str.lastIndexOf(close); + if (end != INDEX_NOT_FOUND) + { + return str.substring(start + open.length(), end); + } + } + return NULLSTR; + } + + /** + * 判断是否为空,并且不是空白字符 + * + * @param str 要判断的value + * @return 结果 + */ + public static boolean hasText(String str) + { + return (str != null && !str.isEmpty() && containsText(str)); + } + + private static boolean containsText(CharSequence str) + { + int strLen = str.length(); + for (int i = 0; i < strLen; i++) + { + if (!Character.isWhitespace(str.charAt(i))) + { + return true; + } + } + return false; + } + + /** + * 字符串转set + * + * @param str 字符串 + * @param sep 分隔符 + * @return set集合 + */ + public static final Set str2Set(String str, String sep) + { + return new HashSet(str2List(str, sep, true, false)); + } + + /** + * 字符串转list + * + * @param str 字符串 + * @param sep 分隔符 + * @return list集合 + */ + public static final List str2List(String str, String sep) + { + return str2List(str, sep, true, false); + } + + /** + * 字符串转list + * + * @param str 字符串 + * @param sep 分隔符 + * @param filterBlank 过滤纯空白 + * @param trim 去掉首尾空白 + * @return list集合 + */ + public static final List str2List(String str, String sep, boolean filterBlank, boolean trim) + { + List list = new ArrayList(); + if (StringUtils.isEmpty(str)) + { + return list; + } + + // 过滤空白字符串 + if (filterBlank && StringUtils.isBlank(str)) + { + return list; + } + String[] split = str.split(sep); + for (String string : split) + { + if (filterBlank && StringUtils.isBlank(string)) + { + continue; + } + if (trim) + { + string = string.trim(); + } + list.add(string); + } + + return list; + } + + /** + * 判断给定的collection列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value + * + * @param collection 给定的集合 + * @param array 给定的数组 + * @return boolean 结果 + */ + public static boolean containsAny(Collection collection, String... array) + { + if (isEmpty(collection) || isEmpty(array)) + { + return false; + } + else + { + for (String str : array) + { + if (collection.contains(str)) + { + return true; + } + } + return false; + } + } + + /** + * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写 + * + * @param cs 指定字符串 + * @param searchCharSequences 需要检查的字符串数组 + * @return 是否包含任意一个字符串 + */ + public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) + { + if (isEmpty(cs) || isEmpty(searchCharSequences)) + { + return false; + } + for (CharSequence testStr : searchCharSequences) + { + if (containsIgnoreCase(cs, testStr)) + { + return true; + } + } + return false; + } + + /** + * 驼峰转下划线命名 + */ + public static String toUnderScoreCase(String str) + { + if (str == null) + { + return null; + } + StringBuilder sb = new StringBuilder(); + // 前置字符是否大写 + boolean preCharIsUpperCase = true; + // 当前字符是否大写 + boolean curreCharIsUpperCase = true; + // 下一字符是否大写 + boolean nexteCharIsUpperCase = true; + for (int i = 0; i < str.length(); i++) + { + char c = str.charAt(i); + if (i > 0) + { + preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1)); + } + else + { + preCharIsUpperCase = false; + } + + curreCharIsUpperCase = Character.isUpperCase(c); + + if (i < (str.length() - 1)) + { + nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1)); + } + + if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) + { + sb.append(SEPARATOR); + } + else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) + { + sb.append(SEPARATOR); + } + sb.append(Character.toLowerCase(c)); + } + + return sb.toString(); + } + + /** + * 是否包含字符串 + * + * @param str 验证字符串 + * @param strs 字符串组 + * @return 包含返回true + */ + public static boolean inStringIgnoreCase(String str, String... strs) + { + if (str != null && strs != null) + { + for (String s : strs) + { + if (str.equalsIgnoreCase(trim(s))) + { + return true; + } + } + } + return false; + } + + /** + * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld + * + * @param name 转换前的下划线大写方式命名的字符串 + * @return 转换后的驼峰式命名的字符串 + */ + public static String convertToCamelCase(String name) + { + StringBuilder result = new StringBuilder(); + // 快速检查 + if (name == null || name.isEmpty()) + { + // 没必要转换 + return ""; + } + else if (!name.contains("_")) + { + // 不含下划线,仅将首字母大写 + return name.substring(0, 1).toUpperCase() + name.substring(1); + } + // 用下划线将原始字符串分割 + String[] camels = name.split("_"); + for (String camel : camels) + { + // 跳过原始字符串中开头、结尾的下换线或双重下划线 + if (camel.isEmpty()) + { + continue; + } + // 首字母大写 + result.append(camel.substring(0, 1).toUpperCase()); + result.append(camel.substring(1).toLowerCase()); + } + return result.toString(); + } + + /** + * 驼峰式命名法 + * 例如:user_name->userName + */ + public static String toCamelCase(String s) + { + if (s == null) + { + return null; + } + if (s.indexOf(SEPARATOR) == -1) + { + return s; + } + s = s.toLowerCase(); + StringBuilder sb = new StringBuilder(s.length()); + boolean upperCase = false; + for (int i = 0; i < s.length(); i++) + { + char c = s.charAt(i); + + if (c == SEPARATOR) + { + upperCase = true; + } + else if (upperCase) + { + sb.append(Character.toUpperCase(c)); + upperCase = false; + } + else + { + sb.append(c); + } + } + return sb.toString(); + } + + /** + * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串 + * + * @param str 指定字符串 + * @param strs 需要检查的字符串数组 + * @return 是否匹配 + */ + public static boolean matches(String str, List strs) + { + if (isEmpty(str) || isEmpty(strs)) + { + return false; + } + for (String pattern : strs) + { + if (isMatch(pattern, str)) + { + return true; + } + } + return false; + } + + /** + * 判断url是否与规则配置: + * ? 表示单个字符; + * * 表示一层路径内的任意字符串,不可跨层级; + * ** 表示任意层路径; + * + * @param pattern 匹配规则 + * @param url 需要匹配的url + * @return + */ + public static boolean isMatch(String pattern, String url) + { + AntPathMatcher matcher = new AntPathMatcher(); + return matcher.match(pattern, url); + } + + @SuppressWarnings("unchecked") + public static T cast(Object obj) + { + return (T) obj; + } + + /** + * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。 + * + * @param num 数字对象 + * @param size 字符串指定长度 + * @return 返回数字的字符串格式,该字符串为指定长度。 + */ + public static final String padl(final Number num, final int size) + { + return padl(num.toString(), size, '0'); + } + + /** + * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。 + * + * @param s 原始字符串 + * @param size 字符串指定长度 + * @param c 用于补齐的字符 + * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。 + */ + public static final String padl(final String s, final int size, final char c) + { + final StringBuilder sb = new StringBuilder(size); + if (s != null) + { + final int len = s.length(); + if (s.length() <= size) + { + for (int i = size - len; i > 0; i--) + { + sb.append(c); + } + sb.append(s); + } + else + { + return s.substring(len - size, len); + } + } + else + { + for (int i = size; i > 0; i--) + { + sb.append(c); + } + } + return sb.toString(); + } +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index debe341..f229c96 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -5,7 +5,11 @@ server: # 开发环境配置 spring: config: - import: classpath:config/database/application-database-dev.yml + import: + - classpath:config/database/application-database-dev.yml + - classpath:config/qgis/application-qgis-dev.yml + - classpath:config/customize/application-customize-dev.yml + # redis data: @@ -15,7 +19,6 @@ spring: password: zhangsan database: 0 connect-timeout: 3000ms - # 日志配置 logging: level: @@ -43,15 +46,17 @@ safety: - /druid - /websocket/info - /websocket/** + - /open/** # 请求无需解密的路径 no-decrypt-paths: - /crypto/sm2/public-key - /druid - /websocket/info - /websocket/** + - /open/** # 算法服务器配置 algorithm: server: # 开发环境算法服务器地址 - url: http://localhost:8082 \ No newline at end of file + url: http://localhost:8082 diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index c0d816b..f1b296e 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -5,7 +5,10 @@ server: # 生产环境配置 spring: config: - import: classpath:config/database/application-database-prod.yml + import: + - classpath:config/database/application-database-prod.yml + - classpath:config/qgis/application-qgis-prod.yml + - classpath:config/customize/application-customize-prod.yml # redis data: @@ -51,4 +54,4 @@ safety: algorithm: server: # 生产环境算法服务器地址 - url: http://localhost:8081 \ No newline at end of file + url: http://localhost:8081 diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index e5ff90b..dc6cdec 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -5,9 +5,11 @@ spring: config: import: classpath:config/redis/redis-key.yml -# MyBatis 配置 -mybatis: +# MyBatis-plus 配置 +mybatis-plus: mapper-locations: classpath:com/gis/xian/mapper/*.xml type-aliases-package: com.gis.xian.entity configuration: - map-underscore-to-camel-case: true \ No newline at end of file + map-underscore-to-camel-case: true + # 添加 TypeHandler 扫描包路径 + type-handlers-package: com.gis.xian.handler diff --git a/src/main/resources/config/customize/application-customize-dev.yml b/src/main/resources/config/customize/application-customize-dev.yml new file mode 100644 index 0000000..f4b6b4b --- /dev/null +++ b/src/main/resources/config/customize/application-customize-dev.yml @@ -0,0 +1,8 @@ +# qgis配置 +qgis: + # 地震专题图模板路径 + eq-maps-template-path: D:/代码/xian_api_new/src/main/resources/template/qgis-template/eq/ + # 暴雨专题图模板路径 + rain-maps-template-path: D:/代码/xian_api_new/src/main/resources/template/qgis-template/rain/ + # QGIS基础路径 + base-path: F:/files/xian/maps/ diff --git a/src/main/resources/config/customize/application-customize-prod.yml b/src/main/resources/config/customize/application-customize-prod.yml new file mode 100644 index 0000000..f4b6b4b --- /dev/null +++ b/src/main/resources/config/customize/application-customize-prod.yml @@ -0,0 +1,8 @@ +# qgis配置 +qgis: + # 地震专题图模板路径 + eq-maps-template-path: D:/代码/xian_api_new/src/main/resources/template/qgis-template/eq/ + # 暴雨专题图模板路径 + rain-maps-template-path: D:/代码/xian_api_new/src/main/resources/template/qgis-template/rain/ + # QGIS基础路径 + base-path: F:/files/xian/maps/ diff --git a/src/main/resources/config/database/application-database-dev.yml b/src/main/resources/config/database/application-database-dev.yml index 06f319f..e593c88 100644 --- a/src/main/resources/config/database/application-database-dev.yml +++ b/src/main/resources/config/database/application-database-dev.yml @@ -35,4 +35,10 @@ spring: url: jdbc:postgresql://47.92.216.173:7654/assess_disaster?characterEncoding=utf8&TimeZone=Asia/Shanghai username: postgres password: zhangsan - driver-class-name: org.postgresql.Driver \ No newline at end of file + driver-class-name: org.postgresql.Driver + + slave1: + url: jdbc:postgresql://47.92.216.173:7654/yjzyk_xian?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai&postgis=true + username: postgres + password: zhangsan + driver-class-name: org.postgresql.Driver diff --git a/src/main/resources/config/database/application-database-prod.yml b/src/main/resources/config/database/application-database-prod.yml index 04c4a33..3385672 100644 --- a/src/main/resources/config/database/application-database-prod.yml +++ b/src/main/resources/config/database/application-database-prod.yml @@ -32,4 +32,10 @@ spring: url: jdbc:postgresql://10.22.245.138:54321/xianDCAccess?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai username: zaihailian password: XAYJ@gis2603 - driver-class-name: org.postgresql.Driver \ No newline at end of file + driver-class-name: org.postgresql.Driver + + slave1: + url: jdbc:postgresql://47.92.216.173:7654/yjzyk_xian?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai&postgis=true + username: postgres + password: zhangsan + driver-class-name: org.postgresql.Driver diff --git a/src/main/resources/config/qgis/application-qgis-dev.yml b/src/main/resources/config/qgis/application-qgis-dev.yml new file mode 100644 index 0000000..5a3a06f --- /dev/null +++ b/src/main/resources/config/qgis/application-qgis-dev.yml @@ -0,0 +1,3 @@ +# qgis配置 +qgis: + url: http://localhost:18998/qgis/make/map diff --git a/src/main/resources/config/qgis/application-qgis-prod.yml b/src/main/resources/config/qgis/application-qgis-prod.yml new file mode 100644 index 0000000..5a3a06f --- /dev/null +++ b/src/main/resources/config/qgis/application-qgis-prod.yml @@ -0,0 +1,3 @@ +# qgis配置 +qgis: + url: http://localhost:18998/qgis/make/map