changed = change(assess, "A3", map.getName());
+ arg.setZoomRule(changed.get("k")); // 默认不缩放
+ arg.setZoomValue(changed.get("v")); // 默认缩放值
+
+ args.add(arg);
+ }
+ log.info("制图参数设置完成!");
+
+ return args;
+ }
+
+
+ /**
+ * 专题图缩放变化:
+ * A3:
+ * 4级地震:不放缩
+ * 5-6级地震:所有A3都放缩到intensity
+ * 6级以上,除了影响场intensity,其他不变
+ *
+ * A4:
+ * 所有都不变
+ */
+ public Map change(EqAssessmentDTO assess, String size, String name) {
+ Map map = new HashMap<>();
+
+ if (size.equals("A3")) {
+ if (assess.getEqMagnitude() >= 4 && assess.getEqMagnitude() < 5) {
+ map.put("k", BaseEnums.NO.getCode().toString());
+ map.put("v", "");
+ return map;
+ }
+ if (assess.getEqMagnitude() >= 5 && assess.getEqMagnitude() < 6) {
+ map.put("k", BaseEnums.M_LAYER2.getCode().toString());
+ map.put("v", "intensity");
+ return map;
+ }
+ if (assess.getEqMagnitude() >= 6) {
+ if (name.equals("地震影响估计范围分布图")) {
+ map.put("k", BaseEnums.M_LAYER2.getCode().toString());
+ map.put("v", "intensity");
+ return map;
+ } else {
+ map.put("k", BaseEnums.NO.getCode().toString());
+ map.put("v", "");
+ return map;
+ }
+ }
+ }
+ map.put("k", BaseEnums.NO.getCode().toString());
+ map.put("v", "");
+ return map;
+ }
+
+}
diff --git a/src/main/java/com/gis/xian/service/pub/impl/FeignServiceImpl.java b/src/main/java/com/gis/xian/service/pub/impl/FeignServiceImpl.java
new file mode 100644
index 0000000..7308d78
--- /dev/null
+++ b/src/main/java/com/gis/xian/service/pub/impl/FeignServiceImpl.java
@@ -0,0 +1,97 @@
+package com.gis.xian.service.pub.impl;
+
+import com.alibaba.fastjson2.JSON;
+import com.gis.xian.config.DataSource;
+import com.gis.xian.constant.BaseConstants;
+import com.gis.xian.core.rabbitmq.DlqOperate;
+import com.gis.xian.enums.BaseEnums;
+import com.gis.xian.params.QgisArgs;
+import com.gis.xian.service.ex.ParmaException;
+import com.gis.xian.service.pub.IDZEqQueueService;
+import com.gis.xian.service.pub.IFeignService;
+import com.gis.xian.utils.BaseUtils;
+import com.gis.xian.utils.http.HttpRestClient;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * @author zzw
+ * @description: 三方服务接口
+ * @date 2026/5/26 上午11:33
+ */
+@Slf4j
+@Service
+@DataSource("slave1")
+public class FeignServiceImpl implements IFeignService {
+
+
+ @Resource
+ private HttpRestClient restclient;
+ @Resource
+ private RabbitTemplate rabbitTemplate;
+ @Autowired
+ @Lazy
+ private IDZEqQueueService idzEqQueueService;
+ @Autowired
+ private DlqOperate dlqOperate;
+
+ // 调用制图服务
+ @Override
+ public void invoke(List args) {
+ // 进度
+ double p = 0;
+ // 异常参数
+ if (args == null || args.size() == 0) {
+ throw new ParmaException(BaseConstants.PARAMS_ERROR);
+ }
+ // 保证尽可能的多产, 这里不要进行任何异常抛出, 只能记录失败的图层
+ try {
+ log.info("开始调用pyqgis服务");
+ for (QgisArgs arg : args) {
+ try { // 处理单个图件
+ // 返回数据格式
+ ParameterizedTypeReference res = new ParameterizedTypeReference() {
+ };
+ // 制图
+ String mapName = restclient.post(BaseConstants.HTTP_QGIS_THEMATIC, JSON.toJSON(arg), res);
+ // 专题图名称
+ if (mapName == null || mapName.equals("")) {
+ log.error("产出图件失败!");
+ // 进入死信队列
+ dlqOperate.sendToDlq(arg, "产出图件返回空", null);
+ }
+ log.info("图件产品:{}-产出成功", mapName);
+ // 这里可能会出现线程抢占, 需要加入乐观锁
+ if (arg.getDisaster() == BaseConstants.EQ_DISASTER_MAP) {
+ // 更新状态 专题图是第二部分产品 保证进度同步 所以需要 num+1 处理
+ p = BaseUtils.compute(arg.getId() + 1, 0);
+ }
+ if (arg.getDisaster() == BaseConstants.RAIN_DISASTER_MAP) {
+ p = BaseUtils.compute(arg.getId() + 1, 1);
+ }
+ idzEqQueueService.updated(arg.getEvent(), arg.getQueueId(), p, BaseEnums.CALCULATING.getCode());
+ // 推送消息
+ rabbitTemplate.convertAndSend(BaseConstants.ASSESS_EXCHANGE, BaseConstants.MAPS_QUEUE, arg);
+ } catch (Exception ex) {
+ log.error("制图服务出现错误,请检查服务问题! {}", ex.getMessage());
+ // 进入死信队列
+ dlqOperate.sendToDlq(arg, "图层处理异常", ex);
+ }
+ }
+ } catch (Exception e) {
+ log.error("制图服务出现错误,请检查服务问题! {}", e.getMessage(), e);
+ // 批量失败时,将所有未处理的参数投递到死信
+ for (QgisArgs arg : args) {
+ // 进入死信队列
+ dlqOperate.sendToDlq(arg, "制图服务整体异常", e);
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/gis/xian/task/InitializeData.java b/src/main/java/com/gis/xian/task/InitializeData.java
index d7b6554..3ac5107 100644
--- a/src/main/java/com/gis/xian/task/InitializeData.java
+++ b/src/main/java/com/gis/xian/task/InitializeData.java
@@ -17,7 +17,7 @@ import java.util.concurrent.CompletableFuture;
/**
* 初始化数据
*/
-@Component
+//@Component
@Slf4j
public class InitializeData {
@@ -107,7 +107,7 @@ public class InitializeData {
public void init() {
log.info("开始初始化数据");
// 并行执行所有数据库查询和Redis写入
-
+
// 隐患点 - 总体
CompletableFuture allFuture = CompletableFuture.runAsync(() -> {
redisTemplate.opsForValue().set(allBasePointsKey, JSON.toJSONString(
@@ -117,7 +117,7 @@ public class InitializeData {
);
log.info("加载隐患点信息(总体)并写入redis完成");
});
-
+
// 隐患点 - 滑坡
CompletableFuture landslideFuture = CompletableFuture.runAsync(() -> {
redisTemplate.opsForValue().set(landslideKey, JSON.toJSONString(
@@ -127,7 +127,7 @@ public class InitializeData {
);
log.info("加载隐患点信息(滑坡)并写入redis完成");
});
-
+
// 隐患点 - 泥石流
CompletableFuture debrisFlowFuture = CompletableFuture.runAsync(() -> {
redisTemplate.opsForValue().set(debrisFlowKey, JSON.toJSONString(
@@ -137,7 +137,7 @@ public class InitializeData {
);
log.info("加载隐患点信息(泥石流)并写入redis完成");
});
-
+
// 隐患点 - 山洪
CompletableFuture flashFloodFuture = CompletableFuture.runAsync(() -> {
redisTemplate.opsForValue().set(flashFloodKey, JSON.toJSONString(
@@ -147,7 +147,7 @@ public class InitializeData {
);
log.info("加载隐患点信息(山洪)并写入redis完成");
});
-
+
// 隐患点 - 内涝
CompletableFuture waterLoggingFuture = CompletableFuture.runAsync(() -> {
redisTemplate.opsForValue().set(waterLoggingKey, JSON.toJSONString(
diff --git a/src/main/java/com/gis/xian/utils/BaseUtils.java b/src/main/java/com/gis/xian/utils/BaseUtils.java
new file mode 100644
index 0000000..077107f
--- /dev/null
+++ b/src/main/java/com/gis/xian/utils/BaseUtils.java
@@ -0,0 +1,70 @@
+package com.gis.xian.utils;
+
+import com.gis.xian.constant.BaseConstants;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * @author zzw
+ * @description: 基本工具类
+ * @date 2026/5/25 下午6:06
+ */
+public class BaseUtils {
+ // 生成一个带时间戳的地震编码
+ public static String generationCode(LocalDateTime time) {
+
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
+ String timestamp = time.format(formatter);
+ String code = "T" + timestamp + BaseConstants.CITY_CODE;
+
+ return code;
+ }
+
+ // 格式化时间
+ public static String formatTime(LocalDateTime time, boolean top) {
+ // true 三要素 false 落款)
+ if (top) {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时 mm分");
+ String timestamp = time.format(formatter);
+ return timestamp;
+ }
+
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
+ String timestamp = time.format(formatter);
+ return timestamp;
+
+ }
+
+ // 首次生成批次编码
+ public static String generationBatchCode(String code) {
+ return code + "01";
+ }
+
+ // 修改地震批次编码
+ public static String editBatchCode(String code) {
+
+ String prefix = code.substring(0, code.length() - 2);
+ String last = code.substring(code.length() - 2);
+ return prefix + Integer.parseInt(last) + 1;
+ }
+
+ // 生成一个带时间戳的暴雨编码
+ public static String generationRainCode(LocalDateTime time) {
+
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
+ String timestamp = time.format(formatter);
+ String code = "R" + timestamp + BaseConstants.CITY_CODE;
+
+ return code;
+ }
+
+ // 计算评估进度 0:地震、1:暴雨
+ public static double compute(int finish, int type) {
+ if (type == 0) {
+ return (double) finish / BaseConstants.EQ_TOTAL_FILES * 100;
+ }
+ return (double) finish / BaseConstants.RAIN_TOTAL_FILES * 100;
+ }
+
+}
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/java/com/gis/xian/utils/http/HttpRestClient.java b/src/main/java/com/gis/xian/utils/http/HttpRestClient.java
new file mode 100644
index 0000000..522f8fe
--- /dev/null
+++ b/src/main/java/com/gis/xian/utils/http/HttpRestClient.java
@@ -0,0 +1,198 @@
+package com.gis.xian.utils.http;
+
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.*;
+import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.Map;
+
+/**
+ * @author zzw
+ * @description: 第三方请求工具类
+ * @date 2026/5/26 下午4:27
+ */
+public class HttpRestClient {
+
+ private final RestTemplate restTemplate;
+
+ // 默认的请求头设置
+ private static final HttpHeaders defaultHeaders;
+
+ static {
+ defaultHeaders = new HttpHeaders();
+ defaultHeaders.setContentType(MediaType.APPLICATION_JSON);
+ defaultHeaders.add("Accept", "application/json");
+ }
+
+ /**
+ * 构造方法,注入RestTemplate
+ * @param restTemplate RestTemplate实例
+ */
+ public HttpRestClient(RestTemplate restTemplate) {
+ this.restTemplate = restTemplate;
+ }
+
+ /**
+ * 不带Token的GET请求
+ * @param url 请求URL
+ * @param responseType 响应数据类型
+ * @param 响应数据泛型
+ * @return 响应结果
+ */
+ public T get(String url, ParameterizedTypeReference responseType) {
+ return get(url, null, responseType);
+ }
+
+ /**
+ * 带Token的GET请求
+ * @param url 请求URL
+ * @param token 认证Token
+ * @param responseType 响应数据类型
+ * @param 响应数据泛型
+ * @return 响应结果
+ */
+ public T get(String url, String token, ParameterizedTypeReference responseType) {
+ HttpEntity> entity = createHttpEntity(token, null);
+ ResponseEntity response = restTemplate.exchange(
+ url,
+ HttpMethod.GET,
+ entity,
+ responseType
+ );
+ return response.getBody();
+ }
+
+ /**
+ * 带路径参数和Token的GET请求
+ * @param url 请求URL,包含路径参数占位符,如"/user/{id}"
+ * @param token 认证Token
+ * @param uriVariables 路径参数键值对
+ * @param responseType 响应数据类型
+ * @param 响应数据泛型
+ * @return 响应结果
+ */
+ public T get(String url, String token, Map uriVariables, ParameterizedTypeReference responseType) {
+ HttpEntity> entity = createHttpEntity(token, null);
+ ResponseEntity response = restTemplate.exchange(
+ url,
+ HttpMethod.GET,
+ entity,
+ responseType,
+ uriVariables
+ );
+ return response.getBody();
+ }
+
+ /**
+ * 不带Token的POST请求
+ * @param url 请求URL
+ * @param requestBody 请求体
+ * @param responseType 响应数据类型
+ * @param 响应数据泛型
+ * @param 请求体泛型
+ * @return 响应结果
+ */
+ public T post(String url, R requestBody, ParameterizedTypeReference responseType) {
+ return post(url, null, requestBody, responseType);
+ }
+
+ /**
+ * 带Token的POST请求
+ * @param url 请求URL
+ * @param token 认证Token
+ * @param requestBody 请求体
+ * @param responseType 响应数据类型
+ * @param 响应数据泛型
+ * @param 请求体泛型
+ * @return 响应结果
+ */
+ public T post(String url, String token, R requestBody, ParameterizedTypeReference responseType) {
+ HttpEntity entity = createHttpEntity(token, requestBody);
+ ResponseEntity response = restTemplate.exchange(
+ url,
+ HttpMethod.POST,
+ entity,
+ responseType
+ );
+ return response.getBody();
+ }
+
+ /**
+ * 带Token的PUT请求
+ * @param url 请求URL
+ * @param token 认证Token
+ * @param requestBody 请求体
+ * @param responseType 响应数据类型
+ * @param 响应数据泛型
+ * @param 请求体泛型
+ * @return 响应结果
+ */
+ public T put(String url, String token, R requestBody, ParameterizedTypeReference responseType) {
+ HttpEntity entity = createHttpEntity(token, requestBody);
+ ResponseEntity response = restTemplate.exchange(
+ url,
+ HttpMethod.PUT,
+ entity,
+ responseType
+ );
+ return response.getBody();
+ }
+
+ /**
+ * 带Token的DELETE请求
+ * @param url 请求URL
+ * @param token 认证Token
+ * @param responseType 响应数据类型
+ * @param 响应数据泛型
+ * @return 响应结果
+ */
+ public T delete(String url, String token, ParameterizedTypeReference responseType) {
+ HttpEntity> entity = createHttpEntity(token, null);
+ ResponseEntity response = restTemplate.exchange(
+ url,
+ HttpMethod.DELETE,
+ entity,
+ responseType
+ );
+ return response.getBody();
+ }
+
+ /**
+ * 创建HTTP请求实体,包含 headers 和 body
+ * @param token 认证Token,可为null
+ * @param body 请求体,可为null
+ * @param 请求体泛型
+ * @return HttpEntity实例
+ */
+ private HttpEntity createHttpEntity(String token, R body) {
+ HttpHeaders headers = new HttpHeaders();
+ // 复制默认请求头
+ headers.putAll(defaultHeaders);
+
+ // 如果有token,添加到请求头
+ if (token != null && !token.trim().isEmpty()) {
+ headers.add("Authorization", "Bearer " + token);
+ }
+
+ return new HttpEntity<>(body, headers);
+ }
+
+ /**
+ * 添加默认请求头
+ * @param name 头名称
+ * @param value 头值
+ */
+ public void addDefaultHeader(String name, String value) {
+ defaultHeaders.add(name, value);
+ }
+
+ /**
+ * 移除默认请求头
+ * @param name 头名称
+ */
+ public void removeDefaultHeader(String name) {
+ defaultHeaders.remove(name);
+ }
+}
+
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
index debe341..3e0c421 100644
--- a/src/main/resources/application-dev.yml
+++ b/src/main/resources/application-dev.yml
@@ -7,6 +7,7 @@ spring:
config:
import: classpath:config/database/application-database-dev.yml
+
# redis
data:
redis:
@@ -15,7 +16,31 @@ spring:
password: zhangsan
database: 0
connect-timeout: 3000ms
-
+ # rabbitmq 配置
+ rabbitmq:
+ host: localhost
+ port: 5672
+ # username: xiaodemo
+ # password: 1234
+ username: zzw
+ password: zzw0401
+ #虚拟host 可以不设置,使用server默认host
+ # virtual-host: /xiaodemos
+ #消息确认配置项
+ publisher-returns: true #确认消息已发送到队列(Queue)
+ publisher-confirm-type: correlated
+ listener: # 手动确认消息配置
+ simple:
+ acknowledge-mode: manual # 全局开启消费者手动确认
+ # 优化配置
+ concurrency: 1 # 最小消费者数
+ max-concurrency: 5 # 最大消费者数
+ prefetch: 1 # 每次从队列取1条消息,处理完再取(避免消息堆积)
+ retry:
+ enabled: true # 开启消费者重试(建议结合业务幂等性使用)
+ max-attempts: 3 # 最大重试次数
+ initial-interval: 1000ms # 第一次重试间隔
+ multiplier: 2 # 重试间隔倍数(第二次2s,第三次4s)
# 日志配置
logging:
level:
@@ -43,15 +68,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..ae5c61f 100644
--- a/src/main/resources/application-prod.yml
+++ b/src/main/resources/application-prod.yml
@@ -15,6 +15,31 @@ spring:
password: XAYJ@gis2603
database: 0
connect-timeout: 3000ms
+ # rabbitmq 配置
+ rabbitmq:
+ host: localhost
+ port: 5672
+ # username: xiaodemo
+ # password: 1234
+ username: zzw
+ password: zzw0401
+ #虚拟host 可以不设置,使用server默认host
+ # virtual-host: /xiaodemos
+ #消息确认配置项
+ publisher-returns: true #确认消息已发送到队列(Queue)
+ publisher-confirm-type: correlated
+ listener: # 手动确认消息配置
+ simple:
+ acknowledge-mode: manual # 全局开启消费者手动确认
+ # 优化配置
+ concurrency: 1 # 最小消费者数
+ max-concurrency: 5 # 最大消费者数
+ prefetch: 1 # 每次从队列取1条消息,处理完再取(避免消息堆积)
+ retry:
+ enabled: true # 开启消费者重试(建议结合业务幂等性使用)
+ max-attempts: 3 # 最大重试次数
+ initial-interval: 1000ms # 第一次重试间隔
+ multiplier: 2 # 重试间隔倍数(第二次2s,第三次4s)
# 日志配置
logging:
@@ -51,4 +76,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 802ba4d..6e8e31a 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:mapper/*.xml
- type-aliases-package: com.gis.basic_template_not_login_back.entity
+ 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/database/application-database-dev.yml b/src/main/resources/config/database/application-database-dev.yml
index 06f319f..1bbbaa7 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://localhost:5432/yjzyk_xian?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai&postgis=true
+ username: postgres
+ password: Zzw.0401
+ 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..bdc9c4b 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://localhost:5432/yjzyk_xian?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai&postgis=true
+ username: postgres
+ password: Zzw.0401
+ driver-class-name: org.postgresql.Driver