增加 StockStrategy 服务,数据表建立及批量更新方法重写。解决多数据源配置 mapper-locations

无法识别并读取到对应数据源的 mapper.xml 及其 statement 方法的问题
This commit is contained in:
2026-01-10 14:26:04 +08:00
parent 52f4dd5e83
commit b0093ccccb
27 changed files with 653 additions and 280 deletions

View File

@@ -0,0 +1,58 @@
package quant.rich.emoney.util;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public final class DateUtils {
// 缓存 formatter行为接近 fastjson2
private static final Map<String, DateTimeFormatter> FORMATTER_CACHE =
new ConcurrentHashMap<>();
private DateUtils() {
}
/**
* 格式化 LocalDateTime
*
* @param dateTime LocalDateTime
* @param pattern 日期格式,如 yyyy-MM-dd HH:mm:ss
* @return 格式化后的字符串
*/
public static String format(LocalDateTime dateTime, String pattern) {
if (dateTime == null) {
return null;
}
if (pattern == null || pattern.isEmpty()) {
throw new IllegalArgumentException("pattern must not be null or empty");
}
DateTimeFormatter formatter =
FORMATTER_CACHE.computeIfAbsent(pattern, DateTimeFormatter::ofPattern);
return dateTime.format(formatter);
}
/**
* 将给定字符串转换成 LocalDate
* @param value 如 "20260101"
* @param pattern 日期格式,如 yyyyMMdd
* @return
*/
public static LocalDate parse(String value, String pattern) {
if (value == null) {
return null;
}
if (pattern == null || pattern.isEmpty()) {
throw new IllegalArgumentException("pattern must not be null or empty");
}
DateTimeFormatter formatter =
FORMATTER_CACHE.computeIfAbsent(pattern, DateTimeFormatter::ofPattern);
LocalDate date = LocalDate.parse(value, formatter);
return date;
}
}

View File

@@ -0,0 +1,90 @@
package quant.rich.emoney.util;
public class StockCodeUtils {
/**
* 从 goodsId 转换为 tsCode
* @param goodsId
* @return
*/
public static String goodsIdToTsCode(Integer goodsId) {
if (goodsId == null || goodsId < 0) {
throw new IllegalArgumentException("goodsId 不能为空或为负数");
}
final String s = goodsId.toString();
final int len = s.length();
switch (len) {
case 6:
// 6 位默认 SH
return s + ".SH";
case 7:
// 7 位默认 SZ 或 BJ
if (!s.startsWith("1")) {
throw new IllegalArgumentException("无法将 goodsId " + s + " 转换为 tsCode");
}
char second = s.charAt(1);
String core = s.substring(1);
if (second == '9') {
return core + ".BJ";
}
return core + ".SZ";
default:
throw new IllegalArgumentException("无法将 goodsId " + s + " 转换为 tsCode");
}
}
/**
* 从 tsCode 转换成 goodsId
* @param tsCode
* @return
*/
public static Integer tsCodeToGoodsId(String tsCode) {
if (tsCode == null || tsCode.isBlank()) {
throw new IllegalArgumentException("tsCode 不能为空");
}
String s = tsCode.trim().toUpperCase();
// 期望格式XXX.SZ / XXX.SH / XXX.BJ
int dot = s.indexOf('.');
if (dot <= 0 || dot == s.length() - 1) {
throw new IllegalArgumentException("tsCode 格式无效: " + tsCode);
}
String code = s.substring(0, dot);
String market = s.substring(dot + 1);
if (!code.matches("\\d+")) {
throw new IllegalArgumentException("tsCode 数字部分无效: " + tsCode);
}
switch (market) {
case "SH":
if (code.length() == 6) {
return Integer.valueOf(code);
}
break;
case "SZ":
if (code.length() == 6) {
return Integer.valueOf("1" + code);
}
break;
case "BJ":
if (code.length() == 6) {
return Integer.valueOf("1" + code);
}
break;
}
throw new IllegalArgumentException("无法将 tsCode " + tsCode + " 转换为 goodsId");
}
}