Browse Source

feat: Implement the initial AI-native CRM backend application with Spring Boot, LangChain4j, and JPA persistence.

main
灰灰 1 week ago
commit
2adde55e08
  1. 10
      .idea/.gitignore
  2. 19
      .idea/compiler.xml
  3. 6
      .idea/encodings.xml
  4. 20
      .idea/jarRepositories.xml
  5. 12
      .idea/misc.xml
  6. 78
      pom.xml
  7. 13
      src/main/java/com/example/ainative/AiNativeApplication.java
  8. 21
      src/main/java/com/example/ainative/ai/CrmAgent.java
  9. 54
      src/main/java/com/example/ainative/ai/CrmTools.java
  10. 52
      src/main/java/com/example/ainative/ai/PersistentChatMemoryStore.java
  11. 20
      src/main/java/com/example/ainative/config/AiConfig.java
  12. 17
      src/main/java/com/example/ainative/config/UserContextHolder.java
  13. 25
      src/main/java/com/example/ainative/config/UserInterceptor.java
  14. 18
      src/main/java/com/example/ainative/config/WebMvcConfig.java
  15. 32
      src/main/java/com/example/ainative/controller/AgentController.java
  16. 116
      src/main/java/com/example/ainative/controller/DynamicDataController.java
  17. 33
      src/main/java/com/example/ainative/controller/SysEntityController.java
  18. 26
      src/main/java/com/example/ainative/entity/ChatMemoryEntity.java
  19. 33
      src/main/java/com/example/ainative/entity/SysDynamicData.java
  20. 38
      src/main/java/com/example/ainative/entity/SysEntity.java
  21. 11
      src/main/java/com/example/ainative/repository/ChatMemoryRepository.java
  22. 11
      src/main/java/com/example/ainative/repository/SysDynamicDataRepository.java
  23. 10
      src/main/java/com/example/ainative/repository/SysEntityRepository.java
  24. 28
      src/main/resources/application.yml
  25. 28
      target/classes/application.yml
  26. 17
      target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst

10
.idea/.gitignore vendored

@ -0,0 +1,10 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 已忽略包含查询文件的默认文件夹
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/

19
.idea/compiler.xml

@ -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>

6
.idea/encodings.xml

@ -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>

20
.idea/jarRepositories.xml

@ -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>

12
.idea/misc.xml

@ -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>

78
pom.xml

@ -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>

13
src/main/java/com/example/ainative/AiNativeApplication.java

@ -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);
}
}

21
src/main/java/com/example/ainative/ai/CrmAgent.java

@ -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);
}

54
src/main/java/com/example/ainative/ai/CrmTools.java

@ -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 结构是否合规。";
}
}
}

52
src/main/java/com/example/ainative/ai/PersistentChatMemoryStore.java

@ -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);
}
}

20
src/main/java/com/example/ainative/config/AiConfig.java

@ -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();
}
}

17
src/main/java/com/example/ainative/config/UserContextHolder.java

@ -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();
}
}

25
src/main/java/com/example/ainative/config/UserInterceptor.java

@ -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();
}
}

18
src/main/java/com/example/ainative/config/WebMvcConfig.java

@ -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("/**");
}
}

32
src/main/java/com/example/ainative/controller/AgentController.java

@ -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);
}
}

116
src/main/java/com/example/ainative/controller/DynamicDataController.java

@ -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);
}
}

33
src/main/java/com/example/ainative/controller/SysEntityController.java

@ -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);
}
}

26
src/main/java/com/example/ainative/entity/ChatMemoryEntity.java

@ -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;
}

33
src/main/java/com/example/ainative/entity/SysDynamicData.java

@ -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;
}

38
src/main/java/com/example/ainative/entity/SysEntity.java

@ -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;
}

11
src/main/java/com/example/ainative/repository/ChatMemoryRepository.java

@ -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);
}

11
src/main/java/com/example/ainative/repository/SysDynamicDataRepository.java

@ -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);
}

10
src/main/java/com/example/ainative/repository/SysEntityRepository.java

@ -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);
}

28
src/main/resources/application.yml

@ -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

28
target/classes/application.yml

@ -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

17
target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst

@ -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…
Cancel
Save