package com.vincent.rsf.openApi.utils; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.vincent.rsf.openApi.entity.app.ApiFunction; import com.vincent.rsf.openApi.entity.app.ApiMap; import com.vincent.rsf.openApi.entity.app.App; import com.vincent.rsf.openApi.service.ApiMapService; import com.vincent.rsf.openApi.service.ApiFunctionService; import com.vincent.rsf.openApi.service.AppService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.tika.utils.StringUtils; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; import java.math.BigDecimal; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 接口字段动态映射工具类 * 支持从数据库加载映射配置,并提供字段转换功能 * * @author vincent * @since 2026-01-04 */ @Slf4j @Component @RequiredArgsConstructor public class ParamsMapUtils { private final AppService appService; private final ApiFunctionService functionService; private final ApiMapService mapService; // 缓存到内存 public static List APPS = new ArrayList<>(); public static List FUNCTIONS = new ArrayList<>(); public static List ATTRIBUTE_MAPS = new ArrayList<>(); /** * 应用完全启动后自动加载配置 * 使用 ApplicationReadyEvent 确保 Spring 容器完全初始化 */ @EventListener(ApplicationReadyEvent.class) public void init() { log.info("=============== 开始加载接口映射配置 ==============="); try { appService.refreshCache(); functionService.refreshCache(); mapService.refreshCache(); log.info("接口映射配置加载完成 - 应用数:{}, 功能数:{}, 映射规则数:{}", APPS.size(), FUNCTIONS.size(), ATTRIBUTE_MAPS.size()); } catch (Exception e) { log.error("接口映射配置加载失败", e); } } /** * 手动刷新所有缓存 */ public void refreshAll() { log.info("手动刷新所有映射缓存..."); appService.refreshCache(); functionService.refreshCache(); mapService.refreshCache(); } /** * 执行字段映射转换 * * @param appId 应用ID * @param funcId 功能ID * @param params 原始参数 * @return 转换后的参数 */ public static JSONObject apiMaps(String appId, String funcId, JSONObject params) { if (params == null || params.isEmpty()) { return params; } // 1、获取映射表 List maps = ATTRIBUTE_MAPS.stream() .filter(map -> map.getAppId().equals(appId) && map.getFuncId().equals(funcId)) .toList(); if (maps.isEmpty()) { log.debug("未找到映射配置 - appId:{}, funcId:{}", appId, funcId); return params; } // 2、构建映射规则Map Map mappingRules = new HashMap<>(); for (ApiMap map : maps) { String sourceAttribute = map.getSourceAttribute(); String targetAttribute = map.getTargetAttribute(); if (!StringUtils.isBlank(sourceAttribute) && !StringUtils.isBlank(targetAttribute)) { mappingRules.put(sourceAttribute, targetAttribute); } } // 3、递归替换所有层级的属性名称 JSONObject result = replaceKeysRecursive(params, mappingRules); return result; } /** * 递归替换JSON所有层级的属性名称 * 支持嵌套对象和数组的深度遍历 * * @param json 原始JSON对象 * @param mappingRules 映射规则 (源字段名 -> 目标字段名) * @return 替换后的JSON对象 */ public static JSONObject replaceKeysRecursive(JSONObject json, Map mappingRules) { if (json == null || json.isEmpty()) { return json; } JSONObject result = new JSONObject(); for (String key : json.keySet()) { Object value = json.get(key); // 确定新的键名(如果有映射规则则使用映射后的名称) String newKey = mappingRules.getOrDefault(key, key); if (value instanceof JSONObject) { // 递归处理嵌套对象 JSONObject nestedResult = replaceKeysRecursive((JSONObject) value, mappingRules); result.put(newKey, nestedResult); log.debug("替换对象字段: {} -> {}", key, newKey); } else if (value instanceof JSONArray) { // 递归处理数组 JSONArray arrayResult = replaceKeysInArray((JSONArray) value, mappingRules); result.put(newKey, arrayResult); log.debug("替换数组字段: {} -> {}", key, newKey); } else { // 普通值直接赋值 Object convertedValue = convertValue(value, newKey); result.put(newKey, convertedValue); if (!key.equals(newKey)) { log.debug("替换字段: {} -> {} (值: {})", key, newKey, convertedValue); } } } return result; } /** * 递归处理JSON数组中的所有元素 * * @param array 原始JSON数组 * @param mappingRules 映射规则 * @return 处理后的JSON数组 */ private static JSONArray replaceKeysInArray(JSONArray array, Map mappingRules) { if (array == null || array.isEmpty()) { return array; } JSONArray result = new JSONArray(); for (int i = 0; i < array.size(); i++) { Object element = array.get(i); if (element instanceof JSONObject) { // 数组元素是对象,递归处理 JSONObject replacedObject = replaceKeysRecursive((JSONObject) element, mappingRules); result.add(replacedObject); } else if (element instanceof JSONArray) { // 数组元素是数组,递归处理 JSONArray replacedArray = replaceKeysInArray((JSONArray) element, mappingRules); result.add(replacedArray); } else { // 基本类型,直接添加 result.add(element); } } return result; } /** * 通用的JSON属性名替换方法(对外提供) * 可以直接传入映射规则进行替换 * * @param json 原始JSON对象 * @param mappingRules 映射规则 Map<源字段名, 目标字段名> * @return 替换后的JSON对象 */ public static JSONObject replaceJsonKeys(JSONObject json, Map mappingRules) { return replaceKeysRecursive(json, mappingRules); } /** * 通用的JSON数组属性名替换方法(对外提供) * * @param array 原始JSON数组 * @param mappingRules 映射规则 Map<源字段名, 目标字段名> * @return 替换后的JSON数组 */ public static JSONArray replaceJsonArrayKeys(JSONArray array, Map mappingRules) { return replaceKeysInArray(array, mappingRules); } /** * 值类型转换(可扩展) * * @param value 原始值 * @param targetField 目标字段名 * @return 转换后的值 */ private static Object convertValue(Object value, String targetField) { if (value == null) { return null; } // 这里可以根据需要添加更多类型转换逻辑 // 例如:日期格式转换、数字精度转换等 // 示例:如果字段名包含amount、price等,转换为BigDecimal String lowerField = targetField.toLowerCase(); if ((lowerField.contains("amount") || lowerField.contains("price") || lowerField.contains("qty")) && value instanceof String) { try { return new BigDecimal(value.toString()); } catch (Exception e) { log.warn("数字转换失败: {} -> {}", value, targetField); return value; } } return value; } /** * 获取应用信息 * * @param appId 应用ID * @return 应用信息 */ public static App getApp(String appId) { return APPS.stream() .filter(app -> app.getId().equals(appId)) .findFirst() .orElse(null); } /** * 获取功能信息 * * @param funcId 功能ID * @return 功能信息 */ public static ApiFunction getFunction(String funcId) { return FUNCTIONS.stream() .filter(func -> func.getId().equals(funcId)) .findFirst() .orElse(null); } }