package jnpf.dmsService; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import jnpf.exception.DataException; import jnpf.permission.UserApi; import jnpf.permission.entity.UserEntity; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import java.util.*; /** Snapshots native form personnel fields at registration; workflow configuration owns role assignment. */ @Service @RequiredArgsConstructor @Slf4j public class DmsApprovedVersionPropertiesService { private final UserApi users; private final ObjectMapper json; public JsonNode create(String preparedBy, String storedReviewerIds) { LinkedHashSet reviewerIds = reviewerIds(storedReviewerIds); Set ids = new LinkedHashSet<>(reviewerIds); if (StringUtils.hasText(preparedBy)) ids.add(preparedBy); Map names = new HashMap<>(); if (!ids.isEmpty()) { List found = lookupNames(ids); for (UserEntity user : found) { if (user != null && ids.contains(user.getId())) names.put(user.getId(), user.getRealName() == null ? "" : user.getRealName()); } } ObjectNode result = json.createObjectNode(); result.put("prepared_by_name", names.getOrDefault(preparedBy, "")); var reviewers = result.putArray("reviewers"); for (String id : reviewerIds) reviewers.addObject().put("user_id", id).put("name", names.getOrDefault(id, "")); return result; } private List lookupNames(Set ids) { // Names are optional display snapshots; directory availability must not block registration. try { List found = users.getUserName(new ArrayList<>(ids)); return found == null ? List.of() : found; } catch (RuntimeException e) { log.warn("版本人员姓名查询失败,保留人员ID继续登记,异常类型:{}", e.getClass().getSimpleName()); return List.of(); } } private LinkedHashSet reviewerIds(String stored) { LinkedHashSet ids = new LinkedHashSet<>(); if (!StringUtils.hasText(stored)) return ids; String valueText = stored.trim(); // Workflow operation mapping writes the current operator ID as a scalar. // Keep the version/API contract as an array and accept existing multi-user values. if (!valueText.startsWith("[")) { if ("null".equals(valueText) || valueText.matches(".*[\\[\\]{}\",].*")) throw new DataException("审核人字段必须为人员ID或人员ID数组"); ids.add(valueText); return ids; } try { // Native multi-user fields are persisted as JSON arrays by FlowFormDataUtil. JsonNode value = json.readTree(stored); if (value == null || !value.isArray()) throw new DataException("审核人字段必须为人员ID数组"); for (JsonNode id : value) { if (!id.isTextual() || !StringUtils.hasText(id.textValue())) throw new DataException("审核人字段包含无效人员ID"); ids.add(id.textValue().trim()); } return ids; } catch (java.io.IOException e) { throw new DataException("审核人字段格式不正确"); } } }