package jnpf.bizcommon.audit.service.value; import com.alibaba.fastjson.JSON; import jnpf.bizcommon.audit.entity.model.AuditFieldDiffViewVO; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.annotation.Order; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; /** * 按 JNPF dictionaryType 批量解析字典项;一次详情至多执行一条字典查询。 * *

{@code @Order} 的含义见 {@link LimsQingyandanFieldValueResolver} 的类注释。 */ @Slf4j @Component @Order(JnpfDictionaryFieldValueResolver.ORDER) public class JnpfDictionaryFieldValueResolver implements AuditFieldValueResolver { /** 平台通用解析器,排在表专用(100)之后、用户兜底(300)之前。 */ public static final int ORDER = 200; private static final String UNAVAILABLE = "名称暂不可用"; private final DictionaryLookup lookup; @Autowired public JnpfDictionaryFieldValueResolver(JdbcTemplate jdbcTemplate) { this(new JdbcDictionaryLookup(jdbcTemplate)); } JnpfDictionaryFieldValueResolver(DictionaryLookup lookup) { this.lookup = lookup; } @Override public boolean supports(String targetTable) { return true; } @Override public void resolve(AuditFieldValueContext context, List fields) { if (context == null || blank(context.getTenantId()) || fields == null || fields.isEmpty() || context.getDictionaryTypes().isEmpty()) { return; } Set dictionaryTypes = new LinkedHashSet<>(); Set values = new LinkedHashSet<>(); for (AuditFieldDiffViewVO field : fields) { String dictionaryType = dictionaryType(context, field); if (dictionaryType == null || displaysResolved(field)) { continue; } dictionaryTypes.add(dictionaryType); values.addAll(parseValues(field.getOldData())); values.addAll(parseValues(field.getNewData())); } if (dictionaryTypes.isEmpty() || values.isEmpty()) { return; } Map> labels = lookup.labels( context.getTenantId().trim(), dictionaryTypes, values); for (AuditFieldDiffViewVO field : fields) { String dictionaryType = dictionaryType(context, field); Map dictionary = dictionaryType == null ? null : labels.get(dictionaryType); if (dictionary == null) { continue; } boolean multiple = "multiEnum".equals(field.getValueType()) || startsWithArray(field.getOldData()) || startsWithArray(field.getNewData()); if (blank(field.getValueType())) { field.setValueType(multiple ? "multiEnum" : "enum"); } if (field.getOldDisplay() == null) { field.setOldDisplay(display(field.getOldData(), dictionary)); } if (field.getNewDisplay() == null) { field.setNewDisplay(display(field.getNewData(), dictionary)); } } } private static String dictionaryType(AuditFieldValueContext context, AuditFieldDiffViewVO field) { if (field == null || blank(field.getField())) { return null; } return context.getDictionaryTypes().get(field.getField().trim().toLowerCase(Locale.ROOT)); } private static boolean displaysResolved(AuditFieldDiffViewVO field) { return (blank(field.getOldData()) || field.getOldDisplay() != null) && (blank(field.getNewData()) || field.getNewDisplay() != null); } private static String display(String raw, Map labels) { if (blank(raw)) { return ""; } List values = parseValues(raw); if (values.isEmpty()) { return UNAVAILABLE; } List result = new ArrayList<>(values.size()); for (String value : values) { String label = labels.get(value); result.add(blank(label) ? UNAVAILABLE : label); } return String.join("、", result); } @SuppressWarnings("unchecked") private static List parseValues(String raw) { if (blank(raw)) { return Collections.emptyList(); } String trimmed = raw.trim(); if (!trimmed.startsWith("[")) { return Collections.singletonList(trimmed); } try { Object parsed = JSON.parse(trimmed); if (!(parsed instanceof Collection)) { return Collections.emptyList(); } List result = new ArrayList<>(); for (Object value : (Collection) parsed) { if (value != null && !String.valueOf(value).trim().isEmpty()) { result.add(String.valueOf(value).trim()); } } return result; } catch (RuntimeException ignored) { return Collections.emptyList(); } } private static boolean startsWithArray(String raw) { return raw != null && raw.trim().startsWith("["); } private static boolean blank(String value) { return value == null || value.trim().isEmpty(); } interface DictionaryLookup { Map> labels(String tenantId, Set dictionaryTypes, Set values); } /** 表名和列名固定;dictionaryType、值和租户全部参数化。 */ private static final class JdbcDictionaryLookup implements DictionaryLookup { private final NamedParameterJdbcTemplate jdbc; private JdbcDictionaryLookup(JdbcTemplate jdbcTemplate) { this.jdbc = new NamedParameterJdbcTemplate(jdbcTemplate); } @Override public Map> labels( String tenantId, Set dictionaryTypes, Set values) { try { MapSqlParameterSource params = new MapSqlParameterSource() .addValue("dictionaryTypes", dictionaryTypes) .addValue("values", values) .addValue("tenantId", tenantId); List> rows = jdbc.queryForList( "SELECT t.f_id AS type_id, t.f_en_code AS type_code, " + "d.f_id AS data_id, d.f_en_code AS data_code, d.f_full_name AS data_name " + "FROM base_dictionary_data d " + "JOIN base_dictionary_type t ON t.f_id = d.f_dictionary_type_id " + "WHERE (t.f_id IN (:dictionaryTypes) OR t.f_en_code IN (:dictionaryTypes)) " + "AND (d.f_id IN (:values) OR d.f_en_code IN (:values)) " + "AND COALESCE(NULLIF(t.f_tenant_id, ''), '0') = :tenantId " + "AND COALESCE(NULLIF(d.f_tenant_id, ''), '0') = :tenantId " + "AND COALESCE(t.f_delete_mark, 0) = 0 " + "AND COALESCE(d.f_delete_mark, 0) = 0", params); Map> result = new LinkedHashMap<>(); for (Map row : rows) { String typeId = text(row.get("type_id")); String typeCode = text(row.get("type_code")); String dataId = text(row.get("data_id")); String dataCode = text(row.get("data_code")); String label = text(row.get("data_name")); put(result, typeId, dataId, dataCode, label); put(result, typeCode, dataId, dataCode, label); } return result; } catch (RuntimeException ex) { log.warn("[AUDIT-QUERY] JNPF 字典批量解析失败 typeCount={} valueCount={} err={}", dictionaryTypes.size(), values.size(), ex.getClass().getSimpleName()); return Collections.emptyMap(); } } private static void put(Map> result, String type, String dataId, String dataCode, String label) { if (type == null || label == null) { return; } Map values = result.computeIfAbsent(type, key -> new LinkedHashMap<>()); if (dataId != null) { values.put(dataId, label); } if (dataCode != null) { values.put(dataCode, label); } } private static String text(Object value) { if (value == null) { return null; } String result = String.valueOf(value).trim(); return result.isEmpty() ? null : result; } } }