fix: 修复chatlog会话元数据回写与覆盖问题
- 为消息追加命令补充用户、助手与会话标题元信息 - 持久化后按会话聚合触发createOrTouch并优化空值覆盖策略 - 增加ChatPersistMySqlApplyService单测覆盖会话回写链路
This commit is contained in:
@@ -12,7 +12,11 @@ public class ChatAppendMessageCommand implements Serializable {
|
|||||||
private BigInteger deptId;
|
private BigInteger deptId;
|
||||||
private BigInteger sessionId;
|
private BigInteger sessionId;
|
||||||
private BigInteger userId;
|
private BigInteger userId;
|
||||||
|
private String userAccount;
|
||||||
private BigInteger assistantId;
|
private BigInteger assistantId;
|
||||||
|
private String assistantCode;
|
||||||
|
private String assistantName;
|
||||||
|
private String sessionTitle;
|
||||||
private BigInteger senderId;
|
private BigInteger senderId;
|
||||||
private String senderName;
|
private String senderName;
|
||||||
private String senderRole;
|
private String senderRole;
|
||||||
@@ -62,6 +66,14 @@ public class ChatAppendMessageCommand implements Serializable {
|
|||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getUserAccount() {
|
||||||
|
return userAccount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserAccount(String userAccount) {
|
||||||
|
this.userAccount = userAccount;
|
||||||
|
}
|
||||||
|
|
||||||
public BigInteger getAssistantId() {
|
public BigInteger getAssistantId() {
|
||||||
return assistantId;
|
return assistantId;
|
||||||
}
|
}
|
||||||
@@ -70,6 +82,30 @@ public class ChatAppendMessageCommand implements Serializable {
|
|||||||
this.assistantId = assistantId;
|
this.assistantId = assistantId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getAssistantCode() {
|
||||||
|
return assistantCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAssistantCode(String assistantCode) {
|
||||||
|
this.assistantCode = assistantCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAssistantName() {
|
||||||
|
return assistantName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAssistantName(String assistantName) {
|
||||||
|
this.assistantName = assistantName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSessionTitle() {
|
||||||
|
return sessionTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSessionTitle(String sessionTitle) {
|
||||||
|
this.sessionTitle = sessionTitle;
|
||||||
|
}
|
||||||
|
|
||||||
public BigInteger getSenderId() {
|
public BigInteger getSenderId() {
|
||||||
return senderId;
|
return senderId;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,19 +41,22 @@ public class MySqlChatSessionRepository {
|
|||||||
String sql = "INSERT INTO `" + table + "` " +
|
String sql = "INSERT INTO `" + table + "` " +
|
||||||
"(id, tenant_id, dept_id, user_id, user_account, assistant_id, assistant_code, assistant_name, title, last_message_preview, message_count, access_at, created, created_by, modified, modified_by, is_deleted) " +
|
"(id, tenant_id, dept_id, user_id, user_account, assistant_id, assistant_code, assistant_name, title, last_message_preview, message_count, access_at, created, created_by, modified, modified_by, is_deleted) " +
|
||||||
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, '', 0, ?, ?, ?, ?, ?, 0) " +
|
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, '', 0, ?, ?, ?, ?, ?, 0) " +
|
||||||
"ON DUPLICATE KEY UPDATE user_account=VALUES(user_account), assistant_id=VALUES(assistant_id), assistant_code=VALUES(assistant_code), " +
|
"ON DUPLICATE KEY UPDATE user_account=COALESCE(NULLIF(VALUES(user_account), ''), user_account), " +
|
||||||
"assistant_name=VALUES(assistant_name), title=VALUES(title), access_at=VALUES(access_at), modified=VALUES(modified), modified_by=VALUES(modified_by), is_deleted=0";
|
"assistant_id=VALUES(assistant_id), assistant_code=COALESCE(NULLIF(VALUES(assistant_code), ''), assistant_code), " +
|
||||||
|
"assistant_name=COALESCE(NULLIF(VALUES(assistant_name), ''), assistant_name), " +
|
||||||
|
"title=COALESCE(NULLIF(VALUES(title), ''), title), " +
|
||||||
|
"access_at=VALUES(access_at), modified=VALUES(modified), modified_by=VALUES(modified_by), is_deleted=0";
|
||||||
jdbcTemplate.batchUpdate(sql, commands, commands.size(), (ps, command) -> {
|
jdbcTemplate.batchUpdate(sql, commands, commands.size(), (ps, command) -> {
|
||||||
Timestamp operateAt = timestamp(command.getOperateAt());
|
Timestamp operateAt = timestamp(command.getOperateAt());
|
||||||
ps.setObject(1, command.getSessionId());
|
ps.setObject(1, command.getSessionId());
|
||||||
ps.setObject(2, command.getTenantId());
|
ps.setObject(2, command.getTenantId());
|
||||||
ps.setObject(3, command.getDeptId());
|
ps.setObject(3, command.getDeptId());
|
||||||
ps.setObject(4, command.getUserId());
|
ps.setObject(4, command.getUserId());
|
||||||
ps.setString(5, command.getUserAccount());
|
ps.setString(5, safeString(command.getUserAccount()));
|
||||||
ps.setObject(6, command.getAssistantId());
|
ps.setObject(6, command.getAssistantId());
|
||||||
ps.setString(7, command.getAssistantCode());
|
ps.setString(7, safeString(command.getAssistantCode()));
|
||||||
ps.setString(8, command.getAssistantName());
|
ps.setString(8, safeString(command.getAssistantName()));
|
||||||
ps.setString(9, command.getTitle());
|
ps.setString(9, safeString(command.getTitle()));
|
||||||
ps.setTimestamp(10, operateAt);
|
ps.setTimestamp(10, operateAt);
|
||||||
ps.setTimestamp(11, operateAt);
|
ps.setTimestamp(11, operateAt);
|
||||||
ps.setObject(12, command.getOperatorId());
|
ps.setObject(12, command.getOperatorId());
|
||||||
@@ -272,4 +275,8 @@ public class MySqlChatSessionRepository {
|
|||||||
private Timestamp timestamp(Date value) {
|
private Timestamp timestamp(Date value) {
|
||||||
return new Timestamp((value == null ? new Date() : value).getTime());
|
return new Timestamp((value == null ? new Date() : value).getTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String safeString(String value) {
|
||||||
|
return value == null ? "" : value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ public class ChatPersistMySqlApplyService {
|
|||||||
insertedCommands = logRepository.appendMessages(appendCommands);
|
insertedCommands = logRepository.appendMessages(appendCommands);
|
||||||
}
|
}
|
||||||
if (!insertedCommands.isEmpty()) {
|
if (!insertedCommands.isEmpty()) {
|
||||||
|
sessionRepository.createOrTouchBatch(buildSessionUpserts(insertedCommands));
|
||||||
summaryCommands.clear();
|
summaryCommands.clear();
|
||||||
for (ChatAppendMessageCommand insertedCommand : insertedCommands) {
|
for (ChatAppendMessageCommand insertedCommand : insertedCommands) {
|
||||||
accumulateSummary(summaryCommands, insertedCommand);
|
accumulateSummary(summaryCommands, insertedCommand);
|
||||||
@@ -139,9 +140,74 @@ public class ChatPersistMySqlApplyService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<ChatSessionUpsertCommand> buildSessionUpserts(List<ChatAppendMessageCommand> commands) {
|
||||||
|
if (commands == null || commands.isEmpty()) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
Map<BigInteger, ChatSessionUpsertCommand> upserts = new LinkedHashMap<>();
|
||||||
|
for (ChatAppendMessageCommand command : commands) {
|
||||||
|
if (command == null || command.getSessionId() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ChatSessionUpsertCommand upsert = upserts.computeIfAbsent(command.getSessionId(), key -> {
|
||||||
|
ChatSessionUpsertCommand created = new ChatSessionUpsertCommand();
|
||||||
|
created.setSessionId(command.getSessionId());
|
||||||
|
created.setTenantId(command.getTenantId());
|
||||||
|
created.setDeptId(command.getDeptId());
|
||||||
|
created.setUserId(command.getUserId());
|
||||||
|
created.setAssistantId(command.getAssistantId());
|
||||||
|
created.setOperateAt(null);
|
||||||
|
return created;
|
||||||
|
});
|
||||||
|
if (upsert.getTenantId() == null) {
|
||||||
|
upsert.setTenantId(command.getTenantId());
|
||||||
|
}
|
||||||
|
if (upsert.getDeptId() == null) {
|
||||||
|
upsert.setDeptId(command.getDeptId());
|
||||||
|
}
|
||||||
|
if (upsert.getUserId() == null) {
|
||||||
|
upsert.setUserId(command.getUserId());
|
||||||
|
}
|
||||||
|
if (upsert.getAssistantId() == null) {
|
||||||
|
upsert.setAssistantId(command.getAssistantId());
|
||||||
|
}
|
||||||
|
if (isBlank(upsert.getUserAccount()) && !isBlank(command.getUserAccount())) {
|
||||||
|
upsert.setUserAccount(command.getUserAccount().trim());
|
||||||
|
}
|
||||||
|
if (isBlank(upsert.getAssistantCode()) && !isBlank(command.getAssistantCode())) {
|
||||||
|
upsert.setAssistantCode(command.getAssistantCode().trim());
|
||||||
|
}
|
||||||
|
if (isBlank(upsert.getAssistantName()) && !isBlank(command.getAssistantName())) {
|
||||||
|
upsert.setAssistantName(command.getAssistantName().trim());
|
||||||
|
}
|
||||||
|
if (isBlank(upsert.getTitle()) && !isBlank(command.getSessionTitle())) {
|
||||||
|
upsert.setTitle(command.getSessionTitle().trim());
|
||||||
|
}
|
||||||
|
Date operateAt = defaultDate(command.getCreated());
|
||||||
|
if (upsert.getOperateAt() == null || !operateAt.before(upsert.getOperateAt())) {
|
||||||
|
upsert.setOperateAt(operateAt);
|
||||||
|
upsert.setOperatorId(command.getCreatedBy());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (ChatSessionUpsertCommand upsert : upserts.values()) {
|
||||||
|
if (isBlank(upsert.getUserAccount())) {
|
||||||
|
upsert.setUserAccount("");
|
||||||
|
}
|
||||||
|
if (isBlank(upsert.getTitle())) {
|
||||||
|
upsert.setTitle("会话-" + upsert.getSessionId());
|
||||||
|
}
|
||||||
|
if (upsert.getOperateAt() == null) {
|
||||||
|
upsert.setOperateAt(new Date());
|
||||||
|
}
|
||||||
|
if (upsert.getOperatorId() == null) {
|
||||||
|
upsert.setOperatorId(upsert.getUserId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ArrayList<>(upserts.values());
|
||||||
|
}
|
||||||
|
|
||||||
private YearMonth resolveMonth(Date createdAt) {
|
private YearMonth resolveMonth(Date createdAt) {
|
||||||
Date created = createdAt == null ? new Date() : createdAt;
|
return YearMonth.from(defaultDate(createdAt).toInstant().atZone(ZoneId.systemDefault()).toLocalDate());
|
||||||
return YearMonth.from(created.toInstant().atZone(ZoneId.systemDefault()).toLocalDate());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String trimPreview(String text) {
|
private String trimPreview(String text) {
|
||||||
@@ -150,4 +216,12 @@ public class ChatPersistMySqlApplyService {
|
|||||||
}
|
}
|
||||||
return text.length() <= 200 ? text : text.substring(0, 200);
|
return text.length() <= 200 ? text : text.substring(0, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isBlank(String value) {
|
||||||
|
return value == null || value.isBlank();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Date defaultDate(Date value) {
|
||||||
|
return value == null ? new Date() : value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,7 +105,11 @@ public class ChatlogRuntimeListener implements ChatRuntimeListener {
|
|||||||
command.setDeptId(defaultNumber(context.getDeptId()));
|
command.setDeptId(defaultNumber(context.getDeptId()));
|
||||||
command.setSessionId(context.getSessionId());
|
command.setSessionId(context.getSessionId());
|
||||||
command.setUserId(defaultNumber(context.getUserId()));
|
command.setUserId(defaultNumber(context.getUserId()));
|
||||||
|
command.setUserAccount(context.getUserAccount());
|
||||||
command.setAssistantId(defaultNumber(context.getAssistantId()));
|
command.setAssistantId(defaultNumber(context.getAssistantId()));
|
||||||
|
command.setAssistantCode(context.getAssistantCode());
|
||||||
|
command.setAssistantName(context.getAssistantName());
|
||||||
|
command.setSessionTitle(context.getSessionTitle());
|
||||||
command.setSenderId(defaultNumber(message.getSenderId()));
|
command.setSenderId(defaultNumber(message.getSenderId()));
|
||||||
command.setSenderName(message.getSenderName());
|
command.setSenderName(message.getSenderName());
|
||||||
command.setSenderRole(message.getRole());
|
command.setSenderRole(message.getRole());
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package tech.easyflow.chatlog.service;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import tech.easyflow.chatlog.domain.command.ChatAppendMessageCommand;
|
||||||
|
import tech.easyflow.chatlog.domain.command.ChatSessionUpsertCommand;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ChatPersistMySqlApplyServiceTest {
|
||||||
|
|
||||||
|
private final ChatPersistMySqlApplyService service =
|
||||||
|
new ChatPersistMySqlApplyService(null, null, null, null);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldBuildMissingSessionUpsertFromMessageMetadata() {
|
||||||
|
ChatAppendMessageCommand first = new ChatAppendMessageCommand();
|
||||||
|
first.setSessionId(BigInteger.valueOf(101));
|
||||||
|
first.setTenantId(BigInteger.ONE);
|
||||||
|
first.setDeptId(BigInteger.TEN);
|
||||||
|
first.setUserId(BigInteger.valueOf(12));
|
||||||
|
first.setUserAccount("admin");
|
||||||
|
first.setAssistantId(BigInteger.valueOf(99));
|
||||||
|
first.setAssistantCode("test-bot");
|
||||||
|
first.setAssistantName("测试助手");
|
||||||
|
first.setSessionTitle("你是谁");
|
||||||
|
first.setCreatedBy(BigInteger.valueOf(12));
|
||||||
|
first.setCreated(new Date(1_000L));
|
||||||
|
|
||||||
|
ChatAppendMessageCommand second = new ChatAppendMessageCommand();
|
||||||
|
second.setSessionId(BigInteger.valueOf(101));
|
||||||
|
second.setTenantId(BigInteger.ONE);
|
||||||
|
second.setDeptId(BigInteger.TEN);
|
||||||
|
second.setUserId(BigInteger.valueOf(12));
|
||||||
|
second.setAssistantId(BigInteger.valueOf(99));
|
||||||
|
second.setCreatedBy(BigInteger.valueOf(12));
|
||||||
|
second.setCreated(new Date(2_000L));
|
||||||
|
|
||||||
|
List<ChatSessionUpsertCommand> upserts = service.buildSessionUpserts(List.of(first, second));
|
||||||
|
|
||||||
|
Assert.assertEquals(1, upserts.size());
|
||||||
|
ChatSessionUpsertCommand upsert = upserts.get(0);
|
||||||
|
Assert.assertEquals(BigInteger.valueOf(101), upsert.getSessionId());
|
||||||
|
Assert.assertEquals("admin", upsert.getUserAccount());
|
||||||
|
Assert.assertEquals("test-bot", upsert.getAssistantCode());
|
||||||
|
Assert.assertEquals("测试助手", upsert.getAssistantName());
|
||||||
|
Assert.assertEquals("你是谁", upsert.getTitle());
|
||||||
|
Assert.assertEquals(new Date(2_000L), upsert.getOperateAt());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldFallbackTitleWhenMessageMetadataMissing() {
|
||||||
|
ChatAppendMessageCommand command = new ChatAppendMessageCommand();
|
||||||
|
command.setSessionId(BigInteger.valueOf(202));
|
||||||
|
command.setTenantId(BigInteger.ONE);
|
||||||
|
command.setDeptId(BigInteger.ONE);
|
||||||
|
command.setUserId(BigInteger.valueOf(7));
|
||||||
|
command.setAssistantId(BigInteger.valueOf(8));
|
||||||
|
command.setCreatedBy(BigInteger.valueOf(7));
|
||||||
|
command.setCreated(new Date(3_000L));
|
||||||
|
|
||||||
|
List<ChatSessionUpsertCommand> upserts = service.buildSessionUpserts(List.of(command));
|
||||||
|
|
||||||
|
Assert.assertEquals(1, upserts.size());
|
||||||
|
ChatSessionUpsertCommand upsert = upserts.get(0);
|
||||||
|
Assert.assertEquals("", upsert.getUserAccount());
|
||||||
|
Assert.assertEquals("会话-202", upsert.getTitle());
|
||||||
|
Assert.assertEquals(BigInteger.valueOf(7), upsert.getOperatorId());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user