commit
2adde55e08
26 changed files with 748 additions and 0 deletions
@ -0,0 +1,10 @@
|
||||
# 默认忽略的文件 |
||||
/shelf/ |
||||
/workspace.xml |
||||
# 已忽略包含查询文件的默认文件夹 |
||||
/queries/ |
||||
# Datasource local storage ignored files |
||||
/dataSources/ |
||||
/dataSources.local.xml |
||||
# 基于编辑器的 HTTP 客户端请求 |
||||
/httpRequests/ |
||||
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project version="4"> |
||||
<component name="CompilerConfiguration"> |
||||
<annotationProcessing> |
||||
<profile default="true" name="Default" enabled="true" /> |
||||
<profile name="Maven default annotation processors profile" enabled="true"> |
||||
<sourceOutputDir name="target/generated-sources/annotations" /> |
||||
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" /> |
||||
<outputRelativeToContentRoot value="true" /> |
||||
<module name="ai-native-backend" /> |
||||
</profile> |
||||
</annotationProcessing> |
||||
</component> |
||||
<component name="JavacSettings"> |
||||
<option name="ADDITIONAL_OPTIONS_OVERRIDE"> |
||||
<module name="ai-native-backend" options="-parameters" /> |
||||
</option> |
||||
</component> |
||||
</project> |
||||
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project version="4"> |
||||
<component name="Encoding"> |
||||
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" /> |
||||
</component> |
||||
</project> |
||||
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project version="4"> |
||||
<component name="RemoteRepositoriesConfiguration"> |
||||
<remote-repository> |
||||
<option name="id" value="central" /> |
||||
<option name="name" value="Maven Central repository" /> |
||||
<option name="url" value="https://repo1.maven.org/maven2" /> |
||||
</remote-repository> |
||||
<remote-repository> |
||||
<option name="id" value="jboss.community" /> |
||||
<option name="name" value="JBoss Community repository" /> |
||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" /> |
||||
</remote-repository> |
||||
<remote-repository> |
||||
<option name="id" value="central" /> |
||||
<option name="name" value="Central Repository" /> |
||||
<option name="url" value="https://maven.aliyun.com/repository/public" /> |
||||
</remote-repository> |
||||
</component> |
||||
</project> |
||||
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project version="4"> |
||||
<component name="ExternalStorageConfigurationManager" enabled="true" /> |
||||
<component name="MavenProjectsManager"> |
||||
<option name="originalFiles"> |
||||
<list> |
||||
<option value="$PROJECT_DIR$/pom.xml" /> |
||||
</list> |
||||
</option> |
||||
</component> |
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="corretto-21" project-jdk-type="JavaSDK" /> |
||||
</project> |
||||
@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
<parent> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-parent</artifactId> |
||||
<version>3.2.4</version> <!-- using stable 3.2.x version --> |
||||
<relativePath/> <!-- lookup parent from repository --> |
||||
</parent> |
||||
<groupId>com.example</groupId> |
||||
<artifactId>ai-native-backend</artifactId> |
||||
<version>0.0.1-SNAPSHOT</version> |
||||
<name>ai-native-backend</name> |
||||
<description>AI-Native CRM Backend</description> |
||||
<properties> |
||||
<java.version>21</java.version> |
||||
<langchain4j.version>0.30.0</langchain4j.version> |
||||
</properties> |
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-web</artifactId> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-data-jpa</artifactId> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.postgresql</groupId> |
||||
<artifactId>postgresql</artifactId> |
||||
<scope>runtime</scope> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.projectlombok</groupId> |
||||
<artifactId>lombok</artifactId> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
<!-- LangChain4j for AI communication --> |
||||
<dependency> |
||||
<groupId>dev.langchain4j</groupId> |
||||
<artifactId>langchain4j-spring-boot-starter</artifactId> |
||||
<version>${langchain4j.version}</version> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>dev.langchain4j</groupId> |
||||
<artifactId>langchain4j-open-ai-spring-boot-starter</artifactId> |
||||
<version>${langchain4j.version}</version> |
||||
</dependency> |
||||
<!-- JSON type mapping for Hibernate --> |
||||
<dependency> |
||||
<groupId>io.hypersistence</groupId> |
||||
<artifactId>hypersistence-utils-hibernate-63</artifactId> |
||||
<version>3.7.3</version> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-test</artifactId> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
</dependencies> |
||||
<build> |
||||
<plugins> |
||||
<plugin> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-maven-plugin</artifactId> |
||||
<configuration> |
||||
<excludes> |
||||
<exclude> |
||||
<groupId>org.projectlombok</groupId> |
||||
<artifactId>lombok</artifactId> |
||||
</exclude> |
||||
</excludes> |
||||
</configuration> |
||||
</plugin> |
||||
</plugins> |
||||
</build> |
||||
</project> |
||||
@ -0,0 +1,13 @@
|
||||
package com.example.ainative; |
||||
|
||||
import org.springframework.boot.SpringApplication; |
||||
import org.springframework.boot.autoconfigure.SpringBootApplication; |
||||
|
||||
@SpringBootApplication |
||||
public class AiNativeApplication { |
||||
|
||||
public static void main(String[] args) { |
||||
SpringApplication.run(AiNativeApplication.class, args); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,21 @@
|
||||
package com.example.ainative.ai; |
||||
|
||||
import dev.langchain4j.service.MemoryId; |
||||
import dev.langchain4j.service.SystemMessage; |
||||
import dev.langchain4j.service.UserMessage; |
||||
import dev.langchain4j.service.V; |
||||
import dev.langchain4j.service.spring.AiService; |
||||
|
||||
@AiService |
||||
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. |
||||
|
||||
Current User/Tenant context ID: {{userContext}} |
||||
""") |
||||
String chat(@MemoryId String memoryId, @UserMessage String userMessage, @V("userContext") String userContext); |
||||
} |
||||
@ -0,0 +1,54 @@
|
||||
package com.example.ainative.ai; |
||||
|
||||
import com.example.ainative.entity.SysEntity; |
||||
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; |
||||
|
||||
@Slf4j |
||||
@Component |
||||
@RequiredArgsConstructor |
||||
public class CrmTools { |
||||
|
||||
private final SysEntityRepository sysEntityRepository; |
||||
|
||||
/** |
||||
* AI 工具:用于定义新的动态业务对象(Entity)。 |
||||
* 当用户试图创建新表单、新模块(比如“线索管理”、“合同管理”)时,大模型会自动决定调用本工具, |
||||
* 并将它根据系统指令生成的参数传入进来。 |
||||
* |
||||
* @param entityCode 模块唯一标识符,英文,比如 crm_lead (便于后期通过接口精确操作某模块) |
||||
* @param entityName 模块中文名称跨,比如 销售线索 |
||||
* @param jsonSchema 这里是大模型输出的最核心数据:一个合规的 JSON Schema (如属性定义、校验规则),由前端随后解析渲染 |
||||
* @return 返回一段让 AI 知道是否成功的指令结果,这个结果大模型能看到并进一步回答用户。 |
||||
*/ |
||||
@Tool("创建一个新的业务表单模块,当你确定了用户创建新功能的意图后调用。参数包括:entityCode (英文唯一标识), entityName (中文名称), jsonSchema。") |
||||
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. 实体赋值与入库
|
||||
try { |
||||
SysEntity entity = new SysEntity(); |
||||
entity.setEntityCode(entityCode); |
||||
entity.setEntityName(entityName); |
||||
// 此处直接存储大模型生成的 JSON 字符串(PostgreSQL 的 Hibernate 驱动会将其封装为 JSONB)
|
||||
entity.setSchemaDefinition(jsonSchema); |
||||
|
||||
SysEntity saved = sysEntityRepository.save(entity); |
||||
return "核心模块 [" + entityName + "] 已成功持久化并部署到系统中。后台对应的 Entity ID (UUID) 为: " + saved.getId(); |
||||
} catch (Exception e) { |
||||
log.error("保存 Schema 时出现异常", e); |
||||
return "数据库保存异常,请检查 JSON 结构是否合规。"; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,52 @@
|
||||
package com.example.ainative.ai; |
||||
|
||||
import com.example.ainative.entity.ChatMemoryEntity; |
||||
import com.example.ainative.repository.ChatMemoryRepository; |
||||
import dev.langchain4j.data.message.ChatMessage; |
||||
import dev.langchain4j.data.message.ChatMessageDeserializer; |
||||
import dev.langchain4j.data.message.ChatMessageSerializer; |
||||
import dev.langchain4j.store.memory.chat.ChatMemoryStore; |
||||
import lombok.RequiredArgsConstructor; |
||||
import org.springframework.stereotype.Component; |
||||
import org.springframework.transaction.annotation.Transactional; |
||||
|
||||
import java.util.List; |
||||
import java.util.stream.Collectors; |
||||
|
||||
@Component |
||||
@RequiredArgsConstructor |
||||
public class PersistentChatMemoryStore implements ChatMemoryStore { |
||||
|
||||
private final ChatMemoryRepository repository; |
||||
|
||||
@Override |
||||
public List<ChatMessage> getMessages(Object memoryId) { |
||||
String sessionId = (String) memoryId; |
||||
List<ChatMemoryEntity> entities = repository.findByChatSessionIdOrderByIdAsc(sessionId); |
||||
return entities.stream() |
||||
.map(entity -> ChatMessageDeserializer.messageFromJson(entity.getContent())) |
||||
.collect(Collectors.toList()); |
||||
} |
||||
|
||||
@Override |
||||
@Transactional |
||||
public void updateMessages(Object memoryId, List<ChatMessage> messages) { |
||||
String sessionId = (String) memoryId; |
||||
repository.deleteByChatSessionId(sessionId); |
||||
|
||||
List<ChatMemoryEntity> entities = messages.stream().map(message -> { |
||||
ChatMemoryEntity entity = new ChatMemoryEntity(); |
||||
entity.setChatSessionId(sessionId); |
||||
entity.setContent(ChatMessageSerializer.messageToJson(message)); |
||||
return entity; |
||||
}).collect(Collectors.toList()); |
||||
|
||||
repository.saveAll(entities); |
||||
} |
||||
|
||||
@Override |
||||
@Transactional |
||||
public void deleteMessages(Object memoryId) { |
||||
repository.deleteByChatSessionId((String) memoryId); |
||||
} |
||||
} |
||||
@ -0,0 +1,20 @@
|
||||
package com.example.ainative.config; |
||||
|
||||
import dev.langchain4j.memory.chat.ChatMemoryProvider; |
||||
import dev.langchain4j.memory.chat.MessageWindowChatMemory; |
||||
import dev.langchain4j.store.memory.chat.ChatMemoryStore; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
|
||||
@Configuration |
||||
public class AiConfig { |
||||
|
||||
@Bean |
||||
public ChatMemoryProvider chatMemoryProvider(ChatMemoryStore chatMemoryStore) { |
||||
return memoryId -> MessageWindowChatMemory.builder() |
||||
.id(memoryId) |
||||
.maxMessages(50) // Keep last 50 messages in context
|
||||
.chatMemoryStore(chatMemoryStore) |
||||
.build(); |
||||
} |
||||
} |
||||
@ -0,0 +1,17 @@
|
||||
package com.example.ainative.config; |
||||
|
||||
public class UserContextHolder { |
||||
private static final ThreadLocal<String> currentUser = new ThreadLocal<>(); |
||||
|
||||
public static void setCurrentUser(String userId) { |
||||
currentUser.set(userId); |
||||
} |
||||
|
||||
public static String getCurrentUser() { |
||||
return currentUser.get() != null ? currentUser.get() : "anonymous"; |
||||
} |
||||
|
||||
public static void clear() { |
||||
currentUser.remove(); |
||||
} |
||||
} |
||||
@ -0,0 +1,25 @@
|
||||
package com.example.ainative.config; |
||||
|
||||
import jakarta.servlet.http.HttpServletRequest; |
||||
import jakarta.servlet.http.HttpServletResponse; |
||||
import org.springframework.stereotype.Component; |
||||
import org.springframework.web.servlet.HandlerInterceptor; |
||||
|
||||
@Component |
||||
public class UserInterceptor implements HandlerInterceptor { |
||||
@Override |
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { |
||||
String userId = request.getHeader("X-Creator-Id"); |
||||
if (userId != null && !userId.isBlank()) { |
||||
UserContextHolder.setCurrentUser(userId); |
||||
} else { |
||||
UserContextHolder.setCurrentUser("anonymous"); |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { |
||||
UserContextHolder.clear(); |
||||
} |
||||
} |
||||
@ -0,0 +1,18 @@
|
||||
package com.example.ainative.config; |
||||
|
||||
import lombok.RequiredArgsConstructor; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; |
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
||||
|
||||
@Configuration |
||||
@RequiredArgsConstructor |
||||
public class WebMvcConfig implements WebMvcConfigurer { |
||||
|
||||
private final UserInterceptor userInterceptor; |
||||
|
||||
@Override |
||||
public void addInterceptors(InterceptorRegistry registry) { |
||||
registry.addInterceptor(userInterceptor).addPathPatterns("/**"); |
||||
} |
||||
} |
||||
@ -0,0 +1,32 @@
|
||||
package com.example.ainative.controller; |
||||
|
||||
import com.example.ainative.ai.CrmAgent; |
||||
import com.example.ainative.config.UserContextHolder; |
||||
import lombok.RequiredArgsConstructor; |
||||
import org.springframework.web.bind.annotation.*; |
||||
|
||||
@RestController |
||||
@RequestMapping("/api/agent") |
||||
@RequiredArgsConstructor |
||||
public class AgentController { |
||||
|
||||
private final CrmAgent crmAgent; |
||||
|
||||
/** |
||||
* 对话交互核心入口 |
||||
* 这里提供了一个传统的 REST 包装版本。当前端(如 CopilotKit 或测试客户端)发送自然语言时,会调用此处。 |
||||
* |
||||
* @param sessionId 唯一会话识别(与底层的 ChatMemory 持久化紧密对应,确保 AI 能“记住”对话) |
||||
* @param message 用户的原始需求 (比如:"帮我建一个客户关系系统的线索录入表,有姓名和手机号") |
||||
* @return AI 所思所想或执行完 @Tool 后的结果字符串反馈 |
||||
*/ |
||||
@PostMapping("/chat") |
||||
public String chat(@RequestParam String sessionId, @RequestBody String message) { |
||||
// 先从拦截器上下文中拿到刚才设置的调用者身份
|
||||
String currentUser = UserContextHolder.getCurrentUser(); |
||||
|
||||
// 传递给底层的 Agent:这背后的复杂工作流完全交由 Langchain4J 托管!
|
||||
// 如果用户的 message 意图命中“建表”,框架会在返回前自己拦截调去跑 CrmTools 里的 defineDynamicModule。
|
||||
return crmAgent.chat(sessionId, message, currentUser); |
||||
} |
||||
} |
||||
@ -0,0 +1,116 @@
|
||||
package com.example.ainative.controller; |
||||
|
||||
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 lombok.RequiredArgsConstructor; |
||||
import org.springframework.web.bind.annotation.*; |
||||
|
||||
import java.util.List; |
||||
import java.util.UUID; |
||||
|
||||
/** |
||||
* 动态数据交互控制器 |
||||
* 负责接收 AI 生成的 UI 提交过来的所有结构不固定的 JSON 数据。 |
||||
*/ |
||||
@RestController |
||||
@RequestMapping("/api/dynamic") |
||||
@RequiredArgsConstructor |
||||
@CrossOrigin(origins = "*") // 允许前端 Next.js (通常是 3000 端口) 进行跨域调用
|
||||
public class DynamicDataController { |
||||
|
||||
private final SysDynamicDataRepository dataRepository; |
||||
private final SysEntityRepository entityRepository; |
||||
|
||||
/** |
||||
* 根据模块 UUID 获取数据列表 |
||||
*/ |
||||
@GetMapping("/{entityId}") |
||||
public List<SysDynamicData> getRecords(@PathVariable UUID entityId) { |
||||
return dataRepository.findByEntityId(entityId); |
||||
} |
||||
|
||||
/** |
||||
* 根据模块 code 获取数据列表(前端动态表格主要用此接口方便查询) |
||||
*/ |
||||
@GetMapping("/code/{entityCode}") |
||||
public List<SysDynamicData> getRecordsByCode(@PathVariable String entityCode) { |
||||
SysEntity entity = entityRepository.findByEntityCode(entityCode); |
||||
if (entity != null) { |
||||
return dataRepository.findByEntityId(entity.getId()); |
||||
} |
||||
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); |
||||
} |
||||
} |
||||
@ -0,0 +1,33 @@
|
||||
package com.example.ainative.controller; |
||||
|
||||
import com.example.ainative.entity.SysEntity; |
||||
import com.example.ainative.repository.SysEntityRepository; |
||||
import lombok.RequiredArgsConstructor; |
||||
import org.springframework.web.bind.annotation.*; |
||||
|
||||
import java.util.List; |
||||
|
||||
@RestController |
||||
@RequestMapping("/api/entities") |
||||
@RequiredArgsConstructor |
||||
@CrossOrigin(origins = "*") // Allow frontend to fetch menus
|
||||
public class SysEntityController { |
||||
|
||||
private final SysEntityRepository sysEntityRepository; |
||||
|
||||
/** |
||||
* 获取所有已定义的模块列表,供左侧菜单动态渲染使用。 |
||||
*/ |
||||
@GetMapping |
||||
public List<SysEntity> getAllEntities() { |
||||
return sysEntityRepository.findAll(); |
||||
} |
||||
|
||||
/** |
||||
* 根据 code 获取单一实体定义,方便前台表格按需渲染列等动态行为。 |
||||
*/ |
||||
@GetMapping("/code/{entityCode}") |
||||
public SysEntity getEntityByCode(@PathVariable String entityCode) { |
||||
return sysEntityRepository.findByEntityCode(entityCode); |
||||
} |
||||
} |
||||
@ -0,0 +1,26 @@
|
||||
package com.example.ainative.entity; |
||||
|
||||
import jakarta.persistence.*; |
||||
import lombok.Data; |
||||
import org.hibernate.annotations.CreationTimestamp; |
||||
|
||||
import java.time.ZonedDateTime; |
||||
|
||||
@Data |
||||
@Entity |
||||
@Table(name = "chat_memory") |
||||
public class ChatMemoryEntity { |
||||
@Id |
||||
@GeneratedValue(strategy = GenerationType.IDENTITY) |
||||
private Long id; |
||||
|
||||
@Column(name = "chat_session_id", nullable = false) |
||||
private String chatSessionId; |
||||
|
||||
@Column(name = "content", nullable = false, columnDefinition = "TEXT") |
||||
private String content; |
||||
|
||||
@CreationTimestamp |
||||
@Column(name = "created_at", updatable = false) |
||||
private ZonedDateTime createdAt; |
||||
} |
||||
@ -0,0 +1,33 @@
|
||||
package com.example.ainative.entity; |
||||
|
||||
import jakarta.persistence.*; |
||||
import lombok.Data; |
||||
import org.hibernate.annotations.CreationTimestamp; |
||||
import org.hibernate.annotations.JdbcTypeCode; |
||||
import org.hibernate.type.SqlTypes; |
||||
|
||||
import java.time.ZonedDateTime; |
||||
import java.util.UUID; |
||||
|
||||
@Data |
||||
@Entity |
||||
@Table(name = "sys_dynamic_data") |
||||
public class SysDynamicData { |
||||
@Id |
||||
@GeneratedValue(strategy = GenerationType.UUID) |
||||
private UUID id; |
||||
|
||||
@Column(name = "entity_id") |
||||
private UUID entityId; |
||||
|
||||
@JdbcTypeCode(SqlTypes.JSON) |
||||
@Column(name = "data", columnDefinition = "jsonb", nullable = false) |
||||
private String data; |
||||
|
||||
@Column(name = "creator_id", length = 50) |
||||
private String creatorId; |
||||
|
||||
@CreationTimestamp |
||||
@Column(name = "created_at", updatable = false) |
||||
private ZonedDateTime createdAt; |
||||
} |
||||
@ -0,0 +1,38 @@
|
||||
package com.example.ainative.entity; |
||||
|
||||
import jakarta.persistence.*; |
||||
import lombok.Data; |
||||
import org.hibernate.annotations.CreationTimestamp; |
||||
import org.hibernate.annotations.JdbcTypeCode; |
||||
import org.hibernate.annotations.UpdateTimestamp; |
||||
import org.hibernate.type.SqlTypes; |
||||
|
||||
import java.time.ZonedDateTime; |
||||
import java.util.UUID; |
||||
|
||||
@Data |
||||
@Entity |
||||
@Table(name = "sys_entities") |
||||
public class SysEntity { |
||||
@Id |
||||
@GeneratedValue(strategy = GenerationType.UUID) |
||||
private UUID id; |
||||
|
||||
@Column(name = "entity_code", unique = true, nullable = false, length = 50) |
||||
private String entityCode; |
||||
|
||||
@Column(name = "entity_name", nullable = false, length = 100) |
||||
private String entityName; |
||||
|
||||
@JdbcTypeCode(SqlTypes.JSON) |
||||
@Column(name = "schema_definition", columnDefinition = "jsonb", nullable = false) |
||||
private String schemaDefinition; |
||||
|
||||
@CreationTimestamp |
||||
@Column(name = "created_at", updatable = false) |
||||
private ZonedDateTime createdAt; |
||||
|
||||
@UpdateTimestamp |
||||
@Column(name = "updated_at") |
||||
private ZonedDateTime updatedAt; |
||||
} |
||||
@ -0,0 +1,11 @@
|
||||
package com.example.ainative.repository; |
||||
|
||||
import com.example.ainative.entity.ChatMemoryEntity; |
||||
import org.springframework.data.jpa.repository.JpaRepository; |
||||
|
||||
import java.util.List; |
||||
|
||||
public interface ChatMemoryRepository extends JpaRepository<ChatMemoryEntity, Long> { |
||||
List<ChatMemoryEntity> findByChatSessionIdOrderByIdAsc(String chatSessionId); |
||||
void deleteByChatSessionId(String chatSessionId); |
||||
} |
||||
@ -0,0 +1,11 @@
|
||||
package com.example.ainative.repository; |
||||
|
||||
import com.example.ainative.entity.SysDynamicData; |
||||
import org.springframework.data.jpa.repository.JpaRepository; |
||||
|
||||
import java.util.List; |
||||
import java.util.UUID; |
||||
|
||||
public interface SysDynamicDataRepository extends JpaRepository<SysDynamicData, UUID> { |
||||
List<SysDynamicData> findByEntityId(UUID entityId); |
||||
} |
||||
@ -0,0 +1,10 @@
|
||||
package com.example.ainative.repository; |
||||
|
||||
import com.example.ainative.entity.SysEntity; |
||||
import org.springframework.data.jpa.repository.JpaRepository; |
||||
|
||||
import java.util.UUID; |
||||
|
||||
public interface SysEntityRepository extends JpaRepository<SysEntity, UUID> { |
||||
SysEntity findByEntityCode(String entityCode); |
||||
} |
||||
@ -0,0 +1,28 @@
|
||||
spring: |
||||
application: |
||||
name: ai-native-backend |
||||
datasource: |
||||
url: jdbc:postgresql://localhost:5432/ai_native_db |
||||
username: ai_agent_user |
||||
password: Agent@2026 |
||||
driver-class-name: org.postgresql.Driver |
||||
jpa: |
||||
hibernate: |
||||
ddl-auto: none |
||||
show-sql: true |
||||
properties: |
||||
hibernate: |
||||
format_sql: true |
||||
dialect: org.hibernate.dialect.PostgreSQLDialect |
||||
|
||||
langchain4j: |
||||
open-ai: |
||||
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 |
||||
@ -0,0 +1,28 @@
|
||||
spring: |
||||
application: |
||||
name: ai-native-backend |
||||
datasource: |
||||
url: jdbc:postgresql://localhost:5432/ai_native_db |
||||
username: ai_agent_user |
||||
password: Agent@2026 |
||||
driver-class-name: org.postgresql.Driver |
||||
jpa: |
||||
hibernate: |
||||
ddl-auto: none |
||||
show-sql: true |
||||
properties: |
||||
hibernate: |
||||
format_sql: true |
||||
dialect: org.hibernate.dialect.PostgreSQLDialect |
||||
|
||||
langchain4j: |
||||
open-ai: |
||||
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 |
||||
@ -0,0 +1,17 @@
|
||||
/Users/hui/project/ai/backend/src/main/java/com/example/ainative/entity/SysDynamicData.java |
||||
/Users/hui/project/ai/backend/src/main/java/com/example/ainative/controller/AgentController.java |
||||
/Users/hui/project/ai/backend/src/main/java/com/example/ainative/config/UserContextHolder.java |
||||
/Users/hui/project/ai/backend/src/main/java/com/example/ainative/controller/DynamicDataController.java |
||||
/Users/hui/project/ai/backend/src/main/java/com/example/ainative/ai/PersistentChatMemoryStore.java |
||||
/Users/hui/project/ai/backend/src/main/java/com/example/ainative/entity/SysEntity.java |
||||
/Users/hui/project/ai/backend/src/main/java/com/example/ainative/ai/CrmTools.java |
||||
/Users/hui/project/ai/backend/src/main/java/com/example/ainative/entity/ChatMemoryEntity.java |
||||
/Users/hui/project/ai/backend/src/main/java/com/example/ainative/controller/SysEntityController.java |
||||
/Users/hui/project/ai/backend/src/main/java/com/example/ainative/AiNativeApplication.java |
||||
/Users/hui/project/ai/backend/src/main/java/com/example/ainative/config/WebMvcConfig.java |
||||
/Users/hui/project/ai/backend/src/main/java/com/example/ainative/repository/SysDynamicDataRepository.java |
||||
/Users/hui/project/ai/backend/src/main/java/com/example/ainative/config/UserInterceptor.java |
||||
/Users/hui/project/ai/backend/src/main/java/com/example/ainative/repository/ChatMemoryRepository.java |
||||
/Users/hui/project/ai/backend/src/main/java/com/example/ainative/repository/SysEntityRepository.java |
||||
/Users/hui/project/ai/backend/src/main/java/com/example/ainative/ai/CrmAgent.java |
||||
/Users/hui/project/ai/backend/src/main/java/com/example/ainative/config/AiConfig.java |
||||
Loading…
Reference in new issue