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 写路径收权)。
*
*
用户在预览卡/表单上点按钮(保存/审核/作废…)→ 确定性端点校验后经本类落一行队列。
* **xlyAi 工作到此为止**:是否/何时执行、执行权属、审计留痕全部由 ERP 侧负责
* (暂存执行器读本表;现状=报价自动执行、其余进待办)。
* xlyAi 侧只保留 {@link #statusLabel} 只读展示处理进度(流程卡)。
*
*
状态协议 = sStatus 三态 + iStatusCode 状态码(2026-07-29 定):
*
* - {@code pending}:100=已提交等待 ERP 处理(xlyAi 写入的唯一状态)、101=ERP 处理中(已领取);
* - {@code success}:200=执行成功、201=幂等命中(此前已执行);
* - {@code fail}:400 载荷非法 / 401 身份权限 / 404 记录不存在 / 409 状态冲突 /
* 422 业务校验未通过 / 423 记录被锁 / 450 已取消 / 500 执行异常 / 504 超时。
*
* 旧值(draft/confirmed/executing/executed/failed/cancelled)兼容读(见 statusLabel),不再写。
*
* 归属列只写 {@code sMakePerson};旧列 {@code sUserId} 已停写但暂不可删——ERP 侧
* {@code myTodos}/归属校验仍以 {@code COALESCE(sMakePerson, 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);
}
/** xlyAi 写入的唯一状态:pending+100(已提交,等待 ERP 处理)。 */
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,sMakePerson,sBrandsId,sSubsidiaryId,sConversationId,sOpType," +
"sTargetFormId,sTargetModuleId,sTargetTable,sTargetBillId,sField,sFieldLabel," +
"sOldValue,sNewValue,sPayload,sDescription,sStatus,iStatusCode,tCreateDate,tConfirmDate) " +
"VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,'pending',100,NOW(),NOW())",
sId, 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