From 170b7945d7a42e488ffa1a0f80424bb7ffc212e6 Mon Sep 17 00:00:00 2001
From: Boom <654612@qq.com>
Date: Wed, 25 Mar 2026 17:25:48 +0800
Subject: [PATCH] feat: Implement initial AI-native CRM backend with dynamic
entity and data management, AI agent tools, and core configurations.
---
.gitignore | 19 ++++
.idea/misc.xml | 2 +-
.idea/vcs.xml | 6 ++
doc/AGENT_HANDOFF.md | 56 ++++++++++
doc/Instruction.md | 49 +++++++++
pom.xml | 6 +-
.../com/example/ainative/ai/CrmAgent.java | 29 +++--
.../com/example/ainative/ai/CrmTools.java | 101 +++++++++++++-----
.../ainative/controller/AgentController.java | 6 +-
.../controller/DynamicDataController.java | 82 ++------------
.../controller/SysEntityController.java | 11 +-
.../example/ainative/entity/SysEntity.java | 3 +
.../repository/SysDynamicDataRepository.java | 1 +
.../repository/SysEntityRepository.java | 3 +
src/main/resources/application.yml | 7 +-
target/classes/application.yml | 7 +-
16 files changed, 267 insertions(+), 121 deletions(-)
create mode 100644 .gitignore
create mode 100644 .idea/vcs.xml
create mode 100644 doc/AGENT_HANDOFF.md
create mode 100644 doc/Instruction.md
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4d458d2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,19 @@
+# Maven
+target/
+pom.xml.tag
+pom.xml.releaseBackup
+pom.xml.versionsBackup
+pom.xml.restore
+release.properties
+dependency-reduced-pom.xml
+buildNumber.properties
+.mvn/timing.properties
+# https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+.idea/
+*.iml
+*.iws
+.idea_modules/
+
+# OS
+.DS_Store
+Thumbs.db
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 6546f14..56c7380 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -8,5 +8,5 @@
-
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/doc/AGENT_HANDOFF.md b/doc/AGENT_HANDOFF.md
new file mode 100644
index 0000000..66eac74
--- /dev/null
+++ b/doc/AGENT_HANDOFF.md
@@ -0,0 +1,56 @@
+# AI-Native CRM: Agent Handoff Guide
+
+> [!IMPORTANT]
+> This document is for the AI Agent to quickly understand the project context, architecture, and current status during session resume.
+
+## 🎯 Project Vision
+An **AI-Native CRM** where business modules and data are managed through AI-driven messages and tools, rather than traditional REST parameters.
+
+---
+
+## 🏗️ Architecture Overview
+
+### Backend (`/backend`)
+* **Env**: JDK 19 + Spring Boot 3.2.4 + PostgreSQL.
+* **Key Logic**:
+ * `CrmTools.java`: **The Message Hub**. Contains `@Tool` methods for **Define, Save, and Delete**. Directly interacts with Repositories.
+ * `AgentController.java`: **Primary Entry Point**. Handles natural language commands or "Business Fact Reports".
+ * **AI-Only Policy**: Create/Update/Delete endpoints have been removed from traditional controllers to force AI-driven flows.
+
+### Frontend (`/frontend`)
+* **Env**: Next.js 15 (App Router).
+* **Components**:
+ * `DynamicForm.tsx`: Shared component supporting **Entity Associations (`x-link-entity`)**, Enums, and Format constraints.
+ * `EntityDataPage` (`/data/[entityCode]`): Full CRUD UI. Actions (Edit/Delete) route through AI messages.
+
+---
+
+## 🛠️ Key File Responsibilities
+
+| File | Role |
+| :--- | :--- |
+| `backend/.../CrmTools.java` | **Source of Truth** for all data-changing tools. |
+| `frontend/src/components/DynamicForm.tsx` | Reusable Form Renderer (Schema + AI messaging). |
+| `frontend/src/app/data/[entityCode]/page.tsx` | Dynamic List View with Agent-driven Actions. |
+
+---
+
+## 🔄 Core Conventions
+
+1. **AI-First Mutation**: DO NOT create manual REST services for data mutation. Use the AI Tool interface.
+2. **Schema Extensions**:
+ * Associations: `x-link-entity: 'module_code'`.
+ * Constraints: `enum`, `pattern`, `format: 'date'`.
+3. **Fact-Based Messaging**: Frontend should report business facts (e.g., "User filled form X") and let the AI decide the tool to call.
+
+---
+
+## 🚀 Current Progress & Next Steps
+- [x] AI-Native CRUD implemented (Agent-driven Insert/Update/Delete).
+- [x] Entity Associations & Selectors supported.
+- [x] Unified DynamicForm for Generation & Management.
+- [ ] Next: Implement Advanced Search & Filtering via AI Query generation.
+- [ ] Next: Dashboard Analytics module for dynamic data summary.
+
+---
+*Updated at: 2026-03-25. Follow this guide for AI-Native CRM development.*
diff --git a/doc/Instruction.md b/doc/Instruction.md
new file mode 100644
index 0000000..e25a492
--- /dev/null
+++ b/doc/Instruction.md
@@ -0,0 +1,49 @@
+# AI-Native CRM 系统开发手册
+
+## 🎯 系统定位 (Core Vision)
+本系统是一款 **AI-Native (原生 AI)** 的声明式 CRM。与传统 CRM 的本质区别在于:
+* **非硬编码业务逻辑**:业务模块、字段定义和 UI 呈现均由 AI 动态生成并持久化。
+* **零研发排期**:业务专家通过自然语言描述需求,即可立刻生成生产可用的功能模块。
+
+---
+
+## 🚀 核心特性 (Key Features)
+
+### 1. 生成式 UI (Generative UI)
+* 集成 **CopilotKit**,在前端实现 AI 对应用的深度操控。
+* 支持通过自然语言一键渲染(Render)基于 JSON Schema 的复杂表单。
+* **UniversalModuleRenderer**:万能组件渲染引擎,适配多种动态业务场景。
+
+### 2. 动态多租户架构
+* 后端使用 `SysEntity` (元数据) 和 `SysDynamicData` (实例数据) 支撑无限扩展。
+* 支持不同租户、不同模块的字段在运行时动态注入,无需繁琐的数据库迁移动作。
+
+### 3. AI 交互中枢 (AI Hub)
+* **后端驱动**:基于 **LangChain4j** 构建 Java 原生 AI 逻辑,支持 RAG 及复杂工作流。
+* **会话持久化**:原生支持 AI 会话的数据库存储,重启不丢失交互上下文。
+
+---
+
+## 📋 技术基准 (Benchmarks & Tech Stack)
+
+### Backend (Java Ecosystem)
+* **Runner**: OpenJDK 19
+* **Framework**: Spring Boot 3.2.4 (Latest Stable)
+* **Persistence**: Spring Data JPA + Hibernate 6.x
+* **Database**: PostgreSQL (Support JSONB for Dynamic Data)
+* **AI SDK**: LangChain4j 0.30.0
+* **Build Tool**: Maven
+
+### Frontend (Modern Web)
+* **Framework**: Next.js (App Router)
+* **Language**: TypeScript
+* **Styling**: Tailwind CSS + Shadcn UI (Visual Esthetics)
+* **Copilot**: CopilotKit (React SDK + Popup UI)
+* **Font**: Geist (Advanced Typography)
+
+---
+
+## 🛠 开发规范
+1. **动态性优先**:任何新功能应考虑是否可通过配置或 AI 生成,而非硬编码 Controller。
+2. **Schema 为王**:业务定义必须严格遵循 JSON Schema 标准,以保证前后端渲染的一致性。
+3. **Prompt 工程**:系统 Prompt 存储于前端 `page.tsx` 或后端专用 AI 服务类中,需定期维护。
diff --git a/pom.xml b/pom.xml
index b59ddcb..5e4e333 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.boot
spring-boot-starter-parent
- 3.2.4
+ 3.5.12
com.example
@@ -14,8 +14,8 @@
ai-native-backend
AI-Native CRM Backend
- 21
- 0.30.0
+ 19
+ 1.12.2-beta22
diff --git a/src/main/java/com/example/ainative/ai/CrmAgent.java b/src/main/java/com/example/ainative/ai/CrmAgent.java
index 4d6089d..f9e4588 100644
--- a/src/main/java/com/example/ainative/ai/CrmAgent.java
+++ b/src/main/java/com/example/ainative/ai/CrmAgent.java
@@ -6,16 +6,33 @@ import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;
import dev.langchain4j.service.spring.AiService;
-@AiService
+@AiService(tools = "crmTools")
public interface CrmAgent {
@SystemMessage("""
- You are a smart AI assistant embedded within a modern enterprise CRM system.
- Your goal is to help the user configure business modules, dynamic forms, and insert data.
- You have tools to define database schema structures (JSON Schema) and manipulate dynamic data.
- Always respond concisely. If you need to manipulate the UI, explain what you are doing.
+ ## 角色与愿景
+ 你是一个 AI 原生 CRM 系统的核心引擎。你的任务是通过自然语言管理业务模块(Schema)和动态数据,替代传统的 REST 接口。
- Current User/Tenant context ID: {{userContext}}
+ ## 核心运行准则(严禁违反)
+ 1. **执行重于对话**:系统已禁用传统的增删改接口,所有数据变更必须通过调用工具实现。
+ 2. **禁止虚假模拟**:严禁在未成功调用工具的情况下,回复任何暗示操作已完成的内容(如“已保存”、“已创建”)。
+ 3. **事实驱动**:前端会报告“业务事实”(如:用户提交了表单内容),你必须根据事实判断该调用哪个工具(如:insertDynamicData)。
+
+ ## 建模协议 (defineDynamicModule)
+ 当定义或更新模块结构时,生成的 JSON Schema 需遵循:
+ - **实体关联**:若字段需关联其他实体,必须在属性中加入 `"x-link-entity": "目标实体Code"`。
+ - **约束增强**:优先使用 `enum` (下拉选项)、`pattern` (正则校验)、`format: "date"` 等标准 JSON Schema 约束。
+ - **命名规范**:entityCode 采用下划线命名法(如:student_course)。
+
+ ## 数据操作协议
+ - **入库 (insert)**:将用户提供的或事实报告中的信息提取为 JSON 字符串。
+ - **物理删除 (delete)**:仅在用户明确指令或业务流程需要时执行。
+
+ ## 输出风格
+ - 保持简洁。
+ - 必须在工具执行成功后,再简要反馈执行结果或后续 UI 操作建议。
+
+ 当前租户上下文 ID: {{userContext}}
""")
String chat(@MemoryId String memoryId, @UserMessage String userMessage, @V("userContext") String userContext);
}
diff --git a/src/main/java/com/example/ainative/ai/CrmTools.java b/src/main/java/com/example/ainative/ai/CrmTools.java
index 523a815..20d47d3 100644
--- a/src/main/java/com/example/ainative/ai/CrmTools.java
+++ b/src/main/java/com/example/ainative/ai/CrmTools.java
@@ -1,54 +1,99 @@
package com.example.ainative.ai;
+import com.example.ainative.config.UserContextHolder;
+
+import com.example.ainative.entity.SysDynamicData;
import com.example.ainative.entity.SysEntity;
+import com.example.ainative.repository.SysDynamicDataRepository;
import com.example.ainative.repository.SysEntityRepository;
import dev.langchain4j.agent.tool.Tool;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
-
-import java.util.UUID;
+import org.springframework.transaction.annotation.Transactional;
@Slf4j
-@Component
+@Component("crmTools")
@RequiredArgsConstructor
public class CrmTools {
private final SysEntityRepository sysEntityRepository;
+ private final SysDynamicDataRepository sysDynamicDataRepository;
- /**
- * AI 工具:用于定义新的动态业务对象(Entity)。
- * 当用户试图创建新表单、新模块(比如“线索管理”、“合同管理”)时,大模型会自动决定调用本工具,
- * 并将它根据系统指令生成的参数传入进来。
- *
- * @param entityCode 模块唯一标识符,英文,比如 crm_lead (便于后期通过接口精确操作某模块)
- * @param entityName 模块中文名称跨,比如 销售线索
- * @param jsonSchema 这里是大模型输出的最核心数据:一个合规的 JSON Schema (如属性定义、校验规则),由前端随后解析渲染
- * @return 返回一段让 AI 知道是否成功的指令结果,这个结果大模型能看到并进一步回答用户。
- */
- @Tool("创建一个新的业务表单模块,当你确定了用户创建新功能的意图后调用。参数包括:entityCode (英文唯一标识), entityName (中文名称), jsonSchema。")
+ @Transactional
+ @Tool("核心业务处理:根据消息意图定义或执行模型变更 (Schema)。支持以下高级能力:\n" +
+ "1. [实体关联]:若字段需要关联其他实体,需在 Schema 属性中加入 `x-link-entity: '目标实体Code'`。\n" +
+ "2. [值约束]:支持标准 JSON Schema 的 `enum` (下拉选择)、`pattern` (正则校验)、`format` (如 date, email)、`minLength` 等约束。\n" +
+ "3. [示例]:商品关联分类,分类字段可定义为 { 'type': 'string', 'x-link-entity': 'category' }")
public String defineDynamicModule(String entityCode, String entityName, String jsonSchema) {
- log.info("Agent 正在调用工具执行创建新模块: Code={}, Name={}", entityCode, entityName);
-
- // 1. 业务校验:防重和容错
- SysEntity existing = sysEntityRepository.findByEntityCode(entityCode);
- if (existing != null) {
- return "模块代码 [" + entityCode + "] 已存在,创建失败,请让用户考虑使用其他代码名称。";
- }
-
- // 2. 实体赋值与入库
+ log.info("Agent Tool 正在处理消息:定义模块: Code={}, Name={}", entityCode, entityName);
try {
+ SysEntity existing = sysEntityRepository.findByEntityCode(entityCode);
+ if (existing != null) {
+ existing.setSchemaDefinition(jsonSchema);
+ existing.setEntityName(entityName);
+ existing.setCreatorId(UserContextHolder.getCurrentUser());
+ sysEntityRepository.save(existing);
+ return "AI 已更新现有模块 [" + entityName + "]。";
+ }
+
SysEntity entity = new SysEntity();
entity.setEntityCode(entityCode);
entity.setEntityName(entityName);
- // 此处直接存储大模型生成的 JSON 字符串(PostgreSQL 的 Hibernate 驱动会将其封装为 JSONB)
entity.setSchemaDefinition(jsonSchema);
-
+ entity.setCreatorId(UserContextHolder.getCurrentUser());
SysEntity saved = sysEntityRepository.save(entity);
- return "核心模块 [" + entityName + "] 已成功持久化并部署到系统中。后台对应的 Entity ID (UUID) 为: " + saved.getId();
+ return "AI 已成功解析并持久化业务模块 [" + entityName + " (" + entityCode + ")],ID: " + saved.getId();
} catch (Exception e) {
- log.error("保存 Schema 时出现异常", e);
- return "数据库保存异常,请检查 JSON 结构是否合规。";
+ log.error("AI 处理定义请求失败: {}", e.getMessage());
+ return "处理失败: " + e.getMessage();
+ }
+ }
+
+ /**
+ * AI 工具封装:直接处理业务数据入库意图。
+ */
+ @Transactional
+ @Tool("核心业务处理:将业务消息中的动态数据持久化到指定模块。参数:entityCode (模块代码), jsonData (JSON 数据)。")
+ public String insertDynamicData(String entityCode, String jsonData) {
+ log.info("Agent Tool 正在处理消息:将数据保存至模块: Code={}", entityCode);
+ try {
+ SysEntity entity = sysEntityRepository.findByEntityCode(entityCode);
+ if (entity == null) {
+ return "AI 无法找到模块 [" + entityCode + "],请先下达创建模块的指令。";
+ }
+
+ SysDynamicData record = new SysDynamicData();
+ record.setEntityId(entity.getId());
+ record.setData(jsonData);
+ record.setCreatorId(UserContextHolder.getCurrentUser());
+ SysDynamicData saved = sysDynamicDataRepository.save(record);
+ return "AI 已自动完成数据入库,记录主键 ID: " + saved.getId();
+ } catch (Exception e) {
+ log.error("AI 自动化入库失败: {}", e.getMessage());
+ return "入库失败: " + e.getMessage();
+ }
+ }
+
+ /**
+ * AI 工具封装:删除指定的业务数据。
+ */
+ @Transactional
+ @Tool("核心业务处理:根据 ID 物理删除指定的业务记录。参数:id (记录的唯一 UUID)。")
+ public String deleteDynamicData(String id) {
+ log.info("Agent Tool 正在处理消息:删除记录 ID: {}", id);
+ try {
+ java.util.UUID uuid = java.util.UUID.fromString(id);
+ if (!sysDynamicDataRepository.existsById(uuid)) {
+ return "AI 无法找到 ID 为 [" + id + "] 的记录,可能已被删除。";
+ }
+ sysDynamicDataRepository.deleteById(uuid);
+ return "AI 已成功从系统中移除该条记录 (ID: " + id + ")。";
+ } catch (Exception e) {
+ log.error("AI 物理删除失败: {}", e.getMessage());
+ return "删除操作遇到技术故障: " + e.getMessage();
}
}
}
+
+
diff --git a/src/main/java/com/example/ainative/controller/AgentController.java b/src/main/java/com/example/ainative/controller/AgentController.java
index 81e1bc4..89eb3f9 100644
--- a/src/main/java/com/example/ainative/controller/AgentController.java
+++ b/src/main/java/com/example/ainative/controller/AgentController.java
@@ -3,11 +3,13 @@ package com.example.ainative.controller;
import com.example.ainative.ai.CrmAgent;
import com.example.ainative.config.UserContextHolder;
import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/agent")
@RequiredArgsConstructor
+@Slf4j
public class AgentController {
private final CrmAgent crmAgent;
@@ -15,7 +17,7 @@ public class AgentController {
/**
* 对话交互核心入口
* 这里提供了一个传统的 REST 包装版本。当前端(如 CopilotKit 或测试客户端)发送自然语言时,会调用此处。
- *
+ *
* @param sessionId 唯一会话识别(与底层的 ChatMemory 持久化紧密对应,确保 AI 能“记住”对话)
* @param message 用户的原始需求 (比如:"帮我建一个客户关系系统的线索录入表,有姓名和手机号")
* @return AI 所思所想或执行完 @Tool 后的结果字符串反馈
@@ -24,7 +26,7 @@ public class AgentController {
public String 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);
diff --git a/src/main/java/com/example/ainative/controller/DynamicDataController.java b/src/main/java/com/example/ainative/controller/DynamicDataController.java
index 1404997..db5ca58 100644
--- a/src/main/java/com/example/ainative/controller/DynamicDataController.java
+++ b/src/main/java/com/example/ainative/controller/DynamicDataController.java
@@ -1,5 +1,6 @@
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;
@@ -18,7 +19,7 @@ import java.util.UUID;
@RestController
@RequestMapping("/api/dynamic")
@RequiredArgsConstructor
-@CrossOrigin(origins = "*") // 允许前端 Next.js (通常是 3000 端口) 进行跨域调用
+@CrossOrigin(origins = "*")
public class DynamicDataController {
private final SysDynamicDataRepository dataRepository;
@@ -29,88 +30,19 @@ public class DynamicDataController {
*/
@GetMapping("/{entityId}")
public List getRecords(@PathVariable UUID entityId) {
- return dataRepository.findByEntityId(entityId);
+ return dataRepository.findByEntityIdAndCreatorId(entityId, UserContextHolder.getCurrentUser());
}
/**
- * 根据模块 code 获取数据列表(前端动态表格主要用此接口方便查询)
+ * 根据模块 code 获取数据列表
*/
@GetMapping("/code/{entityCode}")
public List getRecordsByCode(@PathVariable String entityCode) {
- SysEntity entity = entityRepository.findByEntityCode(entityCode);
+ SysEntity entity = entityRepository.findByEntityCodeAndCreatorId(entityCode, UserContextHolder.getCurrentUser());
if (entity != null) {
- return dataRepository.findByEntityId(entity.getId());
+ return dataRepository.findByEntityIdAndCreatorId(entity.getId(), UserContextHolder.getCurrentUser());
}
return List.of();
}
-
- /**
- * 自动注册/获取动态模块接口:解决前端渲染表单和后端实体记录未创建的时序问题
- * 允许前端在提交实际数据前,主动将推断出的模块结构注册到数据库。
- */
- @PostMapping("/schema")
- public SysEntity defineSchema(@RequestBody SysEntity requestedEntity) {
- SysEntity entity = entityRepository.findByEntityCode(requestedEntity.getEntityCode());
- if (entity == null) {
- entity = new SysEntity();
- entity.setEntityCode(requestedEntity.getEntityCode());
- entity.setEntityName(requestedEntity.getEntityName());
- entity.setSchemaDefinition(requestedEntity.getSchemaDefinition());
- return entityRepository.save(entity);
- }
- return entity;
- }
-
- /**
- * 核心接口:按 entityCode 直接入库
- * 前端不需要知道复杂的 UUID,只需根据 AI 生成的 crm_lead 等 Code 即可提交。
- */
- @PostMapping("/code/{entityCode}")
- public SysDynamicData createRecordByCode(@PathVariable String entityCode, @RequestBody String jsonData) {
- // 1. 先通过 Code 找到模块定义的 ID
- SysEntity entity = entityRepository.findByEntityCode(entityCode);
- if (entity == null) {
- throw new RuntimeException("模块 " + entityCode + " 尚未定义,无法入库。请先让 AI 创建该模块。");
- }
-
- // 2. 构造动态数据记录
- SysDynamicData record = new SysDynamicData();
- record.setEntityId(entity.getId());
- record.setData(jsonData); // 这里的 String 在 JSONB 映射下会自动存为二进制 JSON 格式
- record.setCreatorId(UserContextHolder.getCurrentUser());
-
- return dataRepository.save(record);
- }
-
- /**
- * 原始的 UUID 入库接口 (保留备用)
- */
- @PostMapping("/{entityId}")
- public SysDynamicData createRecord(@PathVariable UUID entityId, @RequestBody String jsonData) {
- SysDynamicData record = new SysDynamicData();
- record.setEntityId(entityId);
- record.setData(jsonData);
- record.setCreatorId(UserContextHolder.getCurrentUser());
-
- return dataRepository.save(record);
- }
-
- /**
- * 修改现有记录
- */
- @PutMapping("/{id}")
- public SysDynamicData updateRecord(@PathVariable UUID id, @RequestBody String jsonData) {
- return dataRepository.findById(id).map(record -> {
- record.setData(jsonData);
- return dataRepository.save(record);
- }).orElseThrow(() -> new RuntimeException("Record not found"));
- }
-
- /**
- * 删除现有记录
- */
- @DeleteMapping("/{id}")
- public void deleteRecord(@PathVariable UUID id) {
- dataRepository.deleteById(id);
- }
}
+
diff --git a/src/main/java/com/example/ainative/controller/SysEntityController.java b/src/main/java/com/example/ainative/controller/SysEntityController.java
index 4ddbd11..680daea 100644
--- a/src/main/java/com/example/ainative/controller/SysEntityController.java
+++ b/src/main/java/com/example/ainative/controller/SysEntityController.java
@@ -10,24 +10,27 @@ import java.util.List;
@RestController
@RequestMapping("/api/entities")
@RequiredArgsConstructor
-@CrossOrigin(origins = "*") // Allow frontend to fetch menus
+@CrossOrigin(origins = "*")
public class SysEntityController {
private final SysEntityRepository sysEntityRepository;
/**
* 获取所有已定义的模块列表,供左侧菜单动态渲染使用。
+ * 该接口属于传统查询,用于 UI 展现。
*/
@GetMapping
public List getAllEntities() {
- return sysEntityRepository.findAll();
+ return sysEntityRepository.findByCreatorId(com.example.ainative.config.UserContextHolder.getCurrentUser());
}
/**
- * 根据 code 获取单一实体定义,方便前台表格按需渲染列等动态行为。
+ * 根据 code 获取单一实体定义。
*/
@GetMapping("/code/{entityCode}")
public SysEntity getEntityByCode(@PathVariable String entityCode) {
- return sysEntityRepository.findByEntityCode(entityCode);
+ return sysEntityRepository.findByEntityCodeAndCreatorId(entityCode, com.example.ainative.config.UserContextHolder.getCurrentUser());
}
}
+
+
diff --git a/src/main/java/com/example/ainative/entity/SysEntity.java b/src/main/java/com/example/ainative/entity/SysEntity.java
index 25264e9..52c67a8 100644
--- a/src/main/java/com/example/ainative/entity/SysEntity.java
+++ b/src/main/java/com/example/ainative/entity/SysEntity.java
@@ -28,6 +28,9 @@ public class SysEntity {
@Column(name = "schema_definition", columnDefinition = "jsonb", nullable = false)
private String schemaDefinition;
+ @Column(name = "creator_id", length = 50)
+ private String creatorId;
+
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private ZonedDateTime createdAt;
diff --git a/src/main/java/com/example/ainative/repository/SysDynamicDataRepository.java b/src/main/java/com/example/ainative/repository/SysDynamicDataRepository.java
index 8973ea7..ec59924 100644
--- a/src/main/java/com/example/ainative/repository/SysDynamicDataRepository.java
+++ b/src/main/java/com/example/ainative/repository/SysDynamicDataRepository.java
@@ -8,4 +8,5 @@ import java.util.UUID;
public interface SysDynamicDataRepository extends JpaRepository {
List findByEntityId(UUID entityId);
+ List findByEntityIdAndCreatorId(UUID entityId, String creatorId);
}
diff --git a/src/main/java/com/example/ainative/repository/SysEntityRepository.java b/src/main/java/com/example/ainative/repository/SysEntityRepository.java
index 0c63d42..ce75695 100644
--- a/src/main/java/com/example/ainative/repository/SysEntityRepository.java
+++ b/src/main/java/com/example/ainative/repository/SysEntityRepository.java
@@ -3,8 +3,11 @@ package com.example.ainative.repository;
import com.example.ainative.entity.SysEntity;
import org.springframework.data.jpa.repository.JpaRepository;
+import java.util.List;
import java.util.UUID;
public interface SysEntityRepository extends JpaRepository {
SysEntity findByEntityCode(String entityCode);
+ List findByCreatorId(String creatorId);
+ SysEntity findByEntityCodeAndCreatorId(String entityCode, String creatorId);
}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index aea9df9..7ad2acb 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -9,7 +9,7 @@ spring:
jpa:
hibernate:
ddl-auto: none
- show-sql: true
+ show-sql: false
properties:
hibernate:
format_sql: true
@@ -26,3 +26,8 @@ langchain4j:
server:
port: 8080
+
+logging:
+ level:
+ dev.ai4j: debug
+ org.hibernate.sql: error
diff --git a/target/classes/application.yml b/target/classes/application.yml
index aea9df9..7ad2acb 100644
--- a/target/classes/application.yml
+++ b/target/classes/application.yml
@@ -9,7 +9,7 @@ spring:
jpa:
hibernate:
ddl-auto: none
- show-sql: true
+ show-sql: false
properties:
hibernate:
format_sql: true
@@ -26,3 +26,8 @@ langchain4j:
server:
port: 8080
+
+logging:
+ level:
+ dev.ai4j: debug
+ org.hibernate.sql: error