Compare commits
9 Commits
codex/原型设计
...
8f7ffd6cc9
| Author | SHA1 | Date | |
|---|---|---|---|
| 8f7ffd6cc9 | |||
| 2dd242c54b | |||
| 29f132e48c | |||
| 32925bad8e | |||
| 8596f5074b | |||
| 5e0212d2a0 | |||
| 041ed0b446 | |||
| d9cf838ace | |||
| 07ad8bb36b |
60
common-agent-agent/pom.xml
Normal file
60
common-agent-agent/pom.xml
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<?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>com.bruce</groupId>
|
||||||
|
<artifactId>common-agent-parent</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>common-agent-agent</artifactId>
|
||||||
|
<name>common-agent-agent</name>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.bruce</groupId>
|
||||||
|
<artifactId>common-agent-common</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.bruce</groupId>
|
||||||
|
<artifactId>common-agent-rag</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.bruce</groupId>
|
||||||
|
<artifactId>common-agent-modelprovider</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-spring-boot4-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.postgresql</groupId>
|
||||||
|
<artifactId>postgresql</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package com.bruce.agent.controller;
|
||||||
|
|
||||||
|
import com.bruce.agent.dto.request.AgentSessionCreateDTO;
|
||||||
|
import com.bruce.agent.dto.request.AgentSessionMessageCreateDTO;
|
||||||
|
import com.bruce.agent.service.IAgentMessageService;
|
||||||
|
import com.bruce.agent.service.IAgentSessionService;
|
||||||
|
import com.bruce.agent.service.IAgentWorkspaceService;
|
||||||
|
import com.bruce.agent.vo.AgentMessageVO;
|
||||||
|
import com.bruce.agent.vo.AgentSessionDetailVO;
|
||||||
|
import com.bruce.agent.vo.AgentWorkspaceVO;
|
||||||
|
import com.bruce.common.domain.model.RequestResult;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/agent-sessions")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AgentSessionController {
|
||||||
|
|
||||||
|
private final IAgentSessionService agentSessionService;
|
||||||
|
private final IAgentMessageService agentMessageService;
|
||||||
|
private final IAgentWorkspaceService agentWorkspaceService;
|
||||||
|
|
||||||
|
@PostMapping("/create")
|
||||||
|
public RequestResult<Boolean> create(@RequestBody AgentSessionCreateDTO request) {
|
||||||
|
return RequestResult.success(agentSessionService.createSession(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/detail")
|
||||||
|
public RequestResult<AgentSessionDetailVO> detail(@RequestParam("id") Long id) {
|
||||||
|
return RequestResult.success(agentSessionService.getDetailById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{sessionId}/messages")
|
||||||
|
public RequestResult<List<AgentMessageVO>> messages(@PathVariable("sessionId") Long sessionId) {
|
||||||
|
return RequestResult.success(agentMessageService.listBySessionId(sessionId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{sessionId}/messages")
|
||||||
|
public RequestResult<Boolean> appendMessage(@PathVariable("sessionId") Long sessionId,
|
||||||
|
@RequestBody AgentSessionMessageCreateDTO request) {
|
||||||
|
request.setSessionId(sessionId);
|
||||||
|
return RequestResult.success(agentMessageService.appendMessage(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/workspace")
|
||||||
|
public RequestResult<AgentWorkspaceVO> workspace(@RequestParam("agentId") Long agentId,
|
||||||
|
@RequestParam(value = "sessionId", required = false) Long sessionId) {
|
||||||
|
return RequestResult.success(agentWorkspaceService.getWorkspace(agentId, sessionId));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.bruce.agent.dto.request;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class AgentSessionCreateDTO {
|
||||||
|
private Long agentId;
|
||||||
|
private String sessionCode;
|
||||||
|
private Long workflowRunId;
|
||||||
|
private String title;
|
||||||
|
private String metadataJson;
|
||||||
|
private String remark;
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.bruce.agent.dto.request;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class AgentSessionMessageCreateDTO {
|
||||||
|
private Long sessionId;
|
||||||
|
private String role;
|
||||||
|
private String content;
|
||||||
|
private String citationJson;
|
||||||
|
private Integer tokenCount;
|
||||||
|
private String requestId;
|
||||||
|
private String remark;
|
||||||
|
}
|
||||||
@@ -11,6 +11,8 @@ public class AgentChatResponse {
|
|||||||
private String agentName;
|
private String agentName;
|
||||||
private Long storeId;
|
private Long storeId;
|
||||||
private String storeName;
|
private String storeName;
|
||||||
|
private Long sessionId;
|
||||||
|
private String sessionCode;
|
||||||
private String answer;
|
private String answer;
|
||||||
private String modelRequestId;
|
private String modelRequestId;
|
||||||
private List<ReferenceChunk> references;
|
private List<ReferenceChunk> references;
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.bruce.agent.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.bruce.common.domain.model.BaseEntity;
|
||||||
|
import com.bruce.common.typehandler.PgJsonbStringTypeHandler;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@TableName("agent_capability_binding")
|
||||||
|
public class AgentCapabilityBinding extends BaseEntity {
|
||||||
|
|
||||||
|
@TableField("owner_type")
|
||||||
|
private String ownerType;
|
||||||
|
|
||||||
|
@TableField("owner_id")
|
||||||
|
private Long ownerId;
|
||||||
|
|
||||||
|
@TableField("capability_type")
|
||||||
|
private String capabilityType;
|
||||||
|
|
||||||
|
@TableField("capability_id")
|
||||||
|
private Long capabilityId;
|
||||||
|
|
||||||
|
private Boolean enabled;
|
||||||
|
|
||||||
|
@TableField(value = "config_json", typeHandler = PgJsonbStringTypeHandler.class)
|
||||||
|
private String configJson;
|
||||||
|
|
||||||
|
private String remark;
|
||||||
|
}
|
||||||
@@ -25,5 +25,6 @@ public class AgentDefinition extends BaseEntity {
|
|||||||
|
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
|
@TableField("remark")
|
||||||
private String remark;
|
private String remark;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.bruce.agent.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.bruce.common.domain.model.BaseEntity;
|
||||||
|
import com.bruce.common.typehandler.PgJsonbStringTypeHandler;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@TableName("agent_message")
|
||||||
|
public class AgentMessage extends BaseEntity {
|
||||||
|
|
||||||
|
@TableField("session_id")
|
||||||
|
private Long sessionId;
|
||||||
|
|
||||||
|
private String role;
|
||||||
|
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@TableField(value = "citation_json", typeHandler = PgJsonbStringTypeHandler.class)
|
||||||
|
private String citationJson;
|
||||||
|
|
||||||
|
@TableField("token_count")
|
||||||
|
private Integer tokenCount;
|
||||||
|
|
||||||
|
private String remark;
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.bruce.agent.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.bruce.common.domain.model.BaseEntity;
|
||||||
|
import com.bruce.common.typehandler.PgJsonbStringTypeHandler;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@TableName("agent_session")
|
||||||
|
public class AgentSession extends BaseEntity {
|
||||||
|
|
||||||
|
@TableField("session_code")
|
||||||
|
private String sessionCode;
|
||||||
|
|
||||||
|
@TableField("agent_id")
|
||||||
|
private Long agentId;
|
||||||
|
|
||||||
|
@TableField("workflow_run_id")
|
||||||
|
private Long workflowRunId;
|
||||||
|
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
@TableField(value = "metadata_json", typeHandler = PgJsonbStringTypeHandler.class)
|
||||||
|
private String metadataJson;
|
||||||
|
|
||||||
|
private String remark;
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package com.bruce.agent.factory;
|
||||||
|
|
||||||
|
import com.bruce.agent.dto.request.AgentDefinitionSaveRequest;
|
||||||
|
import com.bruce.agent.dto.response.AgentDefinitionResponse;
|
||||||
|
import com.bruce.agent.entity.AgentDefinition;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Agent 定义工厂,统一处理请求、实体和返回对象转换。
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class AgentDefinitionFactory {
|
||||||
|
|
||||||
|
public AgentDefinition toEntity(AgentDefinitionSaveRequest request) {
|
||||||
|
if (request == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
AgentDefinition entity = new AgentDefinition();
|
||||||
|
entity.setId(request.getId());
|
||||||
|
entity.setAgentCode(trimToNull(request.getAgentCode()));
|
||||||
|
entity.setAgentName(trimToNull(request.getAgentName()));
|
||||||
|
entity.setSystemPrompt(trimToNull(request.getSystemPrompt()));
|
||||||
|
entity.setStoreId(request.getStoreId());
|
||||||
|
entity.setStatus(trimToNull(request.getStatus()));
|
||||||
|
entity.setRemark(trimToNull(request.getRemark()));
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AgentDefinitionResponse toResponse(AgentDefinition entity) {
|
||||||
|
if (entity == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
AgentDefinitionResponse response = new AgentDefinitionResponse();
|
||||||
|
BeanUtils.copyProperties(entity, response);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<AgentDefinitionResponse> toResponses(List<AgentDefinition> entities) {
|
||||||
|
return entities == null ? List.of() : entities.stream().map(this::toResponse).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String trimToNull(String value) {
|
||||||
|
if (!StringUtils.hasText(value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return value.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package com.bruce.agent.factory;
|
||||||
|
|
||||||
|
import com.bruce.agent.dto.request.AgentSessionMessageCreateDTO;
|
||||||
|
import com.bruce.agent.entity.AgentMessage;
|
||||||
|
import com.bruce.agent.vo.AgentMessageVO;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Agent 消息工厂,统一处理消息入参与返回转换。
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class AgentMessageFactory {
|
||||||
|
|
||||||
|
public AgentMessage toEntity(AgentSessionMessageCreateDTO request) {
|
||||||
|
if (request == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
AgentMessage entity = new AgentMessage();
|
||||||
|
entity.setSessionId(request.getSessionId());
|
||||||
|
entity.setRole(trimToNull(request.getRole()));
|
||||||
|
entity.setContent(trimToNull(request.getContent()));
|
||||||
|
entity.setCitationJson(trimToNull(request.getCitationJson()));
|
||||||
|
entity.setTokenCount(request.getTokenCount());
|
||||||
|
entity.setRemark(trimToNull(request.getRemark()));
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AgentMessageVO toVO(AgentMessage entity) {
|
||||||
|
if (entity == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
AgentMessageVO vo = new AgentMessageVO();
|
||||||
|
BeanUtils.copyProperties(entity, vo);
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<AgentMessageVO> toVOList(List<AgentMessage> entities) {
|
||||||
|
return entities == null ? List.of() : entities.stream().map(this::toVO).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String trimToNull(String value) {
|
||||||
|
if (!StringUtils.hasText(value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return value.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package com.bruce.agent.factory;
|
||||||
|
|
||||||
|
import com.bruce.agent.dto.request.AgentSessionCreateDTO;
|
||||||
|
import com.bruce.agent.entity.AgentSession;
|
||||||
|
import com.bruce.agent.vo.AgentSessionDetailVO;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Agent 会话工厂,统一处理会话转换逻辑。
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class AgentSessionFactory {
|
||||||
|
|
||||||
|
public AgentSession toEntity(AgentSessionCreateDTO request) {
|
||||||
|
if (request == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
AgentSession entity = new AgentSession();
|
||||||
|
entity.setAgentId(request.getAgentId());
|
||||||
|
entity.setSessionCode(trimToNull(request.getSessionCode()));
|
||||||
|
entity.setWorkflowRunId(request.getWorkflowRunId());
|
||||||
|
entity.setTitle(trimToNull(request.getTitle()));
|
||||||
|
entity.setMetadataJson(trimToNull(request.getMetadataJson()));
|
||||||
|
entity.setRemark(trimToNull(request.getRemark()));
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AgentSessionDetailVO toDetailVO(AgentSession entity) {
|
||||||
|
if (entity == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
AgentSessionDetailVO detailVO = new AgentSessionDetailVO();
|
||||||
|
BeanUtils.copyProperties(entity, detailVO);
|
||||||
|
return detailVO;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<AgentSessionDetailVO> toDetailVOList(List<AgentSession> entities) {
|
||||||
|
return entities == null ? List.of() : entities.stream().map(this::toDetailVO).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String trimToNull(String value) {
|
||||||
|
if (!StringUtils.hasText(value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return value.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.bruce.agent.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.bruce.agent.entity.AgentCapabilityBinding;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface AgentCapabilityBindingMapper extends BaseMapper<AgentCapabilityBinding> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.bruce.agent.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.bruce.agent.entity.AgentMessage;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface AgentMessageMapper extends BaseMapper<AgentMessage> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.bruce.agent.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.bruce.agent.entity.AgentSession;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface AgentSessionMapper extends BaseMapper<AgentSession> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.bruce.agent.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.bruce.agent.dto.request.AgentSessionMessageCreateDTO;
|
||||||
|
import com.bruce.agent.entity.AgentMessage;
|
||||||
|
import com.bruce.agent.vo.AgentMessageVO;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface IAgentMessageService extends IService<AgentMessage> {
|
||||||
|
|
||||||
|
boolean appendMessage(AgentSessionMessageCreateDTO request);
|
||||||
|
|
||||||
|
AgentMessage appendMessageEntity(AgentSessionMessageCreateDTO request);
|
||||||
|
|
||||||
|
List<AgentMessageVO> listBySessionId(Long sessionId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.bruce.agent.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.bruce.agent.dto.request.AgentSessionCreateDTO;
|
||||||
|
import com.bruce.agent.entity.AgentSession;
|
||||||
|
import com.bruce.agent.vo.AgentSessionDetailVO;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface IAgentSessionService extends IService<AgentSession> {
|
||||||
|
|
||||||
|
boolean createSession(AgentSessionCreateDTO request);
|
||||||
|
|
||||||
|
AgentSession createSessionEntity(AgentSessionCreateDTO request);
|
||||||
|
|
||||||
|
List<AgentSessionDetailVO> listByAgentId(Long agentId);
|
||||||
|
|
||||||
|
AgentSessionDetailVO getDetailById(Long sessionId);
|
||||||
|
|
||||||
|
boolean closeSession(Long sessionId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.bruce.agent.service;
|
||||||
|
|
||||||
|
import com.bruce.agent.vo.AgentWorkspaceVO;
|
||||||
|
|
||||||
|
public interface IAgentWorkspaceService {
|
||||||
|
|
||||||
|
AgentWorkspaceVO getWorkspace(Long agentId, Long sessionId);
|
||||||
|
}
|
||||||
@@ -4,13 +4,21 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|||||||
import com.bruce.agent.dto.request.AgentChatRequest;
|
import com.bruce.agent.dto.request.AgentChatRequest;
|
||||||
import com.bruce.agent.dto.request.AgentDefinitionQueryRequest;
|
import com.bruce.agent.dto.request.AgentDefinitionQueryRequest;
|
||||||
import com.bruce.agent.dto.request.AgentDefinitionSaveRequest;
|
import com.bruce.agent.dto.request.AgentDefinitionSaveRequest;
|
||||||
|
import com.bruce.agent.dto.request.AgentSessionCreateDTO;
|
||||||
|
import com.bruce.agent.dto.request.AgentSessionMessageCreateDTO;
|
||||||
import com.bruce.agent.dto.response.AgentChatResponse;
|
import com.bruce.agent.dto.response.AgentChatResponse;
|
||||||
import com.bruce.agent.dto.response.AgentDefinitionResponse;
|
import com.bruce.agent.dto.response.AgentDefinitionResponse;
|
||||||
import com.bruce.agent.entity.AgentDefinition;
|
import com.bruce.agent.entity.AgentDefinition;
|
||||||
|
import com.bruce.agent.entity.AgentMessage;
|
||||||
|
import com.bruce.agent.entity.AgentSession;
|
||||||
|
import com.bruce.agent.factory.AgentDefinitionFactory;
|
||||||
import com.bruce.agent.mapper.AgentDefinitionMapper;
|
import com.bruce.agent.mapper.AgentDefinitionMapper;
|
||||||
import com.bruce.agent.service.IAgentDefinitionService;
|
import com.bruce.agent.service.IAgentDefinitionService;
|
||||||
|
import com.bruce.agent.service.IAgentMessageService;
|
||||||
|
import com.bruce.agent.service.IAgentSessionService;
|
||||||
import com.bruce.common.enums.EnableStatusEnum;
|
import com.bruce.common.enums.EnableStatusEnum;
|
||||||
import com.bruce.modelprovider.client.OpenAiChatMessage;
|
import com.bruce.modelprovider.client.OpenAiChatMessage;
|
||||||
|
import com.bruce.modelprovider.entity.ModelCallLog;
|
||||||
import com.bruce.modelprovider.entity.RagStoreModelConfig;
|
import com.bruce.modelprovider.entity.RagStoreModelConfig;
|
||||||
import com.bruce.modelprovider.gateway.ChatModelGateway;
|
import com.bruce.modelprovider.gateway.ChatModelGateway;
|
||||||
import com.bruce.modelprovider.gateway.ChatRequest;
|
import com.bruce.modelprovider.gateway.ChatRequest;
|
||||||
@@ -30,6 +38,7 @@ import org.springframework.util.StringUtils;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@@ -44,35 +53,32 @@ public class AgentDefinitionServiceImpl extends ServiceImpl<AgentDefinitionMappe
|
|||||||
private final RagChunkEmbeddingMapper ragChunkEmbeddingMapper;
|
private final RagChunkEmbeddingMapper ragChunkEmbeddingMapper;
|
||||||
private final EmbeddingModelGateway embeddingModelGateway;
|
private final EmbeddingModelGateway embeddingModelGateway;
|
||||||
private final ChatModelGateway chatModelGateway;
|
private final ChatModelGateway chatModelGateway;
|
||||||
|
private final IAgentSessionService agentSessionService;
|
||||||
|
private final IAgentMessageService agentMessageService;
|
||||||
|
private final AgentDefinitionFactory agentDefinitionFactory;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<AgentDefinitionResponse> listResponses() {
|
public List<AgentDefinitionResponse> listResponses() {
|
||||||
return lambdaQuery()
|
return agentDefinitionFactory.toResponses(lambdaQuery()
|
||||||
.orderByAsc(AgentDefinition::getAgentCode)
|
.orderByAsc(AgentDefinition::getAgentCode)
|
||||||
.list()
|
.list());
|
||||||
.stream()
|
|
||||||
.map(AgentDefinitionResponse::fromEntity)
|
|
||||||
.toList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<AgentDefinitionResponse> query(AgentDefinitionQueryRequest request) {
|
public List<AgentDefinitionResponse> query(AgentDefinitionQueryRequest request) {
|
||||||
AgentDefinitionQueryRequest queryRequest = request == null ? new AgentDefinitionQueryRequest() : request;
|
AgentDefinitionQueryRequest queryRequest = request == null ? new AgentDefinitionQueryRequest() : request;
|
||||||
return lambdaQuery()
|
return agentDefinitionFactory.toResponses(lambdaQuery()
|
||||||
.eq(StringUtils.hasText(queryRequest.getAgentCode()), AgentDefinition::getAgentCode, trimToNull(queryRequest.getAgentCode()))
|
.eq(StringUtils.hasText(queryRequest.getAgentCode()), AgentDefinition::getAgentCode, trimToNull(queryRequest.getAgentCode()))
|
||||||
.like(StringUtils.hasText(queryRequest.getAgentName()), AgentDefinition::getAgentName, trimToNull(queryRequest.getAgentName()))
|
.like(StringUtils.hasText(queryRequest.getAgentName()), AgentDefinition::getAgentName, trimToNull(queryRequest.getAgentName()))
|
||||||
.eq(StringUtils.hasText(queryRequest.getStatus()), AgentDefinition::getStatus, trimToNull(queryRequest.getStatus()))
|
.eq(StringUtils.hasText(queryRequest.getStatus()), AgentDefinition::getStatus, trimToNull(queryRequest.getStatus()))
|
||||||
.eq(queryRequest.getStoreId() != null, AgentDefinition::getStoreId, queryRequest.getStoreId())
|
.eq(queryRequest.getStoreId() != null, AgentDefinition::getStoreId, queryRequest.getStoreId())
|
||||||
.orderByAsc(AgentDefinition::getAgentCode)
|
.orderByAsc(AgentDefinition::getAgentCode)
|
||||||
.list()
|
.list());
|
||||||
.stream()
|
|
||||||
.map(AgentDefinitionResponse::fromEntity)
|
|
||||||
.toList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AgentDefinitionResponse getResponseById(Long id) {
|
public AgentDefinitionResponse getResponseById(Long id) {
|
||||||
return AgentDefinitionResponse.fromEntity(getById(id));
|
return agentDefinitionFactory.toResponse(getById(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -81,30 +87,38 @@ public class AgentDefinitionServiceImpl extends ServiceImpl<AgentDefinitionMappe
|
|||||||
if (ragStoreService.getById(request.getStoreId()) == null) {
|
if (ragStoreService.getById(request.getStoreId()) == null) {
|
||||||
throw new IllegalArgumentException("绑定知识库不存在,ID: " + request.getStoreId());
|
throw new IllegalArgumentException("绑定知识库不存在,ID: " + request.getStoreId());
|
||||||
}
|
}
|
||||||
|
String agentCode = request.getAgentCode().trim();
|
||||||
AgentDefinition duplicate = lambdaQuery()
|
AgentDefinition duplicate = lambdaQuery()
|
||||||
.eq(AgentDefinition::getAgentCode, request.getAgentCode().trim())
|
.eq(AgentDefinition::getAgentCode, agentCode)
|
||||||
.ne(request.getId() != null, AgentDefinition::getId, request.getId())
|
.ne(request.getId() != null, AgentDefinition::getId, request.getId())
|
||||||
.one();
|
.one();
|
||||||
if (duplicate != null) {
|
if (duplicate != null) {
|
||||||
throw new IllegalArgumentException("Agent编码已存在: " + request.getAgentCode().trim());
|
throw new IllegalArgumentException("Agent编码已存在: " + agentCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AgentDefinition requestEntity = agentDefinitionFactory.toEntity(request);
|
||||||
AgentDefinition entity = request.getId() == null ? new AgentDefinition() : getById(request.getId());
|
AgentDefinition entity = request.getId() == null ? new AgentDefinition() : getById(request.getId());
|
||||||
if (entity == null) {
|
if (entity == null) {
|
||||||
throw new IllegalArgumentException("Agent不存在,ID: " + request.getId());
|
throw new IllegalArgumentException("Agent不存在,ID: " + request.getId());
|
||||||
}
|
}
|
||||||
entity.setAgentCode(request.getAgentCode().trim());
|
entity.setAgentCode(requestEntity.getAgentCode());
|
||||||
entity.setAgentName(request.getAgentName().trim());
|
entity.setAgentName(requestEntity.getAgentName());
|
||||||
entity.setSystemPrompt(trimToNull(request.getSystemPrompt()));
|
entity.setSystemPrompt(requestEntity.getSystemPrompt());
|
||||||
entity.setStoreId(request.getStoreId());
|
entity.setStoreId(requestEntity.getStoreId());
|
||||||
entity.setStatus(StringUtils.hasText(request.getStatus())
|
entity.setStatus(StringUtils.hasText(requestEntity.getStatus())
|
||||||
? request.getStatus().trim()
|
? requestEntity.getStatus()
|
||||||
: EnableStatusEnum.ENABLED.name());
|
: EnableStatusEnum.ENABLED.name());
|
||||||
entity.setRemark(trimToNull(request.getRemark()));
|
entity.setRemark(requestEntity.getRemark());
|
||||||
return request.getId() == null ? save(entity) : updateById(entity);
|
|
||||||
|
boolean result = request.getId() == null ? save(entity) : updateById(entity);
|
||||||
|
log.info("Agent定义保存完成,agentId={}, agentCode={}, storeId={}, result={}",
|
||||||
|
entity.getId(), entity.getAgentCode(), entity.getStoreId(), result);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AgentChatResponse chat(Long agentId, AgentChatRequest request) {
|
public AgentChatResponse chat(Long agentId, AgentChatRequest request) {
|
||||||
|
log.info("Agent兼容聊天开始,agentId={}", agentId);
|
||||||
if (agentId == null) {
|
if (agentId == null) {
|
||||||
throw new IllegalArgumentException("Agent ID不能为空");
|
throw new IllegalArgumentException("Agent ID不能为空");
|
||||||
}
|
}
|
||||||
@@ -148,16 +162,15 @@ public class AgentDefinitionServiceImpl extends ServiceImpl<AgentDefinitionMappe
|
|||||||
}
|
}
|
||||||
|
|
||||||
String queryVector = toVectorLiteral(queryEmbedding.getVectors().getFirst());
|
String queryVector = toVectorLiteral(queryEmbedding.getVectors().getFirst());
|
||||||
recalls = ragChunkEmbeddingMapper.queryTopKByStore(
|
recalls = ragChunkEmbeddingMapper.queryTopKByStore(agent.getStoreId(), queryVector, DEFAULT_TOP_K);
|
||||||
agent.getStoreId(),
|
|
||||||
queryVector,
|
|
||||||
DEFAULT_TOP_K
|
|
||||||
);
|
|
||||||
if (recalls.isEmpty()) {
|
if (recalls.isEmpty()) {
|
||||||
throw new IllegalArgumentException("未召回到可用知识切片,请先完成知识库切片与向量化");
|
throw new IllegalArgumentException("未召回到可用知识切片,请先完成知识库切片与向量化");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AgentSession session = createCompatibilitySession(agentId, request);
|
||||||
|
appendRequestMessages(session.getId(), request.getMessages());
|
||||||
|
|
||||||
ChatRequest chatRequest = new ChatRequest();
|
ChatRequest chatRequest = new ChatRequest();
|
||||||
chatRequest.setTaskType(ragEnabled ? "RAG_ANSWER" : "CHAT_SIMPLE");
|
chatRequest.setTaskType(ragEnabled ? "RAG_ANSWER" : "CHAT_SIMPLE");
|
||||||
chatRequest.setMatchScope("AGENT");
|
chatRequest.setMatchScope("AGENT");
|
||||||
@@ -167,15 +180,21 @@ public class AgentDefinitionServiceImpl extends ServiceImpl<AgentDefinitionMappe
|
|||||||
chatRequest.setMessages(buildChatMessages(agent, recalls, request.getMessages(), ragEnabled));
|
chatRequest.setMessages(buildChatMessages(agent, recalls, request.getMessages(), ragEnabled));
|
||||||
|
|
||||||
ChatResult chatResult = chatModelGateway.chat(chatRequest);
|
ChatResult chatResult = chatModelGateway.chat(chatRequest);
|
||||||
|
appendAssistantMessage(session.getId(), recalls, chatResult);
|
||||||
|
|
||||||
AgentChatResponse response = new AgentChatResponse();
|
AgentChatResponse response = new AgentChatResponse();
|
||||||
response.setAgentId(agent.getId());
|
response.setAgentId(agent.getId());
|
||||||
response.setAgentCode(agent.getAgentCode());
|
response.setAgentCode(agent.getAgentCode());
|
||||||
response.setAgentName(agent.getAgentName());
|
response.setAgentName(agent.getAgentName());
|
||||||
response.setStoreId(agent.getStoreId());
|
response.setStoreId(agent.getStoreId());
|
||||||
response.setStoreName(store.getStoreName());
|
response.setStoreName(store.getStoreName());
|
||||||
|
response.setSessionId(session.getId());
|
||||||
|
response.setSessionCode(session.getSessionCode());
|
||||||
response.setAnswer(chatResult.getContent());
|
response.setAnswer(chatResult.getContent());
|
||||||
response.setModelRequestId(chatResult.getCallLog().getRequestId());
|
response.setModelRequestId(resolveRequestId(chatResult));
|
||||||
response.setReferences(toReferenceChunks(recalls));
|
response.setReferences(toReferenceChunks(recalls));
|
||||||
|
log.info("Agent兼容聊天结束,agentId={}, sessionId={}, requestId={}, referenceCount={}",
|
||||||
|
agentId, session.getId(), resolveRequestId(chatResult), recalls.size());
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,6 +291,105 @@ public class AgentDefinitionServiceImpl extends ServiceImpl<AgentDefinitionMappe
|
|||||||
}).toList();
|
}).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 兼容旧调试接口时,统一创建独立会话并沉淀会话编码,便于工作台后续追踪完整对话。
|
||||||
|
*/
|
||||||
|
private AgentSession createCompatibilitySession(Long agentId, AgentChatRequest request) {
|
||||||
|
if (agentSessionService == null) {
|
||||||
|
AgentSession session = new AgentSession();
|
||||||
|
session.setId(0L);
|
||||||
|
session.setAgentId(agentId);
|
||||||
|
session.setSessionCode("chat_mock");
|
||||||
|
session.setTitle(resolveLatestUserMessage(request.getMessages()));
|
||||||
|
session.setStatus("ACTIVE");
|
||||||
|
session.setMetadataJson("{\"source\":\"compat_chat\"}");
|
||||||
|
session.setRemark("兼容聊天接口自动创建");
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
AgentSessionCreateDTO createDTO = new AgentSessionCreateDTO();
|
||||||
|
createDTO.setAgentId(agentId);
|
||||||
|
createDTO.setSessionCode("chat_" + UUID.randomUUID().toString().replace("-", ""));
|
||||||
|
createDTO.setTitle(resolveLatestUserMessage(request.getMessages()));
|
||||||
|
createDTO.setMetadataJson("{\"source\":\"compat_chat\"}");
|
||||||
|
createDTO.setRemark("兼容聊天接口自动创建");
|
||||||
|
AgentSession session = agentSessionService.createSessionEntity(createDTO);
|
||||||
|
agentSessionService.save(session);
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendRequestMessages(Long sessionId, List<AgentChatRequest.AgentMessage> messages) {
|
||||||
|
if (agentMessageService == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (AgentChatRequest.AgentMessage rawMessage : messages) {
|
||||||
|
if (rawMessage == null || !StringUtils.hasText(rawMessage.getContent())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
AgentSessionMessageCreateDTO createDTO = new AgentSessionMessageCreateDTO();
|
||||||
|
createDTO.setSessionId(sessionId);
|
||||||
|
createDTO.setRole(normalizeRole(rawMessage.getRole()));
|
||||||
|
createDTO.setContent(rawMessage.getContent().trim());
|
||||||
|
createDTO.setCitationJson("[]");
|
||||||
|
createDTO.setRemark("兼容聊天接口消息");
|
||||||
|
AgentMessage entity = agentMessageService.appendMessageEntity(createDTO);
|
||||||
|
agentMessageService.save(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private AgentMessage appendAssistantMessage(Long sessionId,
|
||||||
|
List<RagChunkRecallResponse> recalls,
|
||||||
|
ChatResult chatResult) {
|
||||||
|
if (agentMessageService == null) {
|
||||||
|
AgentMessage message = new AgentMessage();
|
||||||
|
message.setSessionId(sessionId);
|
||||||
|
message.setRole("assistant");
|
||||||
|
message.setContent(chatResult.getContent());
|
||||||
|
message.setCitationJson(toCitationJson(recalls));
|
||||||
|
message.setTokenCount(chatResult.getCallLog() == null ? null : chatResult.getCallLog().getTotalTokens());
|
||||||
|
message.setRemark("兼容聊天接口模型回复");
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
AgentSessionMessageCreateDTO createDTO = new AgentSessionMessageCreateDTO();
|
||||||
|
createDTO.setSessionId(sessionId);
|
||||||
|
createDTO.setRole("assistant");
|
||||||
|
createDTO.setContent(chatResult.getContent());
|
||||||
|
createDTO.setCitationJson(toCitationJson(recalls));
|
||||||
|
ModelCallLog callLog = chatResult.getCallLog();
|
||||||
|
createDTO.setRequestId(callLog == null ? null : callLog.getRequestId());
|
||||||
|
createDTO.setTokenCount(callLog == null ? null : callLog.getTotalTokens());
|
||||||
|
createDTO.setRemark("兼容聊天接口模型回复");
|
||||||
|
AgentMessage entity = agentMessageService.appendMessageEntity(createDTO);
|
||||||
|
agentMessageService.save(entity);
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveRequestId(ChatResult chatResult) {
|
||||||
|
ModelCallLog callLog = chatResult == null ? null : chatResult.getCallLog();
|
||||||
|
return callLog == null ? null : callLog.getRequestId();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String toCitationJson(List<RagChunkRecallResponse> recalls) {
|
||||||
|
if (recalls == null || recalls.isEmpty()) {
|
||||||
|
return "[]";
|
||||||
|
}
|
||||||
|
StringBuilder builder = new StringBuilder("[");
|
||||||
|
for (int i = 0; i < recalls.size(); i++) {
|
||||||
|
RagChunkRecallResponse recall = recalls.get(i);
|
||||||
|
if (i > 0) {
|
||||||
|
builder.append(',');
|
||||||
|
}
|
||||||
|
builder.append("{\"chunkId\":")
|
||||||
|
.append(recall.getChunkId())
|
||||||
|
.append(",\"documentId\":")
|
||||||
|
.append(recall.getDocumentId())
|
||||||
|
.append(",\"score\":")
|
||||||
|
.append(recall.getScore() == null ? 0D : recall.getScore())
|
||||||
|
.append('}');
|
||||||
|
}
|
||||||
|
builder.append(']');
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
private String normalizeRole(String role) {
|
private String normalizeRole(String role) {
|
||||||
if (!StringUtils.hasText(role)) {
|
if (!StringUtils.hasText(role)) {
|
||||||
return "user";
|
return "user";
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package com.bruce.agent.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.bruce.agent.dto.request.AgentSessionMessageCreateDTO;
|
||||||
|
import com.bruce.agent.entity.AgentMessage;
|
||||||
|
import com.bruce.agent.entity.AgentSession;
|
||||||
|
import com.bruce.agent.factory.AgentMessageFactory;
|
||||||
|
import com.bruce.agent.mapper.AgentMessageMapper;
|
||||||
|
import com.bruce.agent.service.IAgentMessageService;
|
||||||
|
import com.bruce.agent.service.IAgentSessionService;
|
||||||
|
import com.bruce.agent.vo.AgentMessageVO;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AgentMessageServiceImpl extends ServiceImpl<AgentMessageMapper, AgentMessage> implements IAgentMessageService {
|
||||||
|
|
||||||
|
private final IAgentSessionService agentSessionService;
|
||||||
|
private final AgentMessageFactory agentMessageFactory;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean appendMessage(AgentSessionMessageCreateDTO request) {
|
||||||
|
AgentMessage entity = appendMessageEntity(request);
|
||||||
|
boolean saved = save(entity);
|
||||||
|
log.info("Agent消息写入完成,sessionId={}, role={}, messageId={}, requestId={}, result={}",
|
||||||
|
entity.getSessionId(), entity.getRole(), entity.getId(), request.getRequestId(), saved);
|
||||||
|
return saved;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AgentMessage appendMessageEntity(AgentSessionMessageCreateDTO request) {
|
||||||
|
validateRequest(request);
|
||||||
|
AgentSession session = loadSession(request.getSessionId());
|
||||||
|
if ("CLOSED".equals(session.getStatus())) {
|
||||||
|
throw new IllegalArgumentException("会话已关闭,不能继续写入消息");
|
||||||
|
}
|
||||||
|
AgentMessage entity = loadFactory().toEntity(request);
|
||||||
|
if (!StringUtils.hasText(entity.getCitationJson())) {
|
||||||
|
entity.setCitationJson("[]");
|
||||||
|
} else if (!entity.getCitationJson().trim().startsWith("[")) {
|
||||||
|
throw new IllegalArgumentException("citationJson必须是数组结构");
|
||||||
|
}
|
||||||
|
log.info("Agent消息写入开始,sessionId={}, role={}, requestId={}",
|
||||||
|
entity.getSessionId(), entity.getRole(), request.getRequestId());
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AgentMessageVO> listBySessionId(Long sessionId) {
|
||||||
|
if (sessionId == null) {
|
||||||
|
throw new IllegalArgumentException("会话ID不能为空");
|
||||||
|
}
|
||||||
|
List<AgentMessage> messages = lambdaQuery()
|
||||||
|
.eq(AgentMessage::getSessionId, sessionId)
|
||||||
|
.orderByAsc(AgentMessage::getCreateTime)
|
||||||
|
.orderByAsc(AgentMessage::getId)
|
||||||
|
.list();
|
||||||
|
return loadFactory().toVOList(messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AgentSession loadSession(Long sessionId) {
|
||||||
|
AgentSession session = agentSessionService.getById(sessionId);
|
||||||
|
if (session == null) {
|
||||||
|
throw new IllegalArgumentException("会话不存在,ID: " + sessionId);
|
||||||
|
}
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateRequest(AgentSessionMessageCreateDTO request) {
|
||||||
|
if (request == null) {
|
||||||
|
throw new IllegalArgumentException("消息请求不能为空");
|
||||||
|
}
|
||||||
|
if (request.getSessionId() == null) {
|
||||||
|
throw new IllegalArgumentException("会话ID不能为空");
|
||||||
|
}
|
||||||
|
if (!StringUtils.hasText(request.getRole())) {
|
||||||
|
throw new IllegalArgumentException("消息角色不能为空");
|
||||||
|
}
|
||||||
|
if (!StringUtils.hasText(request.getContent())) {
|
||||||
|
throw new IllegalArgumentException("消息内容不能为空");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private AgentMessageFactory loadFactory() {
|
||||||
|
return agentMessageFactory == null ? new AgentMessageFactory() : agentMessageFactory;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
package com.bruce.agent.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.bruce.agent.dto.request.AgentSessionCreateDTO;
|
||||||
|
import com.bruce.agent.entity.AgentDefinition;
|
||||||
|
import com.bruce.agent.entity.AgentSession;
|
||||||
|
import com.bruce.agent.factory.AgentSessionFactory;
|
||||||
|
import com.bruce.agent.mapper.AgentSessionMapper;
|
||||||
|
import com.bruce.agent.service.IAgentDefinitionService;
|
||||||
|
import com.bruce.agent.service.IAgentSessionService;
|
||||||
|
import com.bruce.agent.vo.AgentSessionDetailVO;
|
||||||
|
import com.bruce.common.enums.EnableStatusEnum;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AgentSessionServiceImpl extends ServiceImpl<AgentSessionMapper, AgentSession> implements IAgentSessionService {
|
||||||
|
|
||||||
|
private static final String SESSION_STATUS_ACTIVE = "ACTIVE";
|
||||||
|
private static final String SESSION_STATUS_CLOSED = "CLOSED";
|
||||||
|
|
||||||
|
private final IAgentDefinitionService agentDefinitionService;
|
||||||
|
private final AgentSessionFactory agentSessionFactory;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean createSession(AgentSessionCreateDTO request) {
|
||||||
|
AgentSession entity = createSessionEntity(request);
|
||||||
|
boolean saved = save(entity);
|
||||||
|
log.info("Agent会话创建完成,agentId={}, sessionCode={}, sessionId={}, result={}",
|
||||||
|
entity.getAgentId(), entity.getSessionCode(), entity.getId(), saved);
|
||||||
|
return saved;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AgentSession createSessionEntity(AgentSessionCreateDTO request) {
|
||||||
|
validateCreateRequest(request);
|
||||||
|
AgentDefinition agent = agentDefinitionService.getById(request.getAgentId());
|
||||||
|
if (agent == null) {
|
||||||
|
throw new IllegalArgumentException("Agent不存在,ID: " + request.getAgentId());
|
||||||
|
}
|
||||||
|
if (!EnableStatusEnum.ENABLED.name().equals(agent.getStatus())) {
|
||||||
|
throw new IllegalArgumentException("Agent已停用,无法创建会话");
|
||||||
|
}
|
||||||
|
if (baseMapper != null) {
|
||||||
|
AgentSession duplicate = lambdaQuery()
|
||||||
|
.eq(AgentSession::getSessionCode, request.getSessionCode().trim())
|
||||||
|
.one();
|
||||||
|
if (duplicate != null) {
|
||||||
|
throw new IllegalArgumentException("会话编码已存在: " + request.getSessionCode().trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AgentSession entity = loadFactory().toEntity(request);
|
||||||
|
entity.setStatus(SESSION_STATUS_ACTIVE);
|
||||||
|
if (!StringUtils.hasText(entity.getMetadataJson())) {
|
||||||
|
entity.setMetadataJson("{}");
|
||||||
|
}
|
||||||
|
log.info("Agent会话创建开始,agentId={}, sessionCode={}, workflowRunId={}",
|
||||||
|
entity.getAgentId(), entity.getSessionCode(), entity.getWorkflowRunId());
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AgentSessionDetailVO> listByAgentId(Long agentId) {
|
||||||
|
if (agentId == null) {
|
||||||
|
throw new IllegalArgumentException("Agent ID不能为空");
|
||||||
|
}
|
||||||
|
List<AgentSession> sessions = lambdaQuery()
|
||||||
|
.eq(AgentSession::getAgentId, agentId)
|
||||||
|
.orderByDesc(AgentSession::getUpdateTime)
|
||||||
|
.orderByDesc(AgentSession::getId)
|
||||||
|
.list();
|
||||||
|
return loadFactory().toDetailVOList(sessions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AgentSessionDetailVO getDetailById(Long sessionId) {
|
||||||
|
if (sessionId == null) {
|
||||||
|
throw new IllegalArgumentException("会话ID不能为空");
|
||||||
|
}
|
||||||
|
return loadFactory().toDetailVO(getById(sessionId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean closeSession(Long sessionId) {
|
||||||
|
if (sessionId == null) {
|
||||||
|
throw new IllegalArgumentException("会话ID不能为空");
|
||||||
|
}
|
||||||
|
AgentSession session = getById(sessionId);
|
||||||
|
if (session == null) {
|
||||||
|
throw new IllegalArgumentException("会话不存在,ID: " + sessionId);
|
||||||
|
}
|
||||||
|
session.setStatus(SESSION_STATUS_CLOSED);
|
||||||
|
boolean updated = updateById(session);
|
||||||
|
log.info("Agent会话关闭完成,sessionId={}, sessionCode={}, result={}",
|
||||||
|
session.getId(), session.getSessionCode(), updated);
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateCreateRequest(AgentSessionCreateDTO request) {
|
||||||
|
if (request == null) {
|
||||||
|
throw new IllegalArgumentException("创建会话请求不能为空");
|
||||||
|
}
|
||||||
|
if (request.getAgentId() == null) {
|
||||||
|
throw new IllegalArgumentException("Agent ID不能为空");
|
||||||
|
}
|
||||||
|
if (!StringUtils.hasText(request.getSessionCode())) {
|
||||||
|
throw new IllegalArgumentException("会话编码不能为空");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private AgentSessionFactory loadFactory() {
|
||||||
|
return agentSessionFactory == null ? new AgentSessionFactory() : agentSessionFactory;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package com.bruce.agent.service.impl;
|
||||||
|
|
||||||
|
import com.bruce.agent.entity.AgentDefinition;
|
||||||
|
import com.bruce.agent.service.IAgentDefinitionService;
|
||||||
|
import com.bruce.agent.service.IAgentMessageService;
|
||||||
|
import com.bruce.agent.service.IAgentSessionService;
|
||||||
|
import com.bruce.agent.service.IAgentWorkspaceService;
|
||||||
|
import com.bruce.agent.vo.AgentMessageVO;
|
||||||
|
import com.bruce.agent.vo.AgentSessionDetailVO;
|
||||||
|
import com.bruce.agent.vo.AgentWorkspaceVO;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AgentWorkspaceServiceImpl implements IAgentWorkspaceService {
|
||||||
|
|
||||||
|
private final IAgentDefinitionService agentDefinitionService;
|
||||||
|
private final IAgentSessionService agentSessionService;
|
||||||
|
private final IAgentMessageService agentMessageService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AgentWorkspaceVO getWorkspace(Long agentId, Long sessionId) {
|
||||||
|
log.info("Agent工作台查询开始,agentId={}, sessionId={}", agentId, sessionId);
|
||||||
|
if (agentId == null) {
|
||||||
|
throw new IllegalArgumentException("Agent ID不能为空");
|
||||||
|
}
|
||||||
|
AgentDefinition agent = agentDefinitionService.getById(agentId);
|
||||||
|
if (agent == null) {
|
||||||
|
throw new IllegalArgumentException("Agent不存在,ID: " + agentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<AgentSessionDetailVO> sessions = agentSessionService.listByAgentId(agentId);
|
||||||
|
AgentSessionDetailVO currentSession = resolveSession(sessionId, sessions);
|
||||||
|
List<AgentMessageVO> messages = currentSession == null ? List.of() : agentMessageService.listBySessionId(currentSession.getId());
|
||||||
|
|
||||||
|
AgentWorkspaceVO workspace = new AgentWorkspaceVO();
|
||||||
|
workspace.setAgentId(agent.getId());
|
||||||
|
workspace.setAgentCode(agent.getAgentCode());
|
||||||
|
workspace.setAgentName(agent.getAgentName());
|
||||||
|
workspace.setStoreId(agent.getStoreId());
|
||||||
|
workspace.setStatus(agent.getStatus());
|
||||||
|
workspace.setSessions(sessions);
|
||||||
|
workspace.setMessages(messages);
|
||||||
|
|
||||||
|
if (currentSession != null) {
|
||||||
|
workspace.setSessionId(currentSession.getId());
|
||||||
|
workspace.setSessionCode(currentSession.getSessionCode());
|
||||||
|
workspace.setSessionTitle(currentSession.getTitle());
|
||||||
|
workspace.setSessionStatus(currentSession.getStatus());
|
||||||
|
workspace.setWorkflowRunId(currentSession.getWorkflowRunId());
|
||||||
|
}
|
||||||
|
|
||||||
|
int totalTokens = messages.stream()
|
||||||
|
.map(AgentMessageVO::getTokenCount)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.mapToInt(Integer::intValue)
|
||||||
|
.sum();
|
||||||
|
int citationCount = (int) messages.stream()
|
||||||
|
.filter(message -> StringUtils.hasText(message.getCitationJson()) && message.getCitationJson().contains("chunkId"))
|
||||||
|
.count();
|
||||||
|
String latestRequestId = messages.stream()
|
||||||
|
.map(AgentMessageVO::getRequestId)
|
||||||
|
.filter(StringUtils::hasText)
|
||||||
|
.reduce((first, second) -> second)
|
||||||
|
.orElse(null);
|
||||||
|
|
||||||
|
workspace.setTotalTokens(totalTokens);
|
||||||
|
workspace.setCitationCount(citationCount);
|
||||||
|
workspace.setLatestRequestId(latestRequestId);
|
||||||
|
log.info("Agent工作台查询结束,agentId={}, sessionId={}, messageCount={}, totalTokens={}",
|
||||||
|
agentId, workspace.getSessionId(), messages.size(), totalTokens);
|
||||||
|
return workspace;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AgentSessionDetailVO resolveSession(Long sessionId, List<AgentSessionDetailVO> sessions) {
|
||||||
|
if (sessions == null || sessions.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (sessionId == null) {
|
||||||
|
return sessions.get(0);
|
||||||
|
}
|
||||||
|
return sessions.stream().filter(item -> sessionId.equals(item.getId())).findFirst().orElse(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.bruce.agent.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class AgentMessageVO {
|
||||||
|
private Long id;
|
||||||
|
private Long sessionId;
|
||||||
|
private String role;
|
||||||
|
private String content;
|
||||||
|
private String citationJson;
|
||||||
|
private Integer tokenCount;
|
||||||
|
private String requestId;
|
||||||
|
private String remark;
|
||||||
|
private Date createTime;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.bruce.agent.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class AgentSessionDetailVO {
|
||||||
|
private Long id;
|
||||||
|
private Long agentId;
|
||||||
|
private String sessionCode;
|
||||||
|
private Long workflowRunId;
|
||||||
|
private String title;
|
||||||
|
private String status;
|
||||||
|
private String metadataJson;
|
||||||
|
private String remark;
|
||||||
|
private Date createTime;
|
||||||
|
private Date updateTime;
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.bruce.agent.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class AgentWorkspaceVO {
|
||||||
|
private Long agentId;
|
||||||
|
private String agentCode;
|
||||||
|
private String agentName;
|
||||||
|
private Long storeId;
|
||||||
|
private String status;
|
||||||
|
private Long sessionId;
|
||||||
|
private String sessionCode;
|
||||||
|
private String sessionTitle;
|
||||||
|
private String sessionStatus;
|
||||||
|
private Long workflowRunId;
|
||||||
|
private Integer totalTokens;
|
||||||
|
private Integer citationCount;
|
||||||
|
private String latestRequestId;
|
||||||
|
private List<AgentSessionDetailVO> sessions;
|
||||||
|
private List<AgentMessageVO> messages;
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package com.bruce.agent.factory;
|
||||||
|
|
||||||
|
import com.bruce.agent.dto.request.AgentDefinitionSaveRequest;
|
||||||
|
import com.bruce.agent.dto.request.AgentSessionCreateDTO;
|
||||||
|
import com.bruce.agent.dto.request.AgentSessionMessageCreateDTO;
|
||||||
|
import com.bruce.agent.dto.response.AgentDefinitionResponse;
|
||||||
|
import com.bruce.agent.entity.AgentDefinition;
|
||||||
|
import com.bruce.agent.entity.AgentMessage;
|
||||||
|
import com.bruce.agent.entity.AgentSession;
|
||||||
|
import com.bruce.agent.vo.AgentSessionDetailVO;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
class AgentFactoryTests {
|
||||||
|
|
||||||
|
private final AgentDefinitionFactory agentDefinitionFactory = new AgentDefinitionFactory();
|
||||||
|
private final AgentSessionFactory agentSessionFactory = new AgentSessionFactory();
|
||||||
|
private final AgentMessageFactory agentMessageFactory = new AgentMessageFactory();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void agentDefinitionFactoryShouldTrimRequestAndBuildResponse() {
|
||||||
|
AgentDefinitionSaveRequest request = new AgentDefinitionSaveRequest();
|
||||||
|
request.setId(1L);
|
||||||
|
request.setAgentCode(" AGENT_RAG_HELPER ");
|
||||||
|
request.setAgentName(" 知识问答助手 ");
|
||||||
|
request.setSystemPrompt(" 你是企业知识助手 ");
|
||||||
|
request.setStoreId(1001L);
|
||||||
|
request.setStatus(" ENABLED ");
|
||||||
|
request.setRemark(" 默认Agent ");
|
||||||
|
|
||||||
|
AgentDefinition entity = agentDefinitionFactory.toEntity(request);
|
||||||
|
assertEquals("AGENT_RAG_HELPER", entity.getAgentCode());
|
||||||
|
assertEquals("知识问答助手", entity.getAgentName());
|
||||||
|
assertEquals("你是企业知识助手", entity.getSystemPrompt());
|
||||||
|
assertEquals("ENABLED", entity.getStatus());
|
||||||
|
assertEquals("默认Agent", entity.getRemark());
|
||||||
|
|
||||||
|
AgentDefinitionResponse response = agentDefinitionFactory.toResponse(entity);
|
||||||
|
assertEquals(1001L, response.getStoreId());
|
||||||
|
assertEquals("AGENT_RAG_HELPER", response.getAgentCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void agentSessionFactoryShouldBuildSessionEntityAndDetailView() {
|
||||||
|
AgentSessionCreateDTO request = new AgentSessionCreateDTO();
|
||||||
|
request.setAgentId(1L);
|
||||||
|
request.setSessionCode(" session_001 ");
|
||||||
|
request.setWorkflowRunId(2001L);
|
||||||
|
request.setTitle(" 产品问答会话 ");
|
||||||
|
request.setMetadataJson(" {\"source\":\"debug\"} ");
|
||||||
|
request.setRemark(" 首轮调试 ");
|
||||||
|
|
||||||
|
AgentSession entity = agentSessionFactory.toEntity(request);
|
||||||
|
assertNotNull(entity);
|
||||||
|
assertEquals("session_001", entity.getSessionCode());
|
||||||
|
assertEquals("产品问答会话", entity.getTitle());
|
||||||
|
assertEquals("{\"source\":\"debug\"}", entity.getMetadataJson());
|
||||||
|
assertEquals("首轮调试", entity.getRemark());
|
||||||
|
|
||||||
|
AgentSessionDetailVO detailVO = agentSessionFactory.toDetailVO(entity);
|
||||||
|
assertEquals(1L, detailVO.getAgentId());
|
||||||
|
assertEquals("session_001", detailVO.getSessionCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void agentMessageFactoryShouldBuildMessageEntityAndPreserveCitationJson() {
|
||||||
|
AgentSessionMessageCreateDTO request = new AgentSessionMessageCreateDTO();
|
||||||
|
request.setSessionId(10L);
|
||||||
|
request.setRole(" assistant ");
|
||||||
|
request.setContent(" 这里是回答内容 ");
|
||||||
|
request.setCitationJson(" [{\"chunkId\":1}] ");
|
||||||
|
request.setTokenCount(256);
|
||||||
|
request.setRemark(" 回答成功 ");
|
||||||
|
|
||||||
|
AgentMessage entity = agentMessageFactory.toEntity(request);
|
||||||
|
assertEquals(10L, entity.getSessionId());
|
||||||
|
assertEquals("assistant", entity.getRole());
|
||||||
|
assertEquals("这里是回答内容", entity.getContent());
|
||||||
|
assertEquals("[{\"chunkId\":1}]", entity.getCitationJson());
|
||||||
|
assertEquals(256, entity.getTokenCount());
|
||||||
|
assertTrue(entity.getRemark().contains("回答成功"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
package com.bruce.agent.session;
|
||||||
|
|
||||||
|
import com.bruce.agent.dto.request.AgentSessionCreateDTO;
|
||||||
|
import com.bruce.agent.dto.request.AgentSessionMessageCreateDTO;
|
||||||
|
import com.bruce.agent.entity.AgentDefinition;
|
||||||
|
import com.bruce.agent.entity.AgentMessage;
|
||||||
|
import com.bruce.agent.entity.AgentSession;
|
||||||
|
import com.bruce.agent.service.IAgentDefinitionService;
|
||||||
|
import com.bruce.agent.service.impl.AgentMessageServiceImpl;
|
||||||
|
import com.bruce.agent.service.impl.AgentSessionServiceImpl;
|
||||||
|
import com.bruce.common.enums.EnableStatusEnum;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Spy;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.doAnswer;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class AgentSessionServiceTests {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private IAgentDefinitionService agentDefinitionService;
|
||||||
|
|
||||||
|
@Spy
|
||||||
|
@InjectMocks
|
||||||
|
private AgentSessionServiceImpl agentSessionService;
|
||||||
|
|
||||||
|
@Spy
|
||||||
|
@InjectMocks
|
||||||
|
private AgentMessageServiceImpl agentMessageService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createSessionShouldRejectDisabledAgent() {
|
||||||
|
AgentDefinition agent = new AgentDefinition();
|
||||||
|
agent.setId(1L);
|
||||||
|
agent.setStatus("DISABLED");
|
||||||
|
when(agentDefinitionService.getById(1L)).thenReturn(agent);
|
||||||
|
|
||||||
|
AgentSessionCreateDTO request = new AgentSessionCreateDTO();
|
||||||
|
request.setAgentId(1L);
|
||||||
|
request.setSessionCode("session_001");
|
||||||
|
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> agentSessionService.createSession(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createSessionShouldPersistActiveSession() {
|
||||||
|
AgentDefinition agent = new AgentDefinition();
|
||||||
|
agent.setId(1L);
|
||||||
|
agent.setStatus(EnableStatusEnum.ENABLED.name());
|
||||||
|
when(agentDefinitionService.getById(1L)).thenReturn(agent);
|
||||||
|
doAnswer(invocation -> true).when(agentSessionService).save(any(AgentSession.class));
|
||||||
|
|
||||||
|
AgentSessionCreateDTO request = new AgentSessionCreateDTO();
|
||||||
|
request.setAgentId(1L);
|
||||||
|
request.setSessionCode("session_001");
|
||||||
|
request.setTitle("产品问答");
|
||||||
|
request.setMetadataJson("{\"source\":\"debug\"}");
|
||||||
|
|
||||||
|
boolean result = agentSessionService.createSession(request);
|
||||||
|
assertTrue(result);
|
||||||
|
|
||||||
|
ArgumentCaptor<AgentSession> captor = ArgumentCaptor.forClass(AgentSession.class);
|
||||||
|
verify(agentSessionService).save(captor.capture());
|
||||||
|
assertEquals("session_001", captor.getValue().getSessionCode());
|
||||||
|
assertEquals("ACTIVE", captor.getValue().getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void appendMessageShouldRejectClosedSession() {
|
||||||
|
AgentSession session = new AgentSession();
|
||||||
|
session.setId(10L);
|
||||||
|
session.setStatus("CLOSED");
|
||||||
|
doReturn(session).when(agentMessageService).loadSession(10L);
|
||||||
|
|
||||||
|
AgentSessionMessageCreateDTO request = new AgentSessionMessageCreateDTO();
|
||||||
|
request.setSessionId(10L);
|
||||||
|
request.setRole("user");
|
||||||
|
request.setContent("你好");
|
||||||
|
request.setCitationJson("[]");
|
||||||
|
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> agentMessageService.appendMessage(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void appendMessageShouldPersistCitationJson() {
|
||||||
|
AgentSession session = new AgentSession();
|
||||||
|
session.setId(10L);
|
||||||
|
session.setStatus("ACTIVE");
|
||||||
|
doReturn(session).when(agentMessageService).loadSession(10L);
|
||||||
|
doAnswer(invocation -> true).when(agentMessageService).save(any(AgentMessage.class));
|
||||||
|
|
||||||
|
AgentSessionMessageCreateDTO request = new AgentSessionMessageCreateDTO();
|
||||||
|
request.setSessionId(10L);
|
||||||
|
request.setRole("assistant");
|
||||||
|
request.setContent("这里是回答");
|
||||||
|
request.setCitationJson("[{\"chunkId\":1}]");
|
||||||
|
request.setTokenCount(128);
|
||||||
|
|
||||||
|
boolean result = agentMessageService.appendMessage(request);
|
||||||
|
assertTrue(result);
|
||||||
|
|
||||||
|
ArgumentCaptor<AgentMessage> captor = ArgumentCaptor.forClass(AgentMessage.class);
|
||||||
|
verify(agentMessageService).save(captor.capture());
|
||||||
|
assertEquals("[{\"chunkId\":1}]", captor.getValue().getCitationJson());
|
||||||
|
assertEquals(128, captor.getValue().getTokenCount());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package com.bruce.agent.workspace;
|
||||||
|
|
||||||
|
import com.bruce.agent.entity.AgentDefinition;
|
||||||
|
import com.bruce.agent.service.IAgentDefinitionService;
|
||||||
|
import com.bruce.agent.service.IAgentMessageService;
|
||||||
|
import com.bruce.agent.service.IAgentSessionService;
|
||||||
|
import com.bruce.agent.service.impl.AgentWorkspaceServiceImpl;
|
||||||
|
import com.bruce.agent.vo.AgentMessageVO;
|
||||||
|
import com.bruce.agent.vo.AgentSessionDetailVO;
|
||||||
|
import com.bruce.agent.vo.AgentWorkspaceVO;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class AgentWorkspaceServiceTests {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private IAgentDefinitionService agentDefinitionService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private IAgentSessionService agentSessionService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private IAgentMessageService agentMessageService;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private AgentWorkspaceServiceImpl agentWorkspaceService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getWorkspaceShouldAggregateAgentSessionAndMessages() {
|
||||||
|
AgentDefinition agent = new AgentDefinition();
|
||||||
|
agent.setId(1L);
|
||||||
|
agent.setAgentCode("AGENT_RAG_HELPER");
|
||||||
|
agent.setAgentName("知识问答助手");
|
||||||
|
agent.setStoreId(1001L);
|
||||||
|
agent.setStatus("ENABLED");
|
||||||
|
|
||||||
|
AgentSessionDetailVO session = new AgentSessionDetailVO();
|
||||||
|
session.setId(10L);
|
||||||
|
session.setAgentId(1L);
|
||||||
|
session.setSessionCode("session_001");
|
||||||
|
session.setStatus("ACTIVE");
|
||||||
|
session.setTitle("产品问答");
|
||||||
|
session.setWorkflowRunId(2001L);
|
||||||
|
|
||||||
|
AgentMessageVO userMessage = new AgentMessageVO();
|
||||||
|
userMessage.setRole("user");
|
||||||
|
userMessage.setContent("产品支持哪些模型?");
|
||||||
|
AgentMessageVO assistantMessage = new AgentMessageVO();
|
||||||
|
assistantMessage.setRole("assistant");
|
||||||
|
assistantMessage.setContent("当前支持 OpenAI Compatible 协议模型。");
|
||||||
|
assistantMessage.setCitationJson("[{\"chunkId\":1}]");
|
||||||
|
assistantMessage.setTokenCount(256);
|
||||||
|
assistantMessage.setRequestId("req-001");
|
||||||
|
|
||||||
|
when(agentDefinitionService.getById(1L)).thenReturn(agent);
|
||||||
|
when(agentSessionService.listByAgentId(1L)).thenReturn(List.of(session));
|
||||||
|
when(agentMessageService.listBySessionId(10L)).thenReturn(List.of(userMessage, assistantMessage));
|
||||||
|
|
||||||
|
AgentWorkspaceVO workspace = agentWorkspaceService.getWorkspace(1L, 10L);
|
||||||
|
|
||||||
|
assertNotNull(workspace);
|
||||||
|
assertEquals("AGENT_RAG_HELPER", workspace.getAgentCode());
|
||||||
|
assertEquals("知识问答助手", workspace.getAgentName());
|
||||||
|
assertEquals(10L, workspace.getSessionId());
|
||||||
|
assertEquals("session_001", workspace.getSessionCode());
|
||||||
|
assertEquals(2, workspace.getMessages().size());
|
||||||
|
assertEquals(1, workspace.getCitationCount());
|
||||||
|
assertEquals(256, workspace.getTotalTokens());
|
||||||
|
assertEquals("req-001", workspace.getLatestRequestId());
|
||||||
|
}
|
||||||
|
}
|
||||||
85
common-agent-boot/pom.xml
Normal file
85
common-agent-boot/pom.xml
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
<?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>com.bruce</groupId>
|
||||||
|
<artifactId>common-agent-parent</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>common-agent-boot</artifactId>
|
||||||
|
<name>common-agent-boot</name>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.bruce</groupId>
|
||||||
|
<artifactId>common-agent-common</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.bruce</groupId>
|
||||||
|
<artifactId>common-agent-rag</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.bruce</groupId>
|
||||||
|
<artifactId>common-agent-modelprovider</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.bruce</groupId>
|
||||||
|
<artifactId>common-agent-agent</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.bruce</groupId>
|
||||||
|
<artifactId>common-agent-workflow</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.bruce</groupId>
|
||||||
|
<artifactId>common-agent-mcp</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.bruce</groupId>
|
||||||
|
<artifactId>common-agent-skill</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.bruce</groupId>
|
||||||
|
<artifactId>common-agent-observability</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</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>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
@@ -1,11 +1,22 @@
|
|||||||
package com.bruce;
|
package com.bruce;
|
||||||
|
|
||||||
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.scheduling.annotation.EnableAsync;
|
import org.springframework.scheduling.annotation.EnableAsync;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableAsync
|
@EnableAsync
|
||||||
|
@MapperScan(basePackages = {
|
||||||
|
"com.bruce.common.mapper",
|
||||||
|
"com.bruce.rag.mapper",
|
||||||
|
"com.bruce.modelprovider.mapper",
|
||||||
|
"com.bruce.agent.mapper",
|
||||||
|
"com.bruce.workflow.mapper",
|
||||||
|
"com.bruce.mcp.mapper",
|
||||||
|
"com.bruce.skill.mapper",
|
||||||
|
"com.bruce.observability.mapper"
|
||||||
|
})
|
||||||
public class CommonAgentApplication {
|
public class CommonAgentApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
15
common-agent-boot/src/main/resources/application-dev.yaml
Normal file
15
common-agent-boot/src/main/resources/application-dev.yaml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
spring:
|
||||||
|
datasource:
|
||||||
|
driver-class-name: org.postgresql.Driver
|
||||||
|
url: jdbc:postgresql://110.42.106.130:5431/common_agent?currentSchema=common_agent
|
||||||
|
username: common_agent
|
||||||
|
password: common_agent
|
||||||
|
|
||||||
|
mybatis-plus:
|
||||||
|
mapper-locations: classpath*:/mapper/**/*.xml
|
||||||
|
configuration:
|
||||||
|
map-underscore-to-camel-case: true
|
||||||
|
|
||||||
|
common:
|
||||||
|
attachment:
|
||||||
|
base-path: /data/common-agent/attachments
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.bruce.common.config;
|
package com.bruce.integration.config;
|
||||||
|
|
||||||
|
import com.bruce.common.config.EntityAuditMetaObjectHandler;
|
||||||
import com.bruce.rag.entity.RagStore;
|
import com.bruce.rag.entity.RagStore;
|
||||||
import org.apache.ibatis.reflection.SystemMetaObject;
|
import org.apache.ibatis.reflection.SystemMetaObject;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.bruce.common.enumconfig;
|
package com.bruce.integration.enumconfig;
|
||||||
|
|
||||||
import com.bruce.common.enums.CommonStatusEnum;
|
import com.bruce.common.enums.CommonStatusEnum;
|
||||||
import com.bruce.common.enums.EnableStatusEnum;
|
import com.bruce.common.enums.EnableStatusEnum;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.bruce.common.enumconfig;
|
package com.bruce.integration.enumconfig;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.bruce.common.domain.entity.SysEnum;
|
import com.bruce.common.domain.entity.SysEnum;
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
package com.bruce.integration.enumconfig;
|
||||||
|
|
||||||
|
import com.bruce.common.domain.entity.SysEnum;
|
||||||
|
import com.bruce.common.enums.PersistableSysEnumDefinition;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sys_enum 初始化测试的辅助工具。
|
||||||
|
* <p>
|
||||||
|
* 该类只服务于测试入口,用于把代码里的枚举定义组装成可落库的数据结构,
|
||||||
|
* 并在真正写库前完成组级唯一性校验。
|
||||||
|
*/
|
||||||
|
final class SysEnumDefinitionSyncSupport {
|
||||||
|
|
||||||
|
private SysEnumDefinitionSyncSupport() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static EnumGroup groupOf(List<? extends PersistableSysEnumDefinition> definitions) {
|
||||||
|
if (definitions == null || definitions.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("枚举定义不能为空");
|
||||||
|
}
|
||||||
|
PersistableSysEnumDefinition first = definitions.getFirst();
|
||||||
|
validateGroupMembers(first, definitions);
|
||||||
|
validateUniqueValuesAndSorts(first, definitions);
|
||||||
|
return new EnumGroup(first.getCatalog(), first.getType(), List.copyOf(definitions));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void validateUniqueGroupKeys(List<EnumGroup> groups) {
|
||||||
|
Set<String> keys = new HashSet<>();
|
||||||
|
for (EnumGroup group : groups) {
|
||||||
|
String key = group.catalog() + "/" + group.type();
|
||||||
|
if (!keys.add(key)) {
|
||||||
|
throw new IllegalArgumentException("存在重复的枚举分组: " + key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<SysEnum> toEntities(EnumGroup group) {
|
||||||
|
return group.definitions().stream()
|
||||||
|
.map(item -> {
|
||||||
|
SysEnum sysEnum = new SysEnum();
|
||||||
|
sysEnum.setCatalog(group.catalog());
|
||||||
|
sysEnum.setType(group.type());
|
||||||
|
sysEnum.setName(item.getName());
|
||||||
|
sysEnum.setValue(item.getValue());
|
||||||
|
sysEnum.setStrvalue(item.getStrvalue());
|
||||||
|
sysEnum.setSort(item.getSort());
|
||||||
|
sysEnum.setRemark(item.getRemark());
|
||||||
|
return sysEnum;
|
||||||
|
})
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void validateGroupMembers(
|
||||||
|
PersistableSysEnumDefinition first,
|
||||||
|
List<? extends PersistableSysEnumDefinition> definitions
|
||||||
|
) {
|
||||||
|
for (PersistableSysEnumDefinition item : definitions) {
|
||||||
|
if (!first.getCatalog().equals(item.getCatalog()) || !first.getType().equals(item.getType())) {
|
||||||
|
throw new IllegalArgumentException("同一枚举组中的 catalog/type 必须一致");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void validateUniqueValuesAndSorts(
|
||||||
|
PersistableSysEnumDefinition first,
|
||||||
|
List<? extends PersistableSysEnumDefinition> definitions
|
||||||
|
) {
|
||||||
|
Set<Integer> values = new HashSet<>();
|
||||||
|
Set<Integer> sorts = new HashSet<>();
|
||||||
|
for (PersistableSysEnumDefinition item : definitions) {
|
||||||
|
if (!values.add(item.getValue())) {
|
||||||
|
throw new IllegalArgumentException("枚举值重复: " + first.getCatalog() + "/" + first.getType() + "/" + item.getValue());
|
||||||
|
}
|
||||||
|
if (!sorts.add(item.getSort())) {
|
||||||
|
throw new IllegalArgumentException("枚举排序重复: " + first.getCatalog() + "/" + first.getType() + "/" + item.getSort());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
record EnumGroup(
|
||||||
|
String catalog,
|
||||||
|
String type,
|
||||||
|
List<? extends PersistableSysEnumDefinition> definitions
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.bruce.common.enumconfig;
|
package com.bruce.integration.enumconfig;
|
||||||
|
|
||||||
import com.bruce.common.domain.entity.SysEnum;
|
import com.bruce.common.domain.entity.SysEnum;
|
||||||
import com.bruce.common.enums.EnableStatusEnum;
|
import com.bruce.common.enums.EnableStatusEnum;
|
||||||
61
common-agent-common/pom.xml
Normal file
61
common-agent-common/pom.xml
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<?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>com.bruce</groupId>
|
||||||
|
<artifactId>common-agent-parent</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>common-agent-common</artifactId>
|
||||||
|
<name>common-agent-common</name>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-spring-boot4-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.postgresql</groupId>
|
||||||
|
<artifactId>postgresql</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-annotations</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.tika</groupId>
|
||||||
|
<artifactId>tika-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.tika</groupId>
|
||||||
|
<artifactId>tika-parsers-standard-package</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springdoc</groupId>
|
||||||
|
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
@@ -3,26 +3,39 @@ package com.bruce.common.controller;
|
|||||||
import com.bruce.common.domain.model.RequestResult;
|
import com.bruce.common.domain.model.RequestResult;
|
||||||
import com.bruce.common.dto.request.SysAttachmentUploadRequest;
|
import com.bruce.common.dto.request.SysAttachmentUploadRequest;
|
||||||
import com.bruce.common.dto.response.SysAttachmentResponse;
|
import com.bruce.common.dto.response.SysAttachmentResponse;
|
||||||
|
import com.bruce.common.factory.SysAttachmentFactory;
|
||||||
import com.bruce.common.service.ISysAttachmentService;
|
import com.bruce.common.service.ISysAttachmentService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
@Tag(name = "系统附件管理")
|
@Tag(name = "系统附件管理")
|
||||||
|
@Slf4j
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/attachments")
|
@RequestMapping("/api/attachments")
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class SysAttachmentController {
|
public class SysAttachmentController {
|
||||||
|
|
||||||
@Autowired
|
private final ISysAttachmentService sysAttachmentService;
|
||||||
private ISysAttachmentService sysAttachmentService;
|
|
||||||
|
private final SysAttachmentFactory sysAttachmentFactory;
|
||||||
|
|
||||||
@Operation(summary = "上传附件")
|
@Operation(summary = "上传附件")
|
||||||
@PostMapping("/upload")
|
@PostMapping("/upload")
|
||||||
public RequestResult<SysAttachmentResponse> upload(@ModelAttribute SysAttachmentUploadRequest request) {
|
public RequestResult<SysAttachmentResponse> upload(@ModelAttribute SysAttachmentUploadRequest request) {
|
||||||
return RequestResult.success(SysAttachmentResponse.fromEntity(sysAttachmentService.upload(request)));
|
log.info("上传附件开始,sourceType={}, sourceId={}",
|
||||||
|
request == null ? null : request.getSourceType(),
|
||||||
|
request == null ? null : request.getSourceId());
|
||||||
|
SysAttachmentResponse response = sysAttachmentFactory.toResponse(sysAttachmentService.upload(request));
|
||||||
|
log.info("上传附件结束,attachmentId={}, sourceType={}, sourceId={}",
|
||||||
|
response == null ? null : response.getId(),
|
||||||
|
request == null ? null : request.getSourceType(),
|
||||||
|
request == null ? null : request.getSourceId());
|
||||||
|
return RequestResult.success(response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
package com.bruce.common.dto.response;
|
package com.bruce.common.dto.response;
|
||||||
|
|
||||||
import com.bruce.common.domain.entity.SysAttachment;
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.springframework.beans.BeanUtils;
|
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@Schema(description = "系统附件响应")
|
@Schema(description = "系统附件响应")
|
||||||
@@ -41,13 +39,4 @@ public class SysAttachmentResponse {
|
|||||||
|
|
||||||
@Schema(description = "备注")
|
@Schema(description = "备注")
|
||||||
private String remark;
|
private String remark;
|
||||||
|
|
||||||
public static SysAttachmentResponse fromEntity(SysAttachment entity) {
|
|
||||||
if (entity == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
SysAttachmentResponse response = new SysAttachmentResponse();
|
|
||||||
BeanUtils.copyProperties(entity, response);
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
package com.bruce.common.dto.response;
|
package com.bruce.common.dto.response;
|
||||||
|
|
||||||
import com.bruce.common.domain.entity.SysEnum;
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.springframework.beans.BeanUtils;
|
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@Schema(description = "系统枚举响应")
|
@Schema(description = "系统枚举响应")
|
||||||
@@ -32,13 +30,4 @@ public class SysEnumResponse {
|
|||||||
|
|
||||||
@Schema(description = "备注")
|
@Schema(description = "备注")
|
||||||
private String remark;
|
private String remark;
|
||||||
|
|
||||||
public static SysEnumResponse fromEntity(SysEnum entity) {
|
|
||||||
if (entity == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
SysEnumResponse response = new SysEnumResponse();
|
|
||||||
BeanUtils.copyProperties(entity, response);
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.bruce.common.factory;
|
||||||
|
|
||||||
|
import com.bruce.common.domain.entity.SysAttachment;
|
||||||
|
import com.bruce.common.dto.response.SysAttachmentResponse;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统附件工厂,集中处理附件元数据出参转换。
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class SysAttachmentFactory {
|
||||||
|
|
||||||
|
public SysAttachmentResponse toResponse(SysAttachment entity) {
|
||||||
|
if (entity == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
SysAttachmentResponse response = new SysAttachmentResponse();
|
||||||
|
BeanUtils.copyProperties(entity, response);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package com.bruce.common.factory;
|
||||||
|
|
||||||
|
import com.bruce.common.domain.entity.SysEnum;
|
||||||
|
import com.bruce.common.dto.request.SysEnumBatchSaveRequest;
|
||||||
|
import com.bruce.common.dto.request.SysEnumSaveRequest;
|
||||||
|
import com.bruce.common.dto.response.SysEnumResponse;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统枚举工厂,统一负责请求对象、实体对象和响应对象之间的转换。
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class SysEnumFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将保存请求转换为实体,避免在服务层散落字段拷贝逻辑。
|
||||||
|
*/
|
||||||
|
public SysEnum toEntity(SysEnumSaveRequest request) {
|
||||||
|
if (request == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
SysEnum entity = new SysEnum();
|
||||||
|
BeanUtils.copyProperties(request, entity);
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将批量请求中的单个枚举项转换为实体,catalog/type 由外层分组统一提供。
|
||||||
|
*/
|
||||||
|
public SysEnum toEntity(String catalog, String type, SysEnumBatchSaveRequest.Item item) {
|
||||||
|
if (item == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
SysEnum entity = new SysEnum();
|
||||||
|
entity.setCatalog(catalog);
|
||||||
|
entity.setType(type);
|
||||||
|
entity.setName(item.getName());
|
||||||
|
entity.setValue(item.getValue());
|
||||||
|
entity.setStrvalue(item.getStrvalue());
|
||||||
|
entity.setSort(item.getSort());
|
||||||
|
entity.setRemark(item.getRemark());
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将实体转换为返回对象,保持接口层不直接暴露实体。
|
||||||
|
*/
|
||||||
|
public SysEnumResponse toResponse(SysEnum entity) {
|
||||||
|
if (entity == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
SysEnumResponse response = new SysEnumResponse();
|
||||||
|
BeanUtils.copyProperties(entity, response);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SysEnumResponse> toResponses(List<SysEnum> entities) {
|
||||||
|
return entities == null ? List.of() : entities.stream().map(this::toResponse).toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,6 +28,7 @@ public class SysAttachmentServiceImpl extends ServiceImpl<SysAttachmentMapper, S
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SysAttachment upload(SysAttachmentUploadRequest request) {
|
public SysAttachment upload(SysAttachmentUploadRequest request) {
|
||||||
|
// 这里先完成上传校验和本地落盘,后续如接入对象存储也只需替换该流程内部实现。
|
||||||
if (request == null) {
|
if (request == null) {
|
||||||
throw new IllegalArgumentException("上传请求不能为空");
|
throw new IllegalArgumentException("上传请求不能为空");
|
||||||
}
|
}
|
||||||
@@ -7,8 +7,10 @@ import com.bruce.common.dto.request.SysEnumManageQueryRequest;
|
|||||||
import com.bruce.common.dto.request.SysEnumQueryRequest;
|
import com.bruce.common.dto.request.SysEnumQueryRequest;
|
||||||
import com.bruce.common.dto.request.SysEnumSaveRequest;
|
import com.bruce.common.dto.request.SysEnumSaveRequest;
|
||||||
import com.bruce.common.dto.response.SysEnumResponse;
|
import com.bruce.common.dto.response.SysEnumResponse;
|
||||||
|
import com.bruce.common.factory.SysEnumFactory;
|
||||||
import com.bruce.common.mapper.SysEnumMapper;
|
import com.bruce.common.mapper.SysEnumMapper;
|
||||||
import com.bruce.common.service.ISysEnumService;
|
import com.bruce.common.service.ISysEnumService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -20,8 +22,11 @@ import java.util.Set;
|
|||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class SysEnumServiceImpl extends ServiceImpl<SysEnumMapper, SysEnum> implements ISysEnumService {
|
public class SysEnumServiceImpl extends ServiceImpl<SysEnumMapper, SysEnum> implements ISysEnumService {
|
||||||
|
|
||||||
|
private final SysEnumFactory sysEnumFactory;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SysEnum> listByCatalogAndType(SysEnumQueryRequest request) {
|
public List<SysEnum> listByCatalogAndType(SysEnumQueryRequest request) {
|
||||||
log.info("SysEnumServiceImpl.listByCatalogAndType start, request={}", request);
|
log.info("SysEnumServiceImpl.listByCatalogAndType start, request={}", request);
|
||||||
@@ -39,17 +44,19 @@ public class SysEnumServiceImpl extends ServiceImpl<SysEnumMapper, SysEnum> impl
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SysEnumResponse> listResponses() {
|
public List<SysEnumResponse> listResponses() {
|
||||||
log.info("SysEnumServiceImpl.listResponses start");
|
log.info("查询系统枚举列表开始");
|
||||||
List<SysEnumResponse> responses = toResponses(list());
|
List<SysEnumResponse> responses = sysEnumFactory.toResponses(list());
|
||||||
log.info("SysEnumServiceImpl.listResponses success, count={}", responses.size());
|
log.info("查询系统枚举列表结束,count={}", responses.size());
|
||||||
return responses;
|
return responses;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SysEnumResponse> listByCatalogAndTypeResponses(SysEnumQueryRequest request) {
|
public List<SysEnumResponse> listByCatalogAndTypeResponses(SysEnumQueryRequest request) {
|
||||||
log.info("SysEnumServiceImpl.listByCatalogAndTypeResponses start");
|
log.info("按模块和类型查询系统枚举开始,catalog={}, type={}",
|
||||||
List<SysEnumResponse> responses = toResponses(listByCatalogAndType(request));
|
request == null ? null : request.getCatalog(),
|
||||||
log.info("SysEnumServiceImpl.listByCatalogAndTypeResponses success, count={}", responses.size());
|
request == null ? null : request.getType());
|
||||||
|
List<SysEnumResponse> responses = sysEnumFactory.toResponses(listByCatalogAndType(request));
|
||||||
|
log.info("按模块和类型查询系统枚举结束,count={}", responses.size());
|
||||||
return responses;
|
return responses;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +65,7 @@ public class SysEnumServiceImpl extends ServiceImpl<SysEnumMapper, SysEnum> impl
|
|||||||
log.info("SysEnumServiceImpl.listForManagement start, request={}", request);
|
log.info("SysEnumServiceImpl.listForManagement start, request={}", request);
|
||||||
SysEnumManageQueryRequest queryRequest = request == null ? new SysEnumManageQueryRequest() : request;
|
SysEnumManageQueryRequest queryRequest = request == null ? new SysEnumManageQueryRequest() : request;
|
||||||
String keyword = queryRequest.getKeyword();
|
String keyword = queryRequest.getKeyword();
|
||||||
List<SysEnumResponse> responses = toResponses(lambdaQuery()
|
List<SysEnumResponse> responses = sysEnumFactory.toResponses(lambdaQuery()
|
||||||
.eq(StringUtils.hasText(queryRequest.getCatalog()), SysEnum::getCatalog, queryRequest.getCatalog())
|
.eq(StringUtils.hasText(queryRequest.getCatalog()), SysEnum::getCatalog, queryRequest.getCatalog())
|
||||||
.eq(StringUtils.hasText(queryRequest.getType()), SysEnum::getType, queryRequest.getType())
|
.eq(StringUtils.hasText(queryRequest.getType()), SysEnum::getType, queryRequest.getType())
|
||||||
.and(StringUtils.hasText(keyword), wrapper -> wrapper
|
.and(StringUtils.hasText(keyword), wrapper -> wrapper
|
||||||
@@ -82,37 +89,35 @@ public class SysEnumServiceImpl extends ServiceImpl<SysEnumMapper, SysEnum> impl
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SysEnumResponse getResponseById(Long id) {
|
public SysEnumResponse getResponseById(Long id) {
|
||||||
log.info("SysEnumServiceImpl.getResponseById start, id={}", id);
|
log.info("查询系统枚举详情开始,id={}", id);
|
||||||
SysEnumResponse response = SysEnumResponse.fromEntity(getById(id));
|
SysEnumResponse response = sysEnumFactory.toResponse(getById(id));
|
||||||
log.info("SysEnumServiceImpl.getResponseById success, id={}, found={}", id, response != null);
|
log.info("查询系统枚举详情结束,id={}, found={}", id, response != null);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean saveOrUpdate(SysEnumSaveRequest request) {
|
public boolean saveOrUpdate(SysEnumSaveRequest request) {
|
||||||
log.info("SysEnumServiceImpl.saveOrUpdate start, request={}", request);
|
log.info("保存系统枚举开始,id={}, catalog={}, type={}, value={}",
|
||||||
|
request == null ? null : request.getId(),
|
||||||
|
request == null ? null : request.getCatalog(),
|
||||||
|
request == null ? null : request.getType(),
|
||||||
|
request == null ? null : request.getValue());
|
||||||
if (request == null) {
|
if (request == null) {
|
||||||
throw new IllegalArgumentException("保存请求不能为空");
|
throw new IllegalArgumentException("保存请求不能为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
SysEnum sysEnum = new SysEnum();
|
SysEnum sysEnum = sysEnumFactory.toEntity(request);
|
||||||
sysEnum.setId(request.getId());
|
|
||||||
sysEnum.setCatalog(request.getCatalog());
|
|
||||||
sysEnum.setType(request.getType());
|
|
||||||
sysEnum.setName(request.getName());
|
|
||||||
sysEnum.setValue(request.getValue());
|
|
||||||
sysEnum.setStrvalue(request.getStrvalue());
|
|
||||||
sysEnum.setSort(request.getSort());
|
|
||||||
sysEnum.setRemark(request.getRemark());
|
|
||||||
boolean result = super.saveOrUpdate(sysEnum);
|
boolean result = super.saveOrUpdate(sysEnum);
|
||||||
log.info("SysEnumServiceImpl.saveOrUpdate success, id={}, catalog={}, type={}, value={}, result={}",
|
log.info("保存系统枚举结束,id={}, catalog={}, type={}, value={}, result={}",
|
||||||
request.getId(), request.getCatalog(), request.getType(), request.getValue(), result);
|
request.getId(), request.getCatalog(), request.getType(), request.getValue(), result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean batchSave(SysEnumBatchSaveRequest request) {
|
public boolean batchSave(SysEnumBatchSaveRequest request) {
|
||||||
log.info("SysEnumServiceImpl.batchSave start, request={}", request);
|
log.info("批量保存系统枚举开始,catalog={}, type={}",
|
||||||
|
request == null ? null : request.getCatalog(),
|
||||||
|
request == null ? null : request.getType());
|
||||||
List<SysEnum> existingEnums = lambdaQuery()
|
List<SysEnum> existingEnums = lambdaQuery()
|
||||||
.eq(request != null && StringUtils.hasText(request.getCatalog()), SysEnum::getCatalog, request == null ? null : request.getCatalog())
|
.eq(request != null && StringUtils.hasText(request.getCatalog()), SysEnum::getCatalog, request == null ? null : request.getCatalog())
|
||||||
.eq(request != null && StringUtils.hasText(request.getType()), SysEnum::getType, request == null ? null : request.getType())
|
.eq(request != null && StringUtils.hasText(request.getType()), SysEnum::getType, request == null ? null : request.getType())
|
||||||
@@ -120,20 +125,10 @@ public class SysEnumServiceImpl extends ServiceImpl<SysEnumMapper, SysEnum> impl
|
|||||||
validateBatchSaveRequest(request, existingEnums);
|
validateBatchSaveRequest(request, existingEnums);
|
||||||
|
|
||||||
List<SysEnum> enums = request.getItems().stream()
|
List<SysEnum> enums = request.getItems().stream()
|
||||||
.map(item -> {
|
.map(item -> sysEnumFactory.toEntity(request.getCatalog(), request.getType(), item))
|
||||||
SysEnum sysEnum = new SysEnum();
|
|
||||||
sysEnum.setCatalog(request.getCatalog());
|
|
||||||
sysEnum.setType(request.getType());
|
|
||||||
sysEnum.setName(item.getName());
|
|
||||||
sysEnum.setValue(item.getValue());
|
|
||||||
sysEnum.setStrvalue(item.getStrvalue());
|
|
||||||
sysEnum.setSort(item.getSort());
|
|
||||||
sysEnum.setRemark(item.getRemark());
|
|
||||||
return sysEnum;
|
|
||||||
})
|
|
||||||
.toList();
|
.toList();
|
||||||
boolean result = saveBatch(enums);
|
boolean result = saveBatch(enums);
|
||||||
log.info("SysEnumServiceImpl.batchSave success, catalog={}, type={}, itemCount={}, result={}",
|
log.info("批量保存系统枚举结束,catalog={}, type={}, itemCount={}, result={}",
|
||||||
request.getCatalog(), request.getType(), enums.size(), result);
|
request.getCatalog(), request.getType(), enums.size(), result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -240,9 +235,4 @@ public class SysEnumServiceImpl extends ServiceImpl<SysEnumMapper, SysEnum> impl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<SysEnumResponse> toResponses(List<SysEnum> enums) {
|
|
||||||
return enums.stream()
|
|
||||||
.map(SysEnumResponse::fromEntity)
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.bruce.rag.typehandler;
|
package com.bruce.common.typehandler;
|
||||||
|
|
||||||
import org.apache.ibatis.type.BaseTypeHandler;
|
import org.apache.ibatis.type.BaseTypeHandler;
|
||||||
import org.apache.ibatis.type.JdbcType;
|
import org.apache.ibatis.type.JdbcType;
|
||||||
@@ -9,6 +9,7 @@ import com.bruce.common.domain.model.RequestResult;
|
|||||||
import com.bruce.common.dto.request.SysAttachmentUploadRequest;
|
import com.bruce.common.dto.request.SysAttachmentUploadRequest;
|
||||||
import com.bruce.common.domain.entity.SysAttachment;
|
import com.bruce.common.domain.entity.SysAttachment;
|
||||||
import com.bruce.common.dto.response.SysAttachmentResponse;
|
import com.bruce.common.dto.response.SysAttachmentResponse;
|
||||||
|
import com.bruce.common.factory.SysAttachmentFactory;
|
||||||
import com.bruce.common.mapper.SysAttachmentMapper;
|
import com.bruce.common.mapper.SysAttachmentMapper;
|
||||||
import com.bruce.common.service.ISysAttachmentService;
|
import com.bruce.common.service.ISysAttachmentService;
|
||||||
import com.bruce.common.service.impl.SysAttachmentServiceImpl;
|
import com.bruce.common.service.impl.SysAttachmentServiceImpl;
|
||||||
@@ -47,7 +48,7 @@ class SysAttachmentComponentStructureTests {
|
|||||||
assertEquals(SysAttachment.class, serviceMethod.getReturnType());
|
assertEquals(SysAttachment.class, serviceMethod.getReturnType());
|
||||||
assertEquals(RequestResult.class, controllerMethod.getReturnType());
|
assertEquals(RequestResult.class, controllerMethod.getReturnType());
|
||||||
assertTrue(controllerMethod.getGenericReturnType().getTypeName().contains("SysAttachmentResponse"));
|
assertTrue(controllerMethod.getGenericReturnType().getTypeName().contains("SysAttachmentResponse"));
|
||||||
assertEquals(SysAttachmentResponse.class, SysAttachmentResponse.class.getMethod("fromEntity", SysAttachment.class).getReturnType());
|
assertEquals(SysAttachmentResponse.class, SysAttachmentFactory.class.getMethod("toResponse", SysAttachment.class).getReturnType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -2,6 +2,7 @@ package com.bruce.common.enumconfig;
|
|||||||
|
|
||||||
import com.bruce.common.domain.entity.SysEnum;
|
import com.bruce.common.domain.entity.SysEnum;
|
||||||
import com.bruce.common.dto.request.SysEnumBatchSaveRequest;
|
import com.bruce.common.dto.request.SysEnumBatchSaveRequest;
|
||||||
|
import com.bruce.common.factory.SysEnumFactory;
|
||||||
import com.bruce.common.service.impl.SysEnumServiceImpl;
|
import com.bruce.common.service.impl.SysEnumServiceImpl;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
@@ -12,9 +13,11 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
|||||||
|
|
||||||
class SysEnumBatchSaveValidationTests {
|
class SysEnumBatchSaveValidationTests {
|
||||||
|
|
||||||
|
private final SysEnumFactory sysEnumFactory = new SysEnumFactory();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void batchSaveShouldRejectDuplicateValuesInsideRequest() {
|
void batchSaveShouldRejectDuplicateValuesInsideRequest() {
|
||||||
SysEnumServiceImpl service = new SysEnumServiceImpl();
|
SysEnumServiceImpl service = new SysEnumServiceImpl(sysEnumFactory);
|
||||||
SysEnumBatchSaveRequest request = new SysEnumBatchSaveRequest();
|
SysEnumBatchSaveRequest request = new SysEnumBatchSaveRequest();
|
||||||
request.setCatalog("common");
|
request.setCatalog("common");
|
||||||
request.setType("enable_status");
|
request.setType("enable_status");
|
||||||
@@ -28,7 +31,7 @@ class SysEnumBatchSaveValidationTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void batchSaveShouldRejectDuplicateValuesFromExistingEnums() {
|
void batchSaveShouldRejectDuplicateValuesFromExistingEnums() {
|
||||||
SysEnumServiceImpl service = new SysEnumServiceImpl();
|
SysEnumServiceImpl service = new SysEnumServiceImpl(sysEnumFactory);
|
||||||
SysEnum existing = new SysEnum();
|
SysEnum existing = new SysEnum();
|
||||||
existing.setCatalog("common");
|
existing.setCatalog("common");
|
||||||
existing.setType("enable_status");
|
existing.setType("enable_status");
|
||||||
@@ -47,7 +50,7 @@ class SysEnumBatchSaveValidationTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void batchSaveShouldAcceptUniqueValues() {
|
void batchSaveShouldAcceptUniqueValues() {
|
||||||
SysEnumServiceImpl service = new SysEnumServiceImpl();
|
SysEnumServiceImpl service = new SysEnumServiceImpl(sysEnumFactory);
|
||||||
SysEnumBatchSaveRequest request = new SysEnumBatchSaveRequest();
|
SysEnumBatchSaveRequest request = new SysEnumBatchSaveRequest();
|
||||||
request.setCatalog("common");
|
request.setCatalog("common");
|
||||||
request.setType("enable_status");
|
request.setType("enable_status");
|
||||||
@@ -11,6 +11,7 @@ import com.bruce.common.dto.request.SysEnumManageQueryRequest;
|
|||||||
import com.bruce.common.dto.request.SysEnumQueryRequest;
|
import com.bruce.common.dto.request.SysEnumQueryRequest;
|
||||||
import com.bruce.common.dto.request.SysEnumSaveRequest;
|
import com.bruce.common.dto.request.SysEnumSaveRequest;
|
||||||
import com.bruce.common.dto.response.SysEnumResponse;
|
import com.bruce.common.dto.response.SysEnumResponse;
|
||||||
|
import com.bruce.common.factory.SysEnumFactory;
|
||||||
import com.bruce.common.mapper.SysEnumMapper;
|
import com.bruce.common.mapper.SysEnumMapper;
|
||||||
import com.bruce.common.service.ISysEnumService;
|
import com.bruce.common.service.ISysEnumService;
|
||||||
import com.bruce.common.service.impl.SysEnumServiceImpl;
|
import com.bruce.common.service.impl.SysEnumServiceImpl;
|
||||||
@@ -89,7 +90,7 @@ class SysEnumComponentStructureTests {
|
|||||||
Method manageQueryMethod = SysEnumController.class.getMethod("queryForManagement", SysEnumManageQueryRequest.class);
|
Method manageQueryMethod = SysEnumController.class.getMethod("queryForManagement", SysEnumManageQueryRequest.class);
|
||||||
Method detailMethod = SysEnumController.class.getMethod("getById", Long.class);
|
Method detailMethod = SysEnumController.class.getMethod("getById", Long.class);
|
||||||
Method serviceListMethod = ISysEnumService.class.getMethod("listResponses");
|
Method serviceListMethod = ISysEnumService.class.getMethod("listResponses");
|
||||||
Method responseFactory = SysEnumResponse.class.getMethod("fromEntity", SysEnum.class);
|
Method responseFactory = SysEnumFactory.class.getMethod("toResponse", SysEnum.class);
|
||||||
|
|
||||||
assertTrue(serviceListMethod.getGenericReturnType().getTypeName().contains("SysEnumResponse"));
|
assertTrue(serviceListMethod.getGenericReturnType().getTypeName().contains("SysEnumResponse"));
|
||||||
assertTrue(listMethod.getGenericReturnType().getTypeName().contains("SysEnumResponse"));
|
assertTrue(listMethod.getGenericReturnType().getTypeName().contains("SysEnumResponse"));
|
||||||
@@ -99,10 +100,12 @@ class SysEnumComponentStructureTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void sysEnumShouldProvideManualInitTestEntry() throws NoSuchMethodException {
|
void sysEnumShouldProvideDefinitionSyncSupportEntry() throws NoSuchMethodException {
|
||||||
Method initMethod = SysEnumDataInitTests.class.getMethod("initDefaultEnums");
|
Method groupMethod = SysEnumDefinitionSyncSupport.class.getDeclaredMethod("groupOf", List.class);
|
||||||
|
Method uniqueKeyMethod = SysEnumDefinitionSyncSupport.class.getDeclaredMethod("validateUniqueGroupKeys", List.class);
|
||||||
|
|
||||||
assertNotNull(initMethod);
|
assertNotNull(groupMethod);
|
||||||
|
assertNotNull(uniqueKeyMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.bruce.common.factory;
|
||||||
|
|
||||||
|
import com.bruce.common.domain.entity.SysAttachment;
|
||||||
|
import com.bruce.common.dto.response.SysAttachmentResponse;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
|
||||||
|
class SysAttachmentFactoryTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldConvertEntityToResponse() {
|
||||||
|
SysAttachment entity = new SysAttachment();
|
||||||
|
entity.setId(3003L);
|
||||||
|
entity.setSourceType("RAG");
|
||||||
|
entity.setSourceId(1001L);
|
||||||
|
entity.setOriginalName("知识库说明.pdf");
|
||||||
|
entity.setFileName("uuid.pdf");
|
||||||
|
entity.setFileSuffix("pdf");
|
||||||
|
entity.setContentType("application/pdf");
|
||||||
|
entity.setFileSize(1024L);
|
||||||
|
entity.setStorageType("LOCAL");
|
||||||
|
entity.setFilePath("20260601/uuid.pdf");
|
||||||
|
entity.setFileUrl(null);
|
||||||
|
entity.setRemark("上传附件");
|
||||||
|
|
||||||
|
SysAttachmentFactory factory = new SysAttachmentFactory();
|
||||||
|
SysAttachmentResponse response = factory.toResponse(entity);
|
||||||
|
|
||||||
|
assertNotNull(response);
|
||||||
|
assertEquals(3003L, response.getId());
|
||||||
|
assertEquals("RAG", response.getSourceType());
|
||||||
|
assertEquals(1001L, response.getSourceId());
|
||||||
|
assertEquals("知识库说明.pdf", response.getOriginalName());
|
||||||
|
assertEquals("uuid.pdf", response.getFileName());
|
||||||
|
assertEquals("pdf", response.getFileSuffix());
|
||||||
|
assertEquals("application/pdf", response.getContentType());
|
||||||
|
assertEquals(1024L, response.getFileSize());
|
||||||
|
assertEquals("LOCAL", response.getStorageType());
|
||||||
|
assertEquals("上传附件", response.getRemark());
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user