OpService.java
4.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package com.xly.service;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
/**
* ai_op_queue 暂存管理 —— 写操作的人在环闭环:ProposeWrite 写 draft,用户确认后 confirm 端点执行。
*/
@Service
public class OpService {
private final JdbcTemplate jdbc;
private final AuditService audit;
public OpService(JdbcTemplate jdbc, AuditService audit) {
this.jdbc = jdbc;
this.audit = audit;
}
/** 暂存一条 draft 写操作,返回 opId(不执行)。 */
public String createDraft(String userId, String opType,
String formId, String moduleId, String table, String billId,
String field, String fieldLabel, String oldValue, String newValue,
String description) {
String sId = "op-" + System.currentTimeMillis() + "-" + Integer.toHexString((int) (Math.random() * 0xFFFFF));
jdbc.update(
"INSERT INTO ai_op_queue(sId,sUserId,sOpType,sTargetFormId,sTargetModuleId,sTargetTable," +
"sTargetBillId,sField,sFieldLabel,sOldValue,sNewValue,sDescription,sStatus,tCreateDate) " +
"VALUES(?,?,?,?,?,?,?,?,?,?,?,?, 'draft', NOW())",
sId, userId, opType, formId, moduleId, table, billId, field, fieldLabel, oldValue, newValue, description);
audit.log(userId, null, "propose", table + "#" + billId, description, true, "draft staged (" + sId + ")");
return sId;
}
/** 暂存一条 draft(payload 版,用于 create:sPayload=列->值 JSON)。 */
public String createDraftPayload(String userId, String opType, String formId, String moduleId,
String table, String payload, String description) {
String sId = "op-" + System.currentTimeMillis() + "-" + Integer.toHexString((int) (Math.random() * 0xFFFFF));
jdbc.update(
"INSERT INTO ai_op_queue(sId,sUserId,sOpType,sTargetFormId,sTargetModuleId,sTargetTable," +
"sPayload,sDescription,sStatus,tCreateDate) VALUES(?,?,?,?,?,?,?,?, 'draft', NOW())",
sId, userId, opType, formId, moduleId, table, payload, description);
audit.log(userId, null, "propose", table + " (create)", description, true, "draft staged (" + sId + ")");
return sId;
}
/** 把 draft 关联到会话(控制器在工具执行后调用;控制器持有 conversationId)。 */
public void attachConversation(String opId, String convId) {
jdbc.update("UPDATE ai_op_queue SET sConversationId=? WHERE sId=?", convId, opId);
}
public Map<String, Object> get(String sId) {
List<Map<String, Object>> r = jdbc.queryForList("SELECT * FROM ai_op_queue WHERE sId=?", sId);
return r.isEmpty() ? null : r.get(0);
}
/** 会话里最近一条待确认(draft)操作。 */
public Map<String, Object> pending(String convId) {
List<Map<String, Object>> r = jdbc.queryForList(
"SELECT * FROM ai_op_queue WHERE sConversationId=? AND sStatus='draft' ORDER BY tCreateDate DESC LIMIT 1",
convId);
return r.isEmpty() ? null : r.get(0);
}
public void setStatus(String sId, String status, String resultMsg) {
jdbc.update("UPDATE ai_op_queue SET sStatus=?, sResultMsg=?, tConfirmDate=NOW() WHERE sId=?",
status, resultMsg, sId);
}
/**
* 抢占一条 draft(CAS):只有把 draft 改成 executing 的那个请求返回 true,并发/重复确认直接落空。
* 没有它,两个并发 confirm 都能通过「读到 draft」的判断,把同一张单执行两次。
*/
public boolean claim(String sId) {
return jdbc.update("UPDATE ai_op_queue SET sStatus='executing', tConfirmDate=NOW() " +
"WHERE sId=? AND sStatus='draft'", sId) == 1;
}
/** 执行失败/异常时把 executing 退回 draft 之外的终态由 setStatus 负责;这里仅用于放弃抢占。 */
public void release(String sId) {
jdbc.update("UPDATE ai_op_queue SET sStatus='draft' WHERE sId=? AND sStatus='executing'", sId);
}
}