package jnpf.flowable.service.dev; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import jnpf.flowable.model.templatenode.nodejson.NodeModel; import jnpf.permission.entity.UserEntity; import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.function.Function; /** Test data only, derived from the published native component schema. Existing values are preserved. */ public final class DevApprovalFill { private DevApprovalFill() {} public static List fill(NodeModel node, String formJson, Map data, UserEntity signer, String signId) { return fill(node, formJson, data, signer, signId, type -> null); } public static List fill(NodeModel node, String formJson, Map data, UserEntity signer, String signId, Function dictionaries) { List problems = new ArrayList<>(); Map fields = new LinkedHashMap<>(); JSONObject form = formJson == null ? null : JSON.parseObject(formJson); if (form != null) index(form.getJSONArray("fields"), "", fields); if (node.getFormOperates() == null) return problems; for (Map permission : node.getFormOperates()) { if (!Boolean.TRUE.equals(permission.get("required"))) continue; String path = String.valueOf(permission.get("id")); String label = String.valueOf(permission.getOrDefault("name", path)).replace('\n', ' '); JSONObject field = fields.get(path); if (field != null && "dictionary".equals(field.getJSONObject("__config__").getString("dataType"))) { JSONObject config = field.getJSONObject("__config__"); field.put("options", dictionaries.apply(config.getString("dictionaryType"))); config.put("dataType", "static"); } int separator = path.indexOf('-'); if (separator < 0) fillOne(data, path, field, signer, signId, label, problems); else { Object rows = data.get(path.substring(0, separator)); if (rows instanceof String && ((String) rows).trim().startsWith("[")) { rows = JSON.parseArray((String) rows); data.put(path.substring(0, separator), rows); } // Native child-field required rules validate existing rows; they do not require creating a business row. if (rows instanceof List) { int rowIndex = 0; for (Object row : (List) rows) { rowIndex++; if (row instanceof Map) fillOne((Map) row, path.substring(separator + 1), field, signer, signId, label + "(第" + rowIndex + "行)", problems); } } } } return problems; } private static void index(JSONArray items, String prefix, Map fields) { if (items == null) return; for (Object raw : items) { if (!(raw instanceof JSONObject)) continue; JSONObject field = (JSONObject) raw; JSONObject config = field.getJSONObject("__config__"); if (config == null) continue; String id = field.getString("__vModel__"); if (id != null) fields.put(prefix + id, field); index(config.getJSONArray("children"), "table".equals(config.getString("jnpfKey")) ? id + "-" : prefix, fields); } } private static void fillOne(Map data, String id, JSONObject field, UserEntity signer, String signId, String label, List problems) { if (!DevApprovalValidation.empty(data.get(id))) return; Object value = value(field, signer, signId); if (DevApprovalValidation.empty(value)) problems.add("必填项无法自动补齐:" + label + "(" + id + "),请先填写有效数据"); else data.put(id, value); } static Object value(JSONObject field, UserEntity signer, String signId) { if (field == null) return null; JSONObject config = field.getJSONObject("__config__"); String key = config.getString("jnpfKey"); if ("biz_sign".equals(field.getString("__vModel__"))) return signId; boolean multiple = field.getBooleanValue("multiple"); switch (key == null ? "" : key) { case "input": case "textarea": case "editor": return field.getString("__vModel__").endsWith("_id") ? null : "已输入"; case "inputNumber": case "slider": case "rate": Number minimum = field.getBigDecimal("min"); return minimum == null ? 0 : minimum; case "switch": return field.containsKey("activeValue") ? field.get("activeValue") : 1; case "datePicker": return System.currentTimeMillis(); case "timePicker": return LocalTime.now().format(DateTimeFormatter.ofPattern( "HH:mm".equals(field.getString("format")) ? "HH:mm" : "HH:mm:ss")); case "dateRange": return List.of(System.currentTimeMillis(), System.currentTimeMillis()); case "userSelect": return selection(signer == null ? null : signer.getId(), multiple); case "organizeSelect": case "depSelect": return selection(signer == null ? null : signer.getOrganizeId(), multiple); case "posSelect": String positions = signer == null ? null : signer.getPositionId(); return selection(positions == null ? null : positions.split(",")[0], multiple); case "select": case "radio": case "checkbox": // Do not invent IDs for dictionaries, remote options or related business records. if (config.containsKey("dataType") && !"static".equals(config.getString("dataType"))) return null; JSONObject slot = field.getJSONObject("__slot__"); JSONArray options = slot == null ? field.getJSONArray("options") : slot.getJSONArray("options"); JSONObject props = field.getJSONObject("props"); String valueKey = props == null ? "id" : props.getString("value"); if (valueKey == null) valueKey = "id"; if (options != null) for (Object option : options) { if (!(option instanceof JSONObject)) continue; JSONObject item = (JSONObject) option; if (item.getBooleanValue("disabled")) continue; Object result = item.get(valueKey); if (result != null) return multiple || "checkbox".equals(key) ? List.of(result) : result; } return null; default: return null; } } private static Object selection(String id, boolean multiple) { return id == null || id.isBlank() ? null : multiple ? List.of(id) : id; } }