package com.xly.service; import com.xly.agent.AgentIdentity; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import java.util.List; import java.util.Map; /** * ai_op_queue 队列写入 —— **AI 侧唯一的写入口**(rearch3 §3 写路径收权)。 * *

用户在预览卡/表单上点按钮(保存/审核/作废…)→ 确定性端点校验后经本类落一行 * {@code sStatus='confirmed'}(用户已当面授权)。**xlyAi 工作到此为止**:是否/何时执行、 * 执行权属、审计留痕全部由 ERP 侧负责(暂存执行器读本表;现状=报价自动执行、其余进待办)。 * xlyAi 侧只保留 {@link #statusLabel} 只读展示处理进度(流程卡)。 * *

列名兼容:ERP 执行器仍读 {@code sUserId} → 新列 {@code sMakePerson}(ERP 惯例)双写, * ERP 侧切换后可删 sUserId。 */ @Service public class OpService { private final JdbcTemplate jdbc; public OpService(JdbcTemplate jdbc) { this.jdbc = jdbc; } /** 一次保存里的一个字段改动(批量入队用)。 */ public record FieldChange(String col, String label, String oldValue, String newValue, String description) { } /** 单字段更新入队(update)。 */ public String queueUpdate(AgentIdentity who, String convId, String formId, String moduleId, String table, String billId, String field, String fieldLabel, String oldValue, String newValue, String description) { return insert(who, convId, "update", formId, moduleId, table, billId, field, fieldLabel, oldValue, newValue, null, description); } /** * 多字段更新批量入队(单事务:全部成功或全部回滚,绝不"写一半")。 * ERP 执行器为单字段 update,故一次保存多字段 = 多行待办。 */ @org.springframework.transaction.annotation.Transactional public List queueUpdates(AgentIdentity who, String convId, String formId, String moduleId, String table, String billId, List changes) { List opIds = new java.util.ArrayList<>(changes.size()); for (FieldChange c : changes) { opIds.add(insert(who, convId, "update", formId, moduleId, table, billId, c.col(), c.label(), c.oldValue(), c.newValue(), null, c.description())); } return opIds; } /** 状态操作入队:invalid(sNewValue=toVoid|cancel)/ examine(sNewValue=1|0)/ delete。 */ public String queueStateOp(AgentIdentity who, String convId, String opType, String formId, String moduleId, String table, String billId, String sNewValue, String description) { return insert(who, convId, opType, formId, moduleId, table, billId, null, null, null, sNewValue, null, description); } /** 新增入队(create):payload = 列->值 JSON(多表用 __tables__)。 */ public String queueCreate(AgentIdentity who, String convId, String formId, String moduleId, String table, String payload, String description) { return insert(who, convId, "create", formId, moduleId, table, null, null, null, null, null, payload, description); } private String insert(AgentIdentity who, String convId, String opType, String formId, String moduleId, String table, String billId, String field, String fieldLabel, String oldValue, String newValue, String payload, String description) { String sId = "op-" + System.currentTimeMillis() + "-" + Integer.toHexString((int) (Math.random() * 0xFFFFF)); jdbc.update( "INSERT INTO ai_op_queue(sId,sUserId,sMakePerson,sBrandsId,sSubsidiaryId,sConversationId,sOpType," + "sTargetFormId,sTargetModuleId,sTargetTable,sTargetBillId,sField,sFieldLabel," + "sOldValue,sNewValue,sPayload,sDescription,sStatus,tCreateDate,tConfirmDate) " + "VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,'confirmed',NOW(),NOW())", sId, who.userId(), who.userId(), who.brandsId(), who.subsidiaryId(), convId, opType, formId, moduleId, table, billId, field, trunc(fieldLabel, 128), trunc(oldValue, 500), trunc(newValue, 500), payload, trunc(description, 500)); return sId; } private static String trunc(String s, int max) { return s != null && s.length() > max ? s.substring(0, max) : s; } public Map get(String sId) { List> r = jdbc.queryForList("SELECT * FROM ai_op_queue WHERE sId=?", sId); return r.isEmpty() ? null : r.get(0); } /** sStatus → 人类可读进度(流程卡只读展示;不做任何处理动作)。查不到返回 null。 */ public String statusLabel(String sId) { try { List> r = jdbc.queryForList( "SELECT sStatus, sResultMsg, sErrorMsg FROM ai_op_queue WHERE sId=?", sId); if (r.isEmpty()) { return null; } String st = String.valueOf(r.get(0).get("sStatus")); String msg = str(r.get(0).get("sResultMsg")); String err = str(r.get(0).get("sErrorMsg")); return switch (st) { case "confirmed" -> "已提交,等待 ERP 处理"; case "executing" -> "ERP 处理中"; case "executed" -> "ERP 已执行成功" + (msg.isBlank() ? "" : ":" + msg); case "failed" -> "ERP 执行失败" + (err.isBlank() ? (msg.isBlank() ? "" : ":" + msg) : ":" + err); case "cancelled" -> "已取消"; default -> st; }; } catch (Exception e) { return null; } } private static String str(Object o) { return o == null ? "" : o.toString(); } }