diff --git a/src/main/java/com/wok/supportbot/controller/AiController.java b/src/main/java/com/wok/supportbot/controller/AiController.java index 20ba420..0923a29 100644 --- a/src/main/java/com/wok/supportbot/controller/AiController.java +++ b/src/main/java/com/wok/supportbot/controller/AiController.java @@ -72,7 +72,7 @@ public class AiController { */ @GetMapping("/assistant_app/chat/sync") public String doChatWithAssistantAppSync(String message, String chatId, Long roleId, String accountId, String systemPrompt) { - AccountRoleContext context = resolveAccountRole(toLong(accountId), roleId); + AccountRoleContext context = resolveAccountRole(accountId, roleId); bindConversation(chatId, context); RoleScope scope = customerServiceRoleService.getRoleScope(context.roleId()); return assistantApp.doChat(message, chatId, resolveSystemPrompt(scope, systemPrompt)); @@ -88,7 +88,7 @@ public class AiController { */ @GetMapping(value = "/assistant_app/chat/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux doChatWithLoveAppSSE(String message, String chatId, Long roleId, String accountId, String systemPrompt) { - AccountRoleContext context = resolveAccountRole(toLong(accountId), roleId); + AccountRoleContext context = resolveAccountRole(accountId, roleId); bindConversation(chatId, context); RoleScope scope = customerServiceRoleService.getRoleScope(context.roleId()); return assistantApp.doChatByStream(message, chatId, resolveSystemPrompt(scope, systemPrompt)); @@ -104,7 +104,7 @@ public class AiController { */ @GetMapping(value = "/assistant_app/chat/server_sent_event") public Flux> doChatWithAssistantAppServerSentEvent(String message, String chatId, Long roleId, String accountId, String systemPrompt) { - AccountRoleContext context = resolveAccountRole(toLong(accountId), roleId); + AccountRoleContext context = resolveAccountRole(accountId, roleId); bindConversation(chatId, context); RoleScope scope = customerServiceRoleService.getRoleScope(context.roleId()); return assistantApp.doChatByStream(message, chatId, resolveSystemPrompt(scope, systemPrompt)) @@ -123,7 +123,7 @@ public class AiController { */ @GetMapping(value = "/assistant_app/chat/sse_emitter") public SseEmitter doChatWithAssistantAppServerSseEmitter(String message, String chatId, Long roleId, String accountId, String systemPrompt) { - AccountRoleContext context = resolveAccountRole(toLong(accountId), roleId); + AccountRoleContext context = resolveAccountRole(accountId, roleId); bindConversation(chatId, context); RoleScope scope = customerServiceRoleService.getRoleScope(context.roleId()); // 创建一个超时时间较长的 SseEmitter @@ -155,7 +155,7 @@ public class AiController { */ @GetMapping("/assistant_app/chat/rag/sync") public String doChatWithRagSync(String message, String chatId, String rewriteStrategy, Long roleId, String accountId, Long categoryId, String categoryIds, String systemPrompt) { - AccountRoleContext context = resolveAccountRole(toLong(accountId), roleId); + AccountRoleContext context = resolveAccountRole(accountId, roleId); bindConversation(chatId, context); RoleScope scope = customerServiceRoleService.getRoleScope(context.roleId()); String sys = resolveSystemPrompt(scope, systemPrompt); @@ -182,7 +182,7 @@ public class AiController { */ @GetMapping(value = "/assistant_app/chat/rag/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux doChatWithRagSSE(String message, String chatId, String rewriteStrategy, Long roleId, String accountId, Long categoryId, String categoryIds, String systemPrompt) { - AccountRoleContext context = resolveAccountRole(toLong(accountId), roleId); + AccountRoleContext context = resolveAccountRole(accountId, roleId); bindConversation(chatId, context); RoleScope scope = customerServiceRoleService.getRoleScope(context.roleId()); String sys = resolveSystemPrompt(scope, systemPrompt); @@ -202,7 +202,7 @@ public class AiController { @GetMapping("/assistant_app/rag/sources") public Map getRagSources(String message, String chatId, String rewriteStrategy, Long roleId, String accountId, Long categoryId, String categoryIds) { - AccountRoleContext context = resolveAccountRole(toLong(accountId), roleId); + AccountRoleContext context = resolveAccountRole(accountId, roleId); RoleScope scope = customerServiceRoleService.getRoleScope(context.roleId()); if (message == null || message.isBlank() || isKbDenied(scope) || shouldBypassKnowledgeRetrieval(message)) { return Map.of("success", true, "data", List.of()); @@ -264,8 +264,8 @@ public class AiController { } /** 命中角色时用角色人设,否则用客户端兜底人设。 */ - private AccountRoleContext resolveAccountRole(Long accountId, Long fallbackRoleId) { - AccountScope accountScope = customerAccountService.getAccountScope(accountId); + private AccountRoleContext resolveAccountRole(String accountId, Long fallbackRoleId) { + AccountScope accountScope = customerAccountService.getOrCreateAccountScope(accountId, fallbackRoleId); Long effectiveRoleId = accountScope.hasAccount() && accountScope.roleId() != null ? accountScope.roleId() : fallbackRoleId; @@ -316,15 +316,4 @@ public class AiController { private record AccountRoleContext(Long accountId, Long roleId) { } - /** 将字符串 accountId 安全转为 Long,非数字字符串返回 null */ - private static Long toLong(String value) { - if (value == null || value.isBlank()) { - return null; - } - try { - return Long.valueOf(value.trim()); - } catch (NumberFormatException e) { - return null; - } - } } diff --git a/src/main/java/com/wok/supportbot/service/CustomerAccountService.java b/src/main/java/com/wok/supportbot/service/CustomerAccountService.java index ca35b28..ef1a391 100644 --- a/src/main/java/com/wok/supportbot/service/CustomerAccountService.java +++ b/src/main/java/com/wok/supportbot/service/CustomerAccountService.java @@ -1,6 +1,7 @@ package com.wok.supportbot.service; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DuplicateKeyException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -102,10 +103,73 @@ public class CustomerAccountService { return new AccountScope(true, ((Number) row.get("id")).longValue(), Objects.toString(row.get("name"), ""), roleId); } + @Transactional(rollbackFor = Exception.class) + public AccountScope getOrCreateAccountScope(String accountId, Long fallbackRoleId) { + if (accountId == null || accountId.trim().isEmpty()) { + return AccountScope.empty(); + } + + String accountKey = accountId.trim(); + Long numericAccountId = toLong(accountKey); + AccountScope scope = getAccountScope(numericAccountId); + if (scope.hasAccount()) { + return scope; + } + + scope = getAccountScopeByKey(accountKey); + if (scope.hasAccount()) { + return scope; + } + + Long roleId = normalizeRoleId(fallbackRoleId); + try { + jdbcTemplate.update(""" + INSERT INTO customer_account (account_key, name, description, role_id, enabled, is_delete) + VALUES (?, ?, ?, ?, true, false) + ON CONFLICT (account_key) + DO UPDATE SET enabled = true, + is_delete = false, + role_id = COALESCE(customer_account.role_id, EXCLUDED.role_id), + update_time = CURRENT_TIMESTAMP + """, + accountKey, accountKey, "Auto-created from external chat accountId", roleId); + } catch (DuplicateKeyException ignored) { + // Another request may have created the same account; read it below. + } + return getAccountScopeByKey(accountKey); + } + + private AccountScope getAccountScopeByKey(String accountKey) { + if (accountKey == null || accountKey.trim().isEmpty()) { + return AccountScope.empty(); + } + List> rows = jdbcTemplate.queryForList(""" + SELECT id, name, role_id + FROM customer_account + WHERE account_key = ? AND is_delete = false AND enabled = true + """, accountKey.trim()); + if (rows.isEmpty()) { + return AccountScope.empty(); + } + Map row = rows.get(0); + Long roleId = row.get("role_id") == null ? null : ((Number) row.get("role_id")).longValue(); + return new AccountScope(true, ((Number) row.get("id")).longValue(), Objects.toString(row.get("name"), ""), roleId); + } private Long normalizeRoleId(Long roleId) { return roleId != null && roleId > 0 ? roleId : null; } + + private static Long toLong(String value) { + if (value == null || value.isBlank()) { + return null; + } + try { + return Long.valueOf(value.trim()); + } catch (NumberFormatException e) { + return null; + } + } public record AccountScope(boolean present, Long accountId, String name, Long roleId) { public static AccountScope empty() {