diff --git a/_config/sql/update-tables.sql b/_config/sql/update-tables.sql index f0a00f3c0..e69de29bb 100644 --- a/_config/sql/update-tables.sql +++ b/_config/sql/update-tables.sql @@ -1,41 +0,0 @@ --- ======================================== --- adapay_direct_key_config 直连密钥配置结构调整 --- 开发阶段: Adapay 商户身份由 app_id + apiKey + RSA 私钥承载 --- 无需独立商户号字段, 移除 merchant_no; 新增 sandbox 沙箱环境列与唯一索引 --- ======================================== - --- 1. 删除 Adapay 商户号列(身份由 app_id 承载; 该列旧版仅存商户身份快照, 直接删除) -ALTER TABLE "public"."adapay_direct_key_config" DROP COLUMN IF EXISTS "merchant_no"; - --- 2. 新增沙箱环境列(默认非沙箱, 双环境并存: 同一通道商户同一环境密钥唯一) -ALTER TABLE "public"."adapay_direct_key_config" ADD COLUMN IF NOT EXISTS "sandbox" bool DEFAULT false; -COMMENT ON COLUMN "public"."adapay_direct_key_config"."sandbox" IS '是否沙箱环境'; - --- 3. 唯一索引: 同一通道商户同一环境密钥唯一(存量数据存在重复时建索引会失败, 需先清理) -CREATE UNIQUE INDEX IF NOT EXISTS "uk_adapay_direct_key_config_sandbox" ON "public"."adapay_direct_key_config" USING btree ( - "channel_mch_no" "pg_catalog"."text_ops" ASC NULLS LAST, - "sandbox" "pg_catalog"."bool_ops" ASC NULLS LAST -) WHERE deleted = false; -COMMENT ON INDEX "public"."uk_adapay_direct_key_config_sandbox" IS '同一通道商户同一环境密钥唯一'; - --- ===== pay_trade 增加支付通道冗余字段 ===== --- 冗余自容器 product→channel, 用于按接入通道维度看资金/报表免 JOIN; 与 pay_transfer_trade 口径对齐 -ALTER TABLE "public"."pay_trade" ADD COLUMN "channel" varchar(32) COLLATE "pg_catalog"."default"; -COMMENT ON COLUMN "public"."pay_trade"."channel" IS '支付通道(冗余自容器 product→channel; 创建即终值; 权威在容器 channel)'; - --- 存量数据回填: 从业务容器反向填充 channel --- 普通支付容器(normal) -UPDATE "public"."pay_trade" t -SET "channel" = c."channel" -FROM "public"."pay_normal_order" c -WHERE t."container_id" = c."id" - AND t."trade_type" = 'normal' - AND t."channel" IS NULL; - --- 网关支付容器(gateway) -UPDATE "public"."pay_trade" t -SET "channel" = c."channel" -FROM "public"."pay_gateway_order" c -WHERE t."container_id" = c."id" - AND t."trade_type" = 'gateway' - AND t."channel" IS NULL; diff --git a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/dao/region/CityAdjacentManager.java b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/dao/region/CityAdjacentManager.java new file mode 100644 index 000000000..8ee7460c0 --- /dev/null +++ b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/dao/region/CityAdjacentManager.java @@ -0,0 +1,14 @@ +package cn.daxpay.open.platform.system.dao.region; + +import cn.daxpay.open.platform.common.mybatisplus.impl.BaseManager; +import cn.daxpay.open.platform.system.entity.region.CityAdjacent; +import org.springframework.stereotype.Repository; + +/// # 城市接壤关系 +/// +/// 纯关系表(无审计列), 仅供地理围栏 balanced 策略邻市容错查询; +/// 数据由 update-datas.sql 脚本机器生成导入, 运行期仅提供读取能力。 +@Repository +public class CityAdjacentManager extends BaseManager { + +} diff --git a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/dao/region/CityAdjacentMapper.java b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/dao/region/CityAdjacentMapper.java new file mode 100644 index 000000000..2f9277a50 --- /dev/null +++ b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/dao/region/CityAdjacentMapper.java @@ -0,0 +1,13 @@ +package cn.daxpay.open.platform.system.dao.region; + +import cn.daxpay.open.platform.system.entity.region.CityAdjacent; +import com.github.yulichang.base.MPJBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/// # 城市接壤关系 +/// +/// 纯关系表(无审计列), 仅供地理围栏 balanced 策略邻市容错查询。 +@Mapper +public interface CityAdjacentMapper extends MPJBaseMapper { + +} diff --git a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/entity/region/CityAdjacent.java b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/entity/region/CityAdjacent.java new file mode 100644 index 000000000..fce47c486 --- /dev/null +++ b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/entity/region/CityAdjacent.java @@ -0,0 +1,26 @@ +package cn.daxpay.open.platform.system.entity.region; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/// # 城市接壤关系 +/// +/// 纯关系载体, 供地理围栏 balanced 策略邻市容错查询。 +/// 该表为机器生成的纯导入型字典表(约2000行静态数据, 双向存储), id 用 [IdType#AUTO] 走 DB 自增(bigserial), +/// 不走应用层雪花; 无审计列。 +@Data +@TableName("base_city_adjacent") +public class CityAdjacent { + + /// 主键ID(DB 自增, 纯关系表数据导入专用) + @TableId(type = IdType.AUTO) + private Long id; + + /// 城市编码 + private String cityCode; + + /// 相邻城市编码 + private String adjacentCityCode; +} diff --git a/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/service/region/ChinaRegionAdjacencyService.java b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/service/region/ChinaRegionAdjacencyService.java new file mode 100644 index 000000000..c1db15240 --- /dev/null +++ b/daxpay-platform/daxpay-platform-service/service-system/src/main/java/cn/daxpay/open/platform/system/service/region/ChinaRegionAdjacencyService.java @@ -0,0 +1,65 @@ +package cn.daxpay.open.platform.system.service.region; + +import cn.daxpay.open.platform.system.dao.region.CityAdjacentManager; +import cn.daxpay.open.platform.system.entity.region.CityAdjacent; +import cn.hutool.core.util.StrUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/// # 城市接壤关系服务 +/// +/// 启动后首次访问时一次性把全量接壤关系载入内存(约千级行, 静态数据), 构建城市编码 → 邻市编码集合的多重映射, +/// 供地理围栏 balanced 策略查询邻市。行政数据稳定, 无需运行期失效。 +@Slf4j +@Service +@RequiredArgsConstructor +public class ChinaRegionAdjacencyService { + + private final CityAdjacentManager cityAdjacentManager; + + /// 接壤关系多重映射(惰性加载, 一经加载不再变更) + private volatile Map> adjacencyMap; + + /// 查询指定城市的所有接壤邻市编码 + /// + /// @param cityCode 城市编码(base_city.code, 4位) + /// @return 邻市编码集合, 无记录返回空集 + public Set findAdjacentCityCodes(String cityCode) { + if (StrUtil.isBlank(cityCode)) { + return Set.of(); + } + return getAdjacencyMap().getOrDefault(cityCode, Set.of()); + } + + /// 惰性加载全量接壤关系并构建多重映射(双重检查锁定) + private Map> getAdjacencyMap() { + Map> local = this.adjacencyMap; + if (local != null) { + return local; + } + synchronized (this) { + if (this.adjacencyMap != null) { + return this.adjacencyMap; + } + long start = System.currentTimeMillis(); + Map> map = new HashMap<>(); + for (CityAdjacent row : cityAdjacentManager.findAll()) { + // 双向插入: 数据源可能只存单方向(a,b), 内存映射同时建 a→b 与 b→a, + // 使 findAdjacentCityCodes(cityCode) 对任意一端查询都返回完整邻市集合 + map.computeIfAbsent(row.getCityCode(), k -> new HashSet<>()).add(row.getAdjacentCityCode()); + map.computeIfAbsent(row.getAdjacentCityCode(), k -> new HashSet<>()).add(row.getCityCode()); + } + this.adjacencyMap = map; + log.info("城市接壤关系载入完成: {} 条邻接记录, {} 个城市, 耗时 {}ms", + map.values().stream().mapToInt(Set::size).sum(), + map.size(), System.currentTimeMillis() - start); + return map; + } + } +}