feat(controller): 新增 CopilotKit 控制器并重构数据访问逻辑
- 添加 CopilotKitController 实现 SSE 流式响应 - 重构 SysEntityController 和 DynamicDataController 移除 creatorId 过滤 - 新增 AgentResponse 和 Result 统一响应结构 - 扩展 CrmAgent 支持同步和流式聊天接口 - 更新 application.yml 添加 streaming-chat-model 配置 - 完善 CrmTools 工具类增加更新和查询功能
This commit is contained in:
@@ -7,7 +7,8 @@
|
||||
- **关键组件**:
|
||||
- `CrmTools.java`: 系统的“消息枢纽”,包含所有 `@Tool` 方法(Define, Save, Delete),直接与 Repository 交互。
|
||||
- `AgentController.java`: 主要入口,负责处理自然语言指令或业务事实报告。
|
||||
- **隔离机制**:实现了基于 Header `X-Creator-Id` 的简易用户 Session 隔离,所有数据查询和变更均强制绑定 `creatorId`。
|
||||
- `CopilotKitController.java`: **新增**,充当 CopilotKit 的后端运行时,统一通过 LangChain4j 调用 AI。
|
||||
- [x] **业务模块共享**:移除了 `/api/entities` 的 `creatorId` 过滤,所有用户可共享业务实体定义 (Schema),但业务数据记录目前仍保留隔离。
|
||||
|
||||
## 💼 业务逻辑 (Business Logic)
|
||||
- **动态模块**:系统支持通过 AI 指令动态创建业务实体(Entities),并在数据库中自动维护。
|
||||
@@ -16,17 +17,25 @@
|
||||
## 📈 当前开发进度 (Current Progress)
|
||||
- [x] **AI 原生 CRUD**:已实现 Agent 驱动的插入、更新和删除。
|
||||
- [x] **实体关联**:支持通过 `x-link-entity` 扩展 Schema 实现实体间关联选择。
|
||||
- [x] **用户会话隔离**:已完成后端拦截与过滤逻辑,确保多用户数据不串线。
|
||||
- [x] **用户会话隔离**:已完成后端拦截与过滤逻辑。
|
||||
- [x] **CopilotKit 后端转发**:已实现 AI 交互的后端中转,提升了安全性。
|
||||
- [x] **CORS 配置**:已解决前后端跨域通信问题。
|
||||
- [x] **CopilotKit v1 协议适配**: 修复了由于后端控制器 DTO 结构不匹配导致的 Runtime 协议消息解析异常(messages 嵌套在 body 中)。
|
||||
- [x] **Bug Fix**: 修复了 `CopilotKitController` 处理空请求时的 `NullPointerException`。
|
||||
- [x] **接口扩充**: 新增了 `/api/copilotkit/info` 发现接口,以适配前端 CopilotKit 库的启动流程。
|
||||
- [x] **流式配置修复**: 修复了 `CrmAgent` 在处理流式请求时因缺失 `StreamingChatLanguageModel` 导致的 `IllegalArgumentException`。
|
||||
- [x] **流式协议修正**: 将 `ResponseBodyEmitter` 恢复为 `SseEmitter`。并在流开始时强制发送 `threadId` 和 `runId` 初始事件,确保前端 `@copilotkit/core` 能正确挂载响应流。同时移除了末尾冗余的 `result` 类型消息以避免解析冲突。
|
||||
- [x] **流式数据格式修正**: 修复了由于 `SseEmitter` 自动追加 `data:` 前缀导致前端 `@copilotkit/runtime` 无法解析流的问题,将实现替换为 `ResponseBodyEmitter` 结合手动追加 `\n` 的 NDJSON(换行分隔 JSON)格式。
|
||||
- [x] **Runtime Info 修复**: 修复了 `/api/copilotkit/info` 的响应结构,将 `agents` 从列表改为 Map (Object),并显式声明了 `default` agent。
|
||||
|
||||
## ⚠️ 尚未解决的隐患 (Known Issues/Risks)
|
||||
- **并发冲突**:动态 Schema 变更在极高并发下的稳定性待验证。
|
||||
- **共享冲突**:当多用户同时修改同一个公共实体的 Schema 时,可能产生覆盖风险。
|
||||
- **权限细化**:目前的 `X-Creator-Id` 仅做基础隔离,缺乏细粒度的 RBAC。
|
||||
|
||||
## 🚀 下一步计划 (Next Steps)
|
||||
1. **AI 增强搜索**:实现通过 AI 生成复杂查询逻辑。
|
||||
2. **仪表盘分析**:开发动态数据的 AI 汇总分析模块。
|
||||
3. **性能监控**:针对 AI Tool 调用的耗时进行埋点监控。
|
||||
1. **数据共享开关**:支持针对特定实体配置是否跨用户共享数据。
|
||||
2. **AI 增强搜索**:实现通过 AI 生成复杂查询逻辑。
|
||||
3. **仪表盘分析**:开发动态数据的 AI 汇总分析模块。
|
||||
|
||||
---
|
||||
*Last Updated: 2026-03-25 by Antigravity*
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.example.ainative.ai;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class AgentResponse {
|
||||
/**
|
||||
* AI 生成的对话内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 业务状态标识 (比如: SUCCESS, NEED_INFO, ERROR)
|
||||
*/
|
||||
private String status;
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import dev.langchain4j.service.MemoryId;
|
||||
import dev.langchain4j.service.SystemMessage;
|
||||
import dev.langchain4j.service.UserMessage;
|
||||
import dev.langchain4j.service.V;
|
||||
import dev.langchain4j.service.TokenStream;
|
||||
import dev.langchain4j.service.spring.AiService;
|
||||
|
||||
@AiService(tools = "crmTools")
|
||||
@@ -34,5 +35,35 @@ public interface CrmAgent {
|
||||
|
||||
当前租户上下文 ID: {{userContext}}
|
||||
""")
|
||||
String chat(@MemoryId String memoryId, @UserMessage String userMessage, @V("userContext") String userContext);
|
||||
TokenStream chat(@MemoryId String memoryId, @UserMessage String userMessage, @V("userContext") String userContext);
|
||||
|
||||
|
||||
// 用于普通同步调用 (重载方法,返回 String)
|
||||
@SystemMessage("""
|
||||
## 角色与愿景
|
||||
你是一个 AI 原生 CRM 系统的核心引擎。你的任务是通过自然语言管理业务模块(Schema)和动态数据,替代传统的 REST 接口。
|
||||
|
||||
## 核心运行准则(严禁违反)
|
||||
1. **执行重于对话**:系统已禁用传统的增删改接口,所有数据变更必须通过调用工具实现。
|
||||
2. **禁止虚假模拟**:严禁在未成功调用工具的情况下,回复任何暗示操作已完成的内容(如“已保存”、“已创建”)。
|
||||
3. **事实驱动**:前端会报告“业务事实”(如:用户提交了表单内容),你必须根据事实判断该调用哪个工具(如:insertDynamicData)。
|
||||
|
||||
## 建模协议 (defineDynamicModule)
|
||||
当定义或更新模块结构时,生成的 JSON Schema 需遵循:
|
||||
- **实体关联**:若字段需关联其他实体,必须在属性中加入 `"x-link-entity": "目标实体Code"`。
|
||||
- **约束增强**:优先使用 `enum` (下拉选项)、`pattern` (正则校验)、`format: "date"` 等标准 JSON Schema 约束。
|
||||
- **命名规范**:entityCode 采用下划线命名法(如:student_course)。
|
||||
|
||||
## 数据操作协议
|
||||
- **入库 (insert)**:将用户提供的或事实报告中的信息提取为 JSON 字符串。
|
||||
- **物理删除 (delete)**:仅在用户明确指令或业务流程需要时执行。
|
||||
|
||||
## 输出风格
|
||||
- 保持简洁。
|
||||
- 必须在工具执行成功后,再简要反馈执行结果或后续 UI 操作建议。
|
||||
- **结构化输出**:请直接以 JSON 格式返回,包含 `content` (对话内容) 和 `status` (SUCCESS/ERROR/NEED_INFO) 字段。
|
||||
|
||||
当前租户上下文 ID: {{userContext}}
|
||||
""")
|
||||
AgentResponse chatSync(@MemoryId String memoryId, @UserMessage String userMessage, @V("userContext") String userContext);
|
||||
}
|
||||
|
||||
@@ -75,9 +75,25 @@ public class CrmTools {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AI 工具封装:删除指定的业务数据。
|
||||
*/
|
||||
@Transactional
|
||||
@Tool("核心业务处理:根据记录 ID 更新已有的动态业务数据。参数:id (记录的唯一 UUID), jsonData (更新后的完整 JSON 数据)。")
|
||||
public String updateDynamicData(String id, String jsonData) {
|
||||
log.info("Agent Tool 正在处理消息:更新记录 ID: {}", id);
|
||||
try {
|
||||
java.util.UUID uuid = java.util.UUID.fromString(id);
|
||||
return sysDynamicDataRepository.findById(uuid)
|
||||
.map(record -> {
|
||||
record.setData(jsonData);
|
||||
sysDynamicDataRepository.save(record);
|
||||
return "AI 已成功更新记录 (ID: " + id + ")。";
|
||||
})
|
||||
.orElse("AI 无法找到 ID 为 [" + id + "] 的记录,可能已被删除。");
|
||||
} catch (Exception e) {
|
||||
log.error("AI 更新数据失败: {}", e.getMessage());
|
||||
return "更新操作遇到技术故障: " + e.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Tool("核心业务处理:根据 ID 物理删除指定的业务记录。参数:id (记录的唯一 UUID)。")
|
||||
public String deleteDynamicData(String id) {
|
||||
@@ -94,6 +110,31 @@ public class CrmTools {
|
||||
return "删除操作遇到技术故障: " + e.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
@Tool("核心业务处理:查询指定模块下的所有业务数据。参数:entityCode (模块代码)。返回该模块下当前用户的所有记录 JSON 数组。")
|
||||
public String queryDynamicData(String entityCode) {
|
||||
log.info("Agent Tool 正在处理消息:查询模块数据: Code={}", entityCode);
|
||||
try {
|
||||
SysEntity entity = sysEntityRepository.findByEntityCode(entityCode);
|
||||
if (entity == null) {
|
||||
return "AI 无法找到模块 [" + entityCode + "]。";
|
||||
}
|
||||
java.util.List<SysDynamicData> records = sysDynamicDataRepository.findByEntityIdAndCreatorId(
|
||||
entity.getId(), UserContextHolder.getCurrentUser());
|
||||
if (records.isEmpty()) {
|
||||
return "模块 [" + entityCode + "] 下暂无数据。";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("模块 [").append(entityCode).append("] 共 ").append(records.size()).append(" 条记录:\n");
|
||||
for (SysDynamicData r : records) {
|
||||
sb.append("- ID: ").append(r.getId()).append(", 数据: ").append(r.getData()).append("\n");
|
||||
}
|
||||
return sb.toString();
|
||||
} catch (Exception e) {
|
||||
log.error("AI 查询数据失败: {}", e.getMessage());
|
||||
return "查询失败: " + e.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.example.ainative.common;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class Result<T> {
|
||||
private Integer code;
|
||||
private String msg;
|
||||
private T data;
|
||||
|
||||
public static <T> Result<T> success(T data) {
|
||||
return new Result<>(200, "success", data);
|
||||
}
|
||||
|
||||
public static <T> Result<T> error(String msg) {
|
||||
return new Result<>(500, msg, null);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.example.ainative.controller;
|
||||
|
||||
import com.example.ainative.ai.CrmAgent;
|
||||
import com.example.ainative.ai.AgentResponse;
|
||||
import com.example.ainative.common.Result;
|
||||
import com.example.ainative.config.UserContextHolder;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -20,15 +22,16 @@ public class AgentController {
|
||||
*
|
||||
* @param sessionId 唯一会话识别(与底层的 ChatMemory 持久化紧密对应,确保 AI 能“记住”对话)
|
||||
* @param message 用户的原始需求 (比如:"帮我建一个客户关系系统的线索录入表,有姓名和手机号")
|
||||
* @return AI 所思所想或执行完 @Tool 后的结果字符串反馈
|
||||
* @return AI 所思所想或执行完 @Tool 后的结果结构化反馈
|
||||
*/
|
||||
@PostMapping("/chat")
|
||||
public String chat(@RequestParam String sessionId, @RequestBody String message) {
|
||||
public Result<AgentResponse> chat(@RequestParam String sessionId, @RequestBody String message) {
|
||||
// 先从拦截器上下文中拿到刚才设置的调用者身份
|
||||
String currentUser = UserContextHolder.getCurrentUser();
|
||||
log.info("message:{}",message);
|
||||
// 传递给底层的 Agent:这背后的复杂工作流完全交由 Langchain4J 托管!
|
||||
// 如果用户的 message 意图命中“建表”,框架会在返回前自己拦截调去跑 CrmTools 里的 defineDynamicModule。
|
||||
return crmAgent.chat(sessionId, message, currentUser);
|
||||
AgentResponse res = crmAgent.chatSync(sessionId, message, currentUser);
|
||||
return Result.success(res);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
package com.example.ainative.controller;
|
||||
|
||||
import com.example.ainative.ai.CrmAgent;
|
||||
import com.example.ainative.config.UserContextHolder;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/copilotkit")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
@CrossOrigin(origins = "*")
|
||||
public class CopilotKitController {
|
||||
|
||||
private final CrmAgent crmAgent;
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@GetMapping({"/info", ""})
|
||||
public Map<String, Object> getInfo() {
|
||||
return Map.of(
|
||||
"actions", List.of(),
|
||||
"agents", Map.of(
|
||||
"default", Map.of("description", "Default Agent")
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@PostMapping(value = "", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
||||
public ResponseEntity<Object> handleSingleRoute(@RequestBody CopilotSingleRouteRequest request) {
|
||||
if ("info".equals(request.getMethod())) {
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.body(getInfo());
|
||||
}
|
||||
|
||||
log.info("CopilotKit single-route request: method={}", request.getMethod());
|
||||
AgentRunInput input = request.getBody();
|
||||
if (input == null) {
|
||||
input = new AgentRunInput();
|
||||
}
|
||||
return buildStreamResponse(input);
|
||||
}
|
||||
|
||||
@PostMapping(value = "/agent/{agentId}/run", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
||||
public ResponseEntity<SseEmitter> handleRestRoute(
|
||||
@PathVariable String agentId,
|
||||
@RequestBody AgentRunInput input) {
|
||||
log.info("CopilotKit REST request: agentId={}", agentId);
|
||||
if (input == null) {
|
||||
input = new AgentRunInput();
|
||||
}
|
||||
return buildStreamResponse(input);
|
||||
}
|
||||
|
||||
private <T> ResponseEntity<T> buildStreamResponse(AgentRunInput input) {
|
||||
SseEmitter emitter = new SseEmitter(180_000L);
|
||||
|
||||
String threadId = input.getThreadId() != null ? input.getThreadId() : UUID.randomUUID().toString();
|
||||
String runId = input.getRunId() != null ? input.getRunId() : UUID.randomUUID().toString();
|
||||
String messageId = UUID.randomUUID().toString();
|
||||
|
||||
List<Map<String, Object>> messages = input.getMessages();
|
||||
if (messages == null || messages.isEmpty()) {
|
||||
sendEvent(emitter, aguiEvent("RUN_STARTED", "threadId", threadId, "runId", runId));
|
||||
sendEvent(emitter, aguiEvent("RUN_FINISHED", "threadId", threadId, "runId", runId));
|
||||
emitter.complete();
|
||||
@SuppressWarnings("unchecked")
|
||||
ResponseEntity<T> resp = (ResponseEntity<T>) ResponseEntity.ok()
|
||||
.contentType(MediaType.TEXT_EVENT_STREAM)
|
||||
.body(emitter);
|
||||
return resp;
|
||||
}
|
||||
|
||||
String lastUserMessage = messages.stream()
|
||||
.filter(m -> "user".equals(m.get("role")))
|
||||
.reduce((first, second) -> second)
|
||||
.map(m -> {
|
||||
Object content = m.get("content");
|
||||
if (content instanceof String) {
|
||||
return (String) content;
|
||||
}
|
||||
if (content instanceof List<?> parts) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Object part : parts) {
|
||||
if (part instanceof Map<?, ?> partMap && "text".equals(partMap.get("type"))) {
|
||||
sb.append(partMap.get("text"));
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
return "";
|
||||
})
|
||||
.orElse("");
|
||||
|
||||
log.info("User message: {}", lastUserMessage);
|
||||
String currentUser = UserContextHolder.getCurrentUser();
|
||||
|
||||
sendEvent(emitter, aguiEvent("RUN_STARTED", "threadId", threadId, "runId", runId));
|
||||
sendEvent(emitter, aguiEvent("TEXT_MESSAGE_START", "messageId", messageId, "role", "assistant"));
|
||||
|
||||
crmAgent.chat("copilot-streaming-session", lastUserMessage, currentUser)
|
||||
.onPartialResponse(token -> {
|
||||
sendEvent(emitter, aguiEvent("TEXT_MESSAGE_CONTENT", "messageId", messageId, "delta", token));
|
||||
})
|
||||
.onCompleteResponse(response -> {
|
||||
sendEvent(emitter, aguiEvent("TEXT_MESSAGE_END", "messageId", messageId));
|
||||
sendEvent(emitter, aguiEvent("RUN_FINISHED", "threadId", threadId, "runId", runId));
|
||||
emitter.complete();
|
||||
})
|
||||
.onError(err -> {
|
||||
log.error("AI streaming error:", err);
|
||||
sendEvent(emitter, aguiEvent("TEXT_MESSAGE_END", "messageId", messageId));
|
||||
sendEvent(emitter, aguiEvent("RUN_ERROR", "message", err.getMessage() != null ? err.getMessage() : "Unknown error"));
|
||||
sendEvent(emitter, aguiEvent("RUN_FINISHED", "threadId", threadId, "runId", runId));
|
||||
emitter.completeWithError(err);
|
||||
})
|
||||
.start();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
ResponseEntity<T> resp = (ResponseEntity<T>) ResponseEntity.ok()
|
||||
.contentType(MediaType.TEXT_EVENT_STREAM)
|
||||
.body(emitter);
|
||||
return resp;
|
||||
}
|
||||
|
||||
private Map<String, Object> aguiEvent(String type, String... kvPairs) {
|
||||
Map<String, Object> event = new LinkedHashMap<>();
|
||||
event.put("type", type);
|
||||
for (int i = 0; i < kvPairs.length; i += 2) {
|
||||
event.put(kvPairs[i], kvPairs[i + 1]);
|
||||
}
|
||||
return event;
|
||||
}
|
||||
|
||||
private void sendEvent(SseEmitter emitter, Map<String, Object> data) {
|
||||
try {
|
||||
emitter.send(SseEmitter.event().data(data));
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to send SSE event: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class CopilotSingleRouteRequest {
|
||||
private String method;
|
||||
private Map<String, Object> params;
|
||||
private AgentRunInput body;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class AgentRunInput {
|
||||
private String threadId;
|
||||
private String runId;
|
||||
private List<Map<String, Object>> messages;
|
||||
private List<Map<String, Object>> context;
|
||||
private List<Map<String, Object>> tools;
|
||||
private Map<String, Object> forwardedProps;
|
||||
private Map<String, Object> state;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.example.ainative.controller;
|
||||
|
||||
import com.example.ainative.ai.CrmTools;
|
||||
import com.example.ainative.config.UserContextHolder;
|
||||
import com.example.ainative.entity.SysDynamicData;
|
||||
import com.example.ainative.entity.SysEntity;
|
||||
@@ -12,10 +11,6 @@ import org.springframework.web.bind.annotation.*;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 动态数据交互控制器
|
||||
* 负责接收 AI 生成的 UI 提交过来的所有结构不固定的 JSON 数据。
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/dynamic")
|
||||
@RequiredArgsConstructor
|
||||
@@ -25,22 +20,16 @@ public class DynamicDataController {
|
||||
private final SysDynamicDataRepository dataRepository;
|
||||
private final SysEntityRepository entityRepository;
|
||||
|
||||
/**
|
||||
* 根据模块 UUID 获取数据列表
|
||||
*/
|
||||
@GetMapping("/{entityId}")
|
||||
public List<SysDynamicData> getRecords(@PathVariable UUID entityId) {
|
||||
return dataRepository.findByEntityIdAndCreatorId(entityId, UserContextHolder.getCurrentUser());
|
||||
return dataRepository.findByEntityId(entityId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据模块 code 获取数据列表
|
||||
*/
|
||||
@GetMapping("/code/{entityCode}")
|
||||
public List<SysDynamicData> getRecordsByCode(@PathVariable String entityCode) {
|
||||
SysEntity entity = entityRepository.findByEntityCodeAndCreatorId(entityCode, UserContextHolder.getCurrentUser());
|
||||
SysEntity entity = entityRepository.findByEntityCode(entityCode);
|
||||
if (entity != null) {
|
||||
return dataRepository.findByEntityIdAndCreatorId(entity.getId(), UserContextHolder.getCurrentUser());
|
||||
return dataRepository.findByEntityId(entity.getId());
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@@ -21,7 +21,8 @@ public class SysEntityController {
|
||||
*/
|
||||
@GetMapping
|
||||
public List<SysEntity> getAllEntities() {
|
||||
return sysEntityRepository.findByCreatorId(com.example.ainative.config.UserContextHolder.getCurrentUser());
|
||||
// 全量返回,各用户可共用实体定义
|
||||
return sysEntityRepository.findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -29,7 +30,8 @@ public class SysEntityController {
|
||||
*/
|
||||
@GetMapping("/code/{entityCode}")
|
||||
public SysEntity getEntityByCode(@PathVariable String entityCode) {
|
||||
return sysEntityRepository.findByEntityCodeAndCreatorId(entityCode, com.example.ainative.config.UserContextHolder.getCurrentUser());
|
||||
// 按 code 全量查询,不再限制创建者
|
||||
return sysEntityRepository.findByEntityCode(entityCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,12 @@ langchain4j:
|
||||
model-name: glm-4-plus
|
||||
log-requests: true
|
||||
log-responses: true
|
||||
streaming-chat-model:
|
||||
api-key: ${ZHIPU_API_KEY:61833436cbd642ed844d0128a99b2e89.PTbpERpysO3qSf8w}
|
||||
base-url: https://open.bigmodel.cn/api/paas/v4/
|
||||
model-name: glm-4-plus
|
||||
log-requests: true
|
||||
log-responses: true
|
||||
|
||||
server:
|
||||
port: 8080
|
||||
|
||||
@@ -23,6 +23,12 @@ langchain4j:
|
||||
model-name: glm-4-plus
|
||||
log-requests: true
|
||||
log-responses: true
|
||||
streaming-chat-model:
|
||||
api-key: ${ZHIPU_API_KEY:61833436cbd642ed844d0128a99b2e89.PTbpERpysO3qSf8w}
|
||||
base-url: https://open.bigmodel.cn/api/paas/v4/
|
||||
model-name: glm-4-plus
|
||||
log-requests: true
|
||||
log-responses: true
|
||||
|
||||
server:
|
||||
port: 8080
|
||||
|
||||
Reference in New Issue
Block a user