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;
/**
* 请验单字段的显式业务解析器;引用表和展示列均为固定白名单。
*
*
{@code @Order} 不是装饰(2026-08-02 加):解析器按注入顺序串行执行,先写上
* display 的那个说了算(后续解析器一律 {@code if (display == null)} 才动手)。表专用解析器
* 必须排在通用解析器之前,否则 {@code qingyan_ren} 会被
* {@link JnpfUserFieldValueResolver} 按通用格式(realName/account)抢先解析掉,
* 本类为它定的格式(只显示姓名)就永远不生效。Spring 对没标注的 bean 一律按
* {@code LOWEST_PRECEDENCE} 处理、保留扫描序——那是类名字母序的副产品,不该拿它当契约。
*/
@Slf4j
@Component
@Order(LimsQingyandanFieldValueResolver.ORDER)
public class LimsQingyandanFieldValueResolver implements AuditFieldValueResolver {
/** 表专用解析器,最先跑。 */
public static final int ORDER = 100;
private static final String TABLE = "lims_qingyandan";
private static final String UNAVAILABLE = "名称暂不可用";
private static final Map BIZ_STATUS = labels(
"created", "已创建", "confirmed", "已提交", "pending_fetch", "待取样",
"deleted", "已废弃", "cancelled", "已撤回", "fetched", "已取样",
"pending_receive", "待接样", "received", "已接样", "rejected", "已退回");
private static final Map BIZ_TYPE = labels("CP", "成品");
private static final Map JIANCE_FANGSHI = labels(
"quanjian", "全检", "bufenjian", "部分检", "mianjian", "免检");
private final ReferenceLookup lookup;
@Autowired
public LimsQingyandanFieldValueResolver(JdbcTemplate jdbcTemplate) {
this(new JdbcReferenceLookup(jdbcTemplate));
}
LimsQingyandanFieldValueResolver(ReferenceLookup lookup) {
this.lookup = lookup;
}
@Override
public boolean supports(String targetTable) {
return targetTable != null && TABLE.equals(targetTable.trim().toLowerCase(Locale.ROOT));
}
@Override
public void resolve(AuditFieldValueContext context, List fields) {
if (fields == null || fields.isEmpty()) {
return;
}
applyBusinessEnums(fields);
Set projectIds = new LinkedHashSet<>();
Set standardIds = new LinkedHashSet<>();
Set userIds = new LinkedHashSet<>();
Set productIds = new LinkedHashSet<>();
for (AuditFieldDiffViewVO field : fields) {
String name = normalized(field.getField());
if ("jiance_xiangmu".equals(name)) {
setMissingSemantics(field, "multiReference", "checkbox");
collect(projectIds, field.getOldData());
collect(projectIds, field.getNewData());
} else if ("jianyan_biaozhun".equals(name)) {
setMissingSemantics(field, "reference", "select");
collect(standardIds, field.getOldData());
collect(standardIds, field.getNewData());
} else if ("qingyan_ren".equals(name)) {
setMissingSemantics(field, "reference", "userSelect");
collect(userIds, field.getOldData());
collect(userIds, field.getNewData());
} else if ("chanpin_id".equals(name)) {
setMissingSemantics(field, "reference", "popupSelect");
field.setTechnical(Boolean.TRUE);
collect(productIds, field.getOldData());
collect(productIds, field.getNewData());
}
}
if (context == null || blank(context.getTenantId())) {
return;
}
String tenantId = context.getTenantId().trim();
Map projects = projectIds.isEmpty()
? Collections.emptyMap() : lookup.projects(tenantId, projectIds);
Map standards = standardIds.isEmpty()
? Collections.emptyMap() : lookup.standards(tenantId, standardIds);
Map users = userIds.isEmpty()
? Collections.emptyMap() : lookup.users(tenantId, userIds);
Map products = productIds.isEmpty()
? Collections.emptyMap() : lookup.products(tenantId, productIds);
for (AuditFieldDiffViewVO field : fields) {
String name = normalized(field.getField());
if ("jiance_xiangmu".equals(name)) {
apply(field, projects, true);
} else if ("jianyan_biaozhun".equals(name)) {
apply(field, standards, false);
} else if ("qingyan_ren".equals(name)) {
apply(field, users, false);
} else if ("chanpin_id".equals(name)) {
apply(field, products, false);
}
}
}
private static void applyBusinessEnums(List fields) {
for (AuditFieldDiffViewVO field : fields) {
String name = normalized(field.getField());
if ("biz_status".equals(name)) {
applyEnum(field, BIZ_STATUS, "select");
} else if ("biz_type".equals(name)) {
applyEnum(field, BIZ_TYPE, "select");
} else if ("jiance_fangshi".equals(name)) {
applyEnum(field, JIANCE_FANGSHI, "radio");
}
}
}
private static void applyEnum(AuditFieldDiffViewVO field, Map labels,
String componentType) {
setMissingSemantics(field, "enum", componentType);
if (field.getOldDisplay() == null) {
field.setOldDisplay(enumDisplay(field.getOldData(), labels));
}
if (field.getNewDisplay() == null) {
field.setNewDisplay(enumDisplay(field.getNewData(), labels));
}
}
private static String enumDisplay(String raw, Map labels) {
if (blank(raw)) {
return "";
}
return labels.get(raw.trim());
}
private static Map labels(String... values) {
Map labels = new LinkedHashMap<>();
for (int i = 0; i + 1 < values.length; i += 2) {
labels.put(values[i], values[i + 1]);
}
return Collections.unmodifiableMap(labels);
}
private static void setMissingSemantics(AuditFieldDiffViewVO field,
String valueType, String componentType) {
if (blank(field.getValueType())) {
field.setValueType(valueType);
}
if (blank(field.getComponentType())) {
field.setComponentType(componentType);
}
}
private static void apply(AuditFieldDiffViewVO field, Map names, boolean multiple) {
if (field.getOldDisplay() == null) {
field.setOldDisplay(display(field.getOldData(), names, multiple));
}
if (field.getNewDisplay() == null) {
field.setNewDisplay(display(field.getNewData(), names, multiple));
}
}
private static String display(String raw, Map names, boolean multiple) {
if (blank(raw)) {
return "";
}
List ids = multiple ? parseIds(raw) : Collections.singletonList(raw.trim());
if (ids.isEmpty()) {
return UNAVAILABLE;
}
List display = new ArrayList<>(ids.size());
for (String id : ids) {
String name = names.get(id);
display.add(blank(name) ? UNAVAILABLE : name);
}
return String.join("、", display);
}
private static void collect(Set target, String raw) {
target.addAll(parseIds(raw));
}
@SuppressWarnings("unchecked")
private static List parseIds(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 ids = new ArrayList<>();
for (Object value : (Collection