Compare commits
10 Commits
736f400119
...
16f9a325d7
| Author | SHA1 | Date | |
|---|---|---|---|
| 16f9a325d7 | |||
| e68150ad02 | |||
|
|
bc225d3557 | ||
|
|
fc97c3998d | ||
|
|
603c006c49 | ||
|
|
067b098aa1 | ||
| b4e8324d60 | |||
| d7dcf4c8e8 | |||
| 852cb7e7a6 | |||
| f553409544 |
185
AGENT.md
Normal file
185
AGENT.md
Normal file
@@ -0,0 +1,185 @@
|
||||
# 通用 Agent 平台设计草案
|
||||
|
||||
## 1. 项目定位
|
||||
|
||||
`common_agent` 的目标是构建一个基于 Java、Spring Boot、Spring AI 的通用 Agent 平台,支持:
|
||||
|
||||
- 多 Agent / 单 Agent 运行
|
||||
- 工具调用与流程编排
|
||||
- 会话上下文管理
|
||||
- RAG 知识库接入
|
||||
- 文件上传与附件管理
|
||||
- 前后端统一的管理控制台
|
||||
|
||||
当前阶段以“先搭平台骨架,再逐步补智能能力”为主,优先保证工程结构、接口规范、知识库链路和可扩展性。
|
||||
|
||||
## 2. 总体设计思路
|
||||
|
||||
平台整体按“接入层 - 应用层 - 领域层 - 基础设施层”拆分:
|
||||
|
||||
- 接入层
|
||||
提供 REST API、后续可扩展 WebSocket / SSE,用于前端控制台和外部系统接入。
|
||||
|
||||
- 应用层
|
||||
负责请求编排、DTO 转换、统一返回体、会话协调和 Agent 调度入口。
|
||||
|
||||
- 领域层
|
||||
承载核心业务对象,如系统枚举、附件、知识库、知识文档、Agent 配置、任务执行记录等。
|
||||
|
||||
- 基础设施层
|
||||
负责数据库访问、文件存储、模型调用、向量检索、日志、缓存和第三方工具适配。
|
||||
|
||||
## 3. 核心模块规划
|
||||
|
||||
### 3.1 系统基础模块
|
||||
|
||||
用于支撑整个平台的通用能力:
|
||||
|
||||
- `sys_enum`:系统枚举配置
|
||||
- `sys_attachment`:附件与文件上传
|
||||
- 统一 DTO / `RequestResult`
|
||||
- 通用状态枚举、启用禁用枚举
|
||||
- 后续可补用户、权限、审计等基础能力
|
||||
|
||||
### 3.2 RAG 知识库模块
|
||||
|
||||
当前已经有初步表设计与 Java 骨架:
|
||||
|
||||
- `rag_store`:知识库主表
|
||||
- `rag_document`:知识库文档表
|
||||
|
||||
后续计划继续扩展:
|
||||
|
||||
- 文档切片
|
||||
- 向量化
|
||||
- 检索召回
|
||||
- 索引任务
|
||||
|
||||
当前设计原则:
|
||||
|
||||
- 文件物理信息放在 `sys_attachment`
|
||||
- 业务归属关系通过 `source_type`、`source_id` 或文档关联字段承接
|
||||
- RAG 领域代码独立放在 `com.bruce.rag`
|
||||
|
||||
### 3.3 Agent 运行模块
|
||||
|
||||
后续平台重点能力,建议逐步补齐:
|
||||
|
||||
- Agent 定义
|
||||
- Prompt 模板
|
||||
- 工具注册与调用
|
||||
- 会话上下文与记忆
|
||||
- 执行日志与任务状态
|
||||
- 多步骤编排
|
||||
|
||||
建议未来增加的核心对象:
|
||||
|
||||
- `agent_definition`
|
||||
- `agent_session`
|
||||
- `agent_message`
|
||||
- `agent_task`
|
||||
- `agent_tool`
|
||||
|
||||
### 3.4 管理控制台模块
|
||||
|
||||
后续需要一个前端控制台,至少覆盖:
|
||||
|
||||
- 枚举管理
|
||||
- 附件管理
|
||||
- 知识库管理
|
||||
- 文档上传与状态查看
|
||||
- Agent 调试页
|
||||
- 执行日志查看
|
||||
|
||||
## 4. 当前接口设计原则
|
||||
|
||||
项目后续统一遵循这些规则:
|
||||
|
||||
1. `controller` 不直接暴露实体类
|
||||
所有请求和响应优先走 DTO。
|
||||
|
||||
2. `service` 尽量以 DTO 为边界
|
||||
持久化实体只在内部流转,不直接穿透到外层接口。
|
||||
|
||||
3. 查询条件不直接使用多个裸参数
|
||||
尽量改成 `QueryRequest` / `SaveRequest` / `Response` 形式。
|
||||
|
||||
4. 统一返回体
|
||||
使用 `RequestResult<T>` 作为标准响应包装。
|
||||
|
||||
5. 基础枚举统一化
|
||||
通用状态、启用禁用、RAG 解析/索引状态等统一管理。
|
||||
|
||||
## 5. 数据与存储设计
|
||||
|
||||
### 5.1 关系型数据库
|
||||
|
||||
当前主数据库使用 PostgreSQL。
|
||||
|
||||
已确定的方向:
|
||||
|
||||
- 业务表采用 PostgreSQL 规范 SQL
|
||||
- 主键使用 MyBatis-Plus `ASSIGN_ID`
|
||||
- 通用字段沉淀到 `BaseEntity`
|
||||
|
||||
### 5.2 向量能力
|
||||
|
||||
知识库检索阶段优先使用 PostgreSQL + `pgvector`:
|
||||
|
||||
- 统一技术栈
|
||||
- 降低部署复杂度
|
||||
- 便于关系数据与向量数据联动
|
||||
|
||||
适合项目当前阶段的中小规模知识库场景。
|
||||
|
||||
### 5.3 文件存储
|
||||
|
||||
当前先走本地文件存储,后续可抽象成:
|
||||
|
||||
- 本地文件系统
|
||||
- MinIO / S3
|
||||
- 其他对象存储
|
||||
|
||||
## 6. 当前阶段开发优先级
|
||||
|
||||
建议优先顺序如下:
|
||||
|
||||
1. 统一接口层规范
|
||||
DTO、返回体、基础校验、通用异常处理
|
||||
|
||||
2. 收紧基础模块
|
||||
`sys_enum`、`sys_attachment`
|
||||
|
||||
3. 补全 RAG 基础业务闭环
|
||||
`rag_store`、`rag_document`、文件归属、文档状态
|
||||
|
||||
4. 接入 Spring AI
|
||||
|
||||
5. 建立 Agent 运行时骨架
|
||||
|
||||
6. 补前端控制台
|
||||
|
||||
## 7. 下一步建议
|
||||
|
||||
结合当前代码状态,接下来建议重点做:
|
||||
|
||||
- 完成现有三块接口 DTO 化改造
|
||||
- 建立统一异常处理和错误码规范
|
||||
- 完善 `rag_store` / `rag_document` 的增删改查
|
||||
- 增加知识库文档上传并自动关联附件
|
||||
- 为后续切片与向量化预留任务入口
|
||||
|
||||
## 8. 文档用途说明
|
||||
|
||||
本文件是项目级的 Agent 平台设计入口文档,当前用于:
|
||||
|
||||
- 对齐项目方向
|
||||
- 固定基础架构思路
|
||||
- 作为后续详细设计和模块拆分的上层说明
|
||||
|
||||
后续可继续拆成:
|
||||
|
||||
- `agent-runtime.md`
|
||||
- `rag-design.md`
|
||||
- `api-style.md`
|
||||
- `frontend-console.md`
|
||||
17
README.md
17
README.md
@@ -87,6 +87,22 @@ spring:
|
||||
|
||||
当前阶段还没有加入 Web 服务依赖或常驻任务,所以应用可能启动成功后立即退出。
|
||||
|
||||
启动前端:
|
||||
|
||||
```powershell
|
||||
cd frontend
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
前端检查:
|
||||
|
||||
```powershell
|
||||
cd frontend
|
||||
npm run type-check
|
||||
npm run build
|
||||
```
|
||||
|
||||
## 规划模块
|
||||
|
||||
- `agent-core`:Agent 执行模型、工具注册、记忆和编排能力。
|
||||
@@ -106,4 +122,3 @@ spring:
|
||||
- [Spring AI Reference](https://docs.spring.io/spring-ai/reference/)
|
||||
- [Spring AI RAG Reference](https://docs.spring.io/spring-ai/reference/api/retrieval-augmented-generation.html)
|
||||
- [MyBatis-Plus](https://baomidou.com/)
|
||||
|
||||
|
||||
414
docs/superpowers/plans/2026-05-18-dto-request-result-refactor.md
Normal file
414
docs/superpowers/plans/2026-05-18-dto-request-result-refactor.md
Normal file
@@ -0,0 +1,414 @@
|
||||
# DTO And RequestResult Refactor Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** 将现有枚举、附件、RAG 三块接口统一改造成 DTO 入参与 DTO 返回,并引入 `RequestResult` 作为统一响应包装。
|
||||
|
||||
**Architecture:** `controller`、`service`、`mapper` 三层都尽量以 DTO 作为边界对象,实体类仅用于 MyBatis-Plus 持久化与表映射。控制层统一返回 `RequestResult<T>`,查询条件走 request/query DTO,列表和详情都返回 response DTO,避免继续直接暴露实体和零散参数。
|
||||
|
||||
**Tech Stack:** Java 21、Spring Boot 4、MyBatis-Plus、Spring MVC、JUnit 5
|
||||
|
||||
---
|
||||
|
||||
### Task 1: 建立统一响应体和 DTO 包结构
|
||||
|
||||
**Files:**
|
||||
- Create: `src/main/java/com/bruce/common/dto/RequestResult.java`
|
||||
- Create: `src/main/java/com/bruce/common/dto/request/`
|
||||
- Create: `src/main/java/com/bruce/common/dto/response/`
|
||||
- Create: `src/main/java/com/bruce/rag/dto/request/`
|
||||
- Create: `src/main/java/com/bruce/rag/dto/response/`
|
||||
- Modify: `src/test/java/com/bruce/common/enumconfig/SysEnumComponentStructureTests.java`
|
||||
- Modify: `src/test/java/com/bruce/rag/RagComponentStructureTests.java`
|
||||
|
||||
- [ ] **Step 1: 写失败测试,固定统一返回体存在且控制层不再暴露裸实体**
|
||||
|
||||
在结构测试中增加如下断言思路:
|
||||
|
||||
```java
|
||||
Method saveOrUpdateMethod = SysEnumController.class.getMethod("saveOrUpdate", SysEnumSaveRequest.class);
|
||||
assertEquals(RequestResult.class, saveOrUpdateMethod.getReturnType());
|
||||
```
|
||||
|
||||
```java
|
||||
Method listMethod = RagStoreController.class.getMethod("list", RagStoreQueryRequest.class);
|
||||
assertEquals(RequestResult.class, listMethod.getReturnType());
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 运行测试并确认失败**
|
||||
|
||||
Run: `.\mvnw.cmd "-Dtest=SysEnumComponentStructureTests,RagComponentStructureTests" test`
|
||||
|
||||
Expected: FAIL,提示 DTO 或 `RequestResult` 类型不存在,或控制器方法签名不匹配。
|
||||
|
||||
- [ ] **Step 3: 最小化实现统一响应体和基础 DTO 目录**
|
||||
|
||||
`RequestResult.java` 采用如下结构:
|
||||
|
||||
```java
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class RequestResult<T> {
|
||||
private boolean success;
|
||||
private String code;
|
||||
private String message;
|
||||
private T data;
|
||||
|
||||
public static <T> RequestResult<T> success(T data) {
|
||||
return new RequestResult<>(true, "SUCCESS", "操作成功", data);
|
||||
}
|
||||
|
||||
public static <T> RequestResult<T> success(String message, T data) {
|
||||
return new RequestResult<>(true, "SUCCESS", message, data);
|
||||
}
|
||||
|
||||
public static <T> RequestResult<T> failure(String code, String message) {
|
||||
return new RequestResult<>(false, code, message, null);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 运行测试并确认通过**
|
||||
|
||||
Run: `.\mvnw.cmd "-Dtest=SysEnumComponentStructureTests,RagComponentStructureTests" test`
|
||||
|
||||
Expected: PASS 或只剩下后续控制器签名相关失败。
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src/main/java/com/bruce/common/dto src/main/java/com/bruce/rag/dto src/test/java/com/bruce/common/enumconfig/SysEnumComponentStructureTests.java src/test/java/com/bruce/rag/RagComponentStructureTests.java
|
||||
git commit -m "refactor: 增加统一响应体与DTO结构"
|
||||
```
|
||||
|
||||
### Task 2: 重构 sys_enum 模块为 DTO 入参与 DTO 返回
|
||||
|
||||
**Files:**
|
||||
- Create: `src/main/java/com/bruce/common/dto/request/SysEnumQueryRequest.java`
|
||||
- Create: `src/main/java/com/bruce/common/dto/request/SysEnumSaveRequest.java`
|
||||
- Create: `src/main/java/com/bruce/common/dto/response/SysEnumResponse.java`
|
||||
- Modify: `src/main/java/com/bruce/common/controller/SysEnumController.java`
|
||||
- Modify: `src/main/java/com/bruce/common/service/ISysEnumService.java`
|
||||
- Modify: `src/main/java/com/bruce/common/service/impl/SysEnumServiceImpl.java`
|
||||
- Modify: `src/main/java/com/bruce/common/mapper/SysEnumMapper.java`
|
||||
- Modify: `src/test/java/com/bruce/common/enumconfig/SysEnumComponentStructureTests.java`
|
||||
|
||||
- [ ] **Step 1: 写失败测试,固定 sys_enum 控制器和服务都使用 DTO**
|
||||
|
||||
在 `SysEnumComponentStructureTests` 中增加断言:
|
||||
|
||||
```java
|
||||
Method queryMethod = SysEnumController.class.getMethod("queryByCatalogAndType", SysEnumQueryRequest.class);
|
||||
Method saveMethod = SysEnumController.class.getMethod("saveOrUpdate", SysEnumSaveRequest.class);
|
||||
Method serviceMethod = ISysEnumService.class.getMethod("listByCatalogAndType", SysEnumQueryRequest.class);
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 运行测试并确认失败**
|
||||
|
||||
Run: `.\mvnw.cmd "-Dtest=SysEnumComponentStructureTests" test`
|
||||
|
||||
Expected: FAIL,提示方法签名仍然是 `String` 或 `SysEnum`。
|
||||
|
||||
- [ ] **Step 3: 最小化实现 request/response DTO**
|
||||
|
||||
`SysEnumQueryRequest.java`:
|
||||
|
||||
```java
|
||||
@Data
|
||||
public class SysEnumQueryRequest {
|
||||
private String catalog;
|
||||
private String type;
|
||||
}
|
||||
```
|
||||
|
||||
`SysEnumSaveRequest.java`:
|
||||
|
||||
```java
|
||||
@Data
|
||||
public class SysEnumSaveRequest {
|
||||
private Long id;
|
||||
private String catalog;
|
||||
private String type;
|
||||
private String name;
|
||||
private Integer value;
|
||||
private String strvalue;
|
||||
private Integer sort;
|
||||
private String remark;
|
||||
}
|
||||
```
|
||||
|
||||
`SysEnumResponse.java`:
|
||||
|
||||
```java
|
||||
@Data
|
||||
public class SysEnumResponse {
|
||||
private Long id;
|
||||
private String catalog;
|
||||
private String type;
|
||||
private String name;
|
||||
private Integer value;
|
||||
private String strvalue;
|
||||
private Integer sort;
|
||||
private String remark;
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 修改 mapper/service/controller**
|
||||
|
||||
- `ISysEnumService` 返回 `List<SysEnumResponse>`,保存返回 `SysEnumResponse`
|
||||
- `SysEnumServiceImpl` 新增 DTO 与实体互转私有方法
|
||||
- `SysEnumMapper` 保留 MP 基础能力;如需自定义查询,新增 DTO 查询方法签名
|
||||
- `SysEnumController` 所有接口返回 `RequestResult<?>`
|
||||
|
||||
控制器目标形态:
|
||||
|
||||
```java
|
||||
@PostMapping("/query")
|
||||
public RequestResult<List<SysEnumResponse>> queryByCatalogAndType(@RequestBody SysEnumQueryRequest request) {
|
||||
return RequestResult.success(sysEnumService.listByCatalogAndType(request));
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 5: 运行测试并确认通过**
|
||||
|
||||
Run: `.\mvnw.cmd "-Dtest=SysEnumComponentStructureTests" test`
|
||||
|
||||
Expected: PASS
|
||||
|
||||
- [ ] **Step 6: Commit**
|
||||
|
||||
```bash
|
||||
git add src/main/java/com/bruce/common/controller/SysEnumController.java src/main/java/com/bruce/common/service/ISysEnumService.java src/main/java/com/bruce/common/service/impl/SysEnumServiceImpl.java src/main/java/com/bruce/common/dto/request/SysEnumQueryRequest.java src/main/java/com/bruce/common/dto/request/SysEnumSaveRequest.java src/main/java/com/bruce/common/dto/response/SysEnumResponse.java src/test/java/com/bruce/common/enumconfig/SysEnumComponentStructureTests.java
|
||||
git commit -m "refactor: 调整sys_enum接口为DTO模式"
|
||||
```
|
||||
|
||||
### Task 3: 重构 sys_attachment 模块为 DTO 入参与 DTO 返回
|
||||
|
||||
**Files:**
|
||||
- Create: `src/main/java/com/bruce/common/dto/request/SysAttachmentUploadRequest.java`
|
||||
- Create: `src/main/java/com/bruce/common/dto/request/SysAttachmentQueryRequest.java`
|
||||
- Create: `src/main/java/com/bruce/common/dto/response/SysAttachmentResponse.java`
|
||||
- Modify: `src/main/java/com/bruce/common/controller/SysAttachmentController.java`
|
||||
- Modify: `src/main/java/com/bruce/common/service/ISysAttachmentService.java`
|
||||
- Modify: `src/main/java/com/bruce/common/service/impl/SysAttachmentServiceImpl.java`
|
||||
- Modify: `src/test/java/com/bruce/common/attachment/SysAttachmentComponentStructureTests.java`
|
||||
|
||||
- [ ] **Step 1: 写失败测试,固定附件接口返回 `RequestResult` 且 service 返回 DTO**
|
||||
|
||||
示例断言:
|
||||
|
||||
```java
|
||||
Method uploadMethod = SysAttachmentController.class.getMethod("upload", MultipartFile.class, SysAttachmentUploadRequest.class);
|
||||
assertEquals(RequestResult.class, uploadMethod.getReturnType());
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 运行测试并确认失败**
|
||||
|
||||
Run: `.\mvnw.cmd "-Dtest=SysAttachmentComponentStructureTests" test`
|
||||
|
||||
Expected: FAIL,提示控制器或服务方法签名不匹配。
|
||||
|
||||
- [ ] **Step 3: 新增附件 DTO**
|
||||
|
||||
`SysAttachmentUploadRequest.java`:
|
||||
|
||||
```java
|
||||
@Data
|
||||
public class SysAttachmentUploadRequest {
|
||||
private String sourceType;
|
||||
private Long sourceId;
|
||||
}
|
||||
```
|
||||
|
||||
`SysAttachmentQueryRequest.java`:
|
||||
|
||||
```java
|
||||
@Data
|
||||
public class SysAttachmentQueryRequest {
|
||||
private String sourceType;
|
||||
private Long sourceId;
|
||||
}
|
||||
```
|
||||
|
||||
`SysAttachmentResponse.java`:
|
||||
|
||||
```java
|
||||
@Data
|
||||
public class SysAttachmentResponse {
|
||||
private Long id;
|
||||
private String sourceType;
|
||||
private Long sourceId;
|
||||
private String originalName;
|
||||
private String fileName;
|
||||
private String fileSuffix;
|
||||
private String contentType;
|
||||
private Long fileSize;
|
||||
private String storageType;
|
||||
private String filePath;
|
||||
private String fileUrl;
|
||||
private String remark;
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 修改附件控制器和服务**
|
||||
|
||||
- `ISysAttachmentService.upload` 返回 `SysAttachmentResponse`
|
||||
- 控制器上传接口返回 `RequestResult<SysAttachmentResponse>`
|
||||
- 如补充列表查询,也走 `SysAttachmentQueryRequest`
|
||||
|
||||
- [ ] **Step 5: 运行测试并确认通过**
|
||||
|
||||
Run: `.\mvnw.cmd "-Dtest=SysAttachmentComponentStructureTests" test`
|
||||
|
||||
Expected: PASS
|
||||
|
||||
- [ ] **Step 6: Commit**
|
||||
|
||||
```bash
|
||||
git add src/main/java/com/bruce/common/controller/SysAttachmentController.java src/main/java/com/bruce/common/service/ISysAttachmentService.java src/main/java/com/bruce/common/service/impl/SysAttachmentServiceImpl.java src/main/java/com/bruce/common/dto/request/SysAttachmentUploadRequest.java src/main/java/com/bruce/common/dto/request/SysAttachmentQueryRequest.java src/main/java/com/bruce/common/dto/response/SysAttachmentResponse.java src/test/java/com/bruce/common/attachment/SysAttachmentComponentStructureTests.java
|
||||
git commit -m "refactor: 调整附件接口为DTO模式"
|
||||
```
|
||||
|
||||
### Task 4: 重构 rag_store 与 rag_document 模块为 DTO 入参与 DTO 返回
|
||||
|
||||
**Files:**
|
||||
- Create: `src/main/java/com/bruce/rag/dto/request/RagStoreQueryRequest.java`
|
||||
- Create: `src/main/java/com/bruce/rag/dto/request/RagStoreSaveRequest.java`
|
||||
- Create: `src/main/java/com/bruce/rag/dto/request/RagDocumentQueryRequest.java`
|
||||
- Create: `src/main/java/com/bruce/rag/dto/request/RagDocumentSaveRequest.java`
|
||||
- Create: `src/main/java/com/bruce/rag/dto/response/RagStoreResponse.java`
|
||||
- Create: `src/main/java/com/bruce/rag/dto/response/RagDocumentResponse.java`
|
||||
- Modify: `src/main/java/com/bruce/rag/controller/RagStoreController.java`
|
||||
- Modify: `src/main/java/com/bruce/rag/controller/RagDocumentController.java`
|
||||
- Modify: `src/main/java/com/bruce/rag/service/IRagStoreService.java`
|
||||
- Modify: `src/main/java/com/bruce/rag/service/IRagDocumentService.java`
|
||||
- Modify: `src/main/java/com/bruce/rag/service/impl/RagStoreServiceImpl.java`
|
||||
- Modify: `src/main/java/com/bruce/rag/service/impl/RagDocumentServiceImpl.java`
|
||||
- Modify: `src/test/java/com/bruce/rag/RagComponentStructureTests.java`
|
||||
|
||||
- [ ] **Step 1: 写失败测试,固定 RAG 控制器和服务都使用 DTO**
|
||||
|
||||
示例断言:
|
||||
|
||||
```java
|
||||
Method storeListMethod = RagStoreController.class.getMethod("list", RagStoreQueryRequest.class);
|
||||
Method documentListMethod = RagDocumentController.class.getMethod("list", RagDocumentQueryRequest.class);
|
||||
assertEquals(RequestResult.class, storeListMethod.getReturnType());
|
||||
assertEquals(RequestResult.class, documentListMethod.getReturnType());
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 运行测试并确认失败**
|
||||
|
||||
Run: `.\mvnw.cmd "-Dtest=RagComponentStructureTests" test`
|
||||
|
||||
Expected: FAIL,提示控制器和 service 仍然暴露实体或无 DTO。
|
||||
|
||||
- [ ] **Step 3: 新增 RAG DTO**
|
||||
|
||||
`RagStoreQueryRequest.java`:
|
||||
|
||||
```java
|
||||
@Data
|
||||
public class RagStoreQueryRequest {
|
||||
private String storeCode;
|
||||
private String storeName;
|
||||
private String status;
|
||||
}
|
||||
```
|
||||
|
||||
`RagStoreResponse.java`:
|
||||
|
||||
```java
|
||||
@Data
|
||||
public class RagStoreResponse {
|
||||
private Long id;
|
||||
private String storeCode;
|
||||
private String storeName;
|
||||
private String description;
|
||||
private String status;
|
||||
private String remark;
|
||||
}
|
||||
```
|
||||
|
||||
`RagDocumentQueryRequest.java`:
|
||||
|
||||
```java
|
||||
@Data
|
||||
public class RagDocumentQueryRequest {
|
||||
private Long storeId;
|
||||
private Long attachmentId;
|
||||
private String parseStatus;
|
||||
private String indexStatus;
|
||||
private Boolean enabled;
|
||||
}
|
||||
```
|
||||
|
||||
`RagDocumentResponse.java`:
|
||||
|
||||
```java
|
||||
@Data
|
||||
public class RagDocumentResponse {
|
||||
private Long id;
|
||||
private Long storeId;
|
||||
private Long attachmentId;
|
||||
private String documentTitle;
|
||||
private String documentSummary;
|
||||
private String parseStatus;
|
||||
private String indexStatus;
|
||||
private Boolean enabled;
|
||||
private String errorMessage;
|
||||
private String remark;
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 修改 RAG service/controller**
|
||||
|
||||
- `IRagStoreService`、`IRagDocumentService` 返回 DTO
|
||||
- `RagStoreController` 和 `RagDocumentController` 返回 `RequestResult`
|
||||
- 查询接口改为 `@PostMapping("/query")` + `@RequestBody QueryRequest`
|
||||
|
||||
- [ ] **Step 5: 运行测试并确认通过**
|
||||
|
||||
Run: `.\mvnw.cmd "-Dtest=RagComponentStructureTests" test`
|
||||
|
||||
Expected: PASS
|
||||
|
||||
- [ ] **Step 6: Commit**
|
||||
|
||||
```bash
|
||||
git add src/main/java/com/bruce/rag/controller/RagStoreController.java src/main/java/com/bruce/rag/controller/RagDocumentController.java src/main/java/com/bruce/rag/service/IRagStoreService.java src/main/java/com/bruce/rag/service/IRagDocumentService.java src/main/java/com/bruce/rag/service/impl/RagStoreServiceImpl.java src/main/java/com/bruce/rag/service/impl/RagDocumentServiceImpl.java src/main/java/com/bruce/rag/dto/request src/main/java/com/bruce/rag/dto/response src/test/java/com/bruce/rag/RagComponentStructureTests.java
|
||||
git commit -m "refactor: 调整RAG接口为DTO模式"
|
||||
```
|
||||
|
||||
### Task 5: 全量验证与整理
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/test/java/com/bruce/common/attachment/SysAttachmentComponentStructureTests.java`
|
||||
- Modify: `src/test/java/com/bruce/common/enumconfig/SysEnumComponentStructureTests.java`
|
||||
- Modify: `src/test/java/com/bruce/rag/RagComponentStructureTests.java`
|
||||
|
||||
- [ ] **Step 1: 运行定向测试,确认三块 DTO 改造都覆盖到**
|
||||
|
||||
Run: `.\mvnw.cmd "-Dtest=SysAttachmentComponentStructureTests,SysEnumComponentStructureTests,RagComponentStructureTests" test`
|
||||
|
||||
Expected: PASS
|
||||
|
||||
- [ ] **Step 2: 运行全量测试**
|
||||
|
||||
Run: `.\mvnw.cmd test`
|
||||
|
||||
Expected: `BUILD SUCCESS`
|
||||
|
||||
- [ ] **Step 3: 检查工作区**
|
||||
|
||||
Run: `git status --short`
|
||||
|
||||
Expected: 仅显示本次预期文件,或为空(若已提交)。
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add src/test/java/com/bruce/common/attachment/SysAttachmentComponentStructureTests.java src/test/java/com/bruce/common/enumconfig/SysEnumComponentStructureTests.java src/test/java/com/bruce/rag/RagComponentStructureTests.java
|
||||
git commit -m "test: 校验DTO接口改造结构"
|
||||
```
|
||||
5
frontend/.gitignore
vendored
Normal file
5
frontend/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
node_modules/
|
||||
dist/
|
||||
*.log
|
||||
*.local
|
||||
|
||||
1
frontend/env.d.ts
vendored
Normal file
1
frontend/env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
12
frontend/index.html
Normal file
12
frontend/index.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Common Agent</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
3345
frontend/package-lock.json
generated
Normal file
3345
frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
32
frontend/package.json
Normal file
32
frontend/package.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "common-agent-frontend",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host 0.0.0.0",
|
||||
"build": "vue-tsc -b && vite build",
|
||||
"preview": "vite preview --host 0.0.0.0",
|
||||
"test:unit": "vitest run",
|
||||
"type-check": "vue-tsc -b"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.2",
|
||||
"axios": "^1.13.2",
|
||||
"element-plus": "^2.11.8",
|
||||
"pinia": "^3.0.4",
|
||||
"vue": "^3.5.24",
|
||||
"vue-router": "^4.6.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/tsconfig": "^0.8.1",
|
||||
"@vitejs/plugin-vue": "^6.0.2",
|
||||
"@types/node": "^24.10.2",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"jsdom": "^27.2.0",
|
||||
"typescript": "~5.9.3",
|
||||
"vite": "^7.2.7",
|
||||
"vitest": "^4.0.15",
|
||||
"vue-tsc": "^3.1.8"
|
||||
}
|
||||
}
|
||||
3
frontend/src/App.vue
Normal file
3
frontend/src/App.vue
Normal file
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<RouterView />
|
||||
</template>
|
||||
9
frontend/src/api/__tests__/request.spec.ts
Normal file
9
frontend/src/api/__tests__/request.spec.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { request } from '../request';
|
||||
|
||||
describe('request client', () => {
|
||||
it('uses the backend api prefix', () => {
|
||||
expect(request.defaults.baseURL).toBe('/api');
|
||||
});
|
||||
});
|
||||
21
frontend/src/api/request.ts
Normal file
21
frontend/src/api/request.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import axios from 'axios';
|
||||
import type { AxiosRequestConfig } from 'axios';
|
||||
|
||||
export interface RequestResult<T> {
|
||||
code: number;
|
||||
message: string;
|
||||
data: T;
|
||||
}
|
||||
|
||||
export const request = axios.create({
|
||||
baseURL: '/api',
|
||||
timeout: 15000,
|
||||
});
|
||||
|
||||
export function get<T>(url: string, config?: AxiosRequestConfig) {
|
||||
return request.get<RequestResult<T>>(url, config).then((response) => response.data);
|
||||
}
|
||||
|
||||
export function post<T, D = unknown>(url: string, data?: D, config?: AxiosRequestConfig) {
|
||||
return request.post<RequestResult<T>>(url, data, config).then((response) => response.data);
|
||||
}
|
||||
62
frontend/src/layouts/AdminLayout.vue
Normal file
62
frontend/src/layouts/AdminLayout.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
Box,
|
||||
Collection,
|
||||
DataBoard,
|
||||
Document,
|
||||
Files,
|
||||
Grid,
|
||||
} from '@element-plus/icons-vue';
|
||||
import { computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import { useAppStore } from '@/stores/app';
|
||||
|
||||
const route = useRoute();
|
||||
const appStore = useAppStore();
|
||||
|
||||
const pageTitle = computed(() => String(route.meta.title ?? 'Common Agent'));
|
||||
|
||||
const menuItems = [
|
||||
{ path: '/dashboard', label: '工作台', icon: DataBoard },
|
||||
{ path: '/system/enums', label: '系统枚举', icon: Grid },
|
||||
{ path: '/system/attachments', label: '附件管理', icon: Files },
|
||||
{ path: '/rag/stores', label: '知识库', icon: Collection },
|
||||
{ path: '/rag/documents', label: '知识文档', icon: Document },
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-container class="admin-layout">
|
||||
<el-aside class="admin-sidebar" width="232px">
|
||||
<div class="brand">
|
||||
<el-icon :size="24">
|
||||
<Box />
|
||||
</el-icon>
|
||||
<span>Common Agent</span>
|
||||
</div>
|
||||
|
||||
<el-menu class="side-menu" :default-active="$route.path" router>
|
||||
<el-menu-item v-for="item in menuItems" :key="item.path" :index="item.path">
|
||||
<el-icon>
|
||||
<component :is="item.icon" />
|
||||
</el-icon>
|
||||
<span>{{ item.label }}</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</el-aside>
|
||||
|
||||
<el-container>
|
||||
<el-header class="admin-header">
|
||||
<div>
|
||||
<h1>{{ pageTitle }}</h1>
|
||||
<p>{{ appStore.environmentName }}</p>
|
||||
</div>
|
||||
</el-header>
|
||||
|
||||
<el-main class="admin-main">
|
||||
<RouterView />
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</template>
|
||||
17
frontend/src/main.ts
Normal file
17
frontend/src/main.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import 'element-plus/dist/index.css';
|
||||
import './styles/global.css';
|
||||
|
||||
import ElementPlus from 'element-plus';
|
||||
import { createPinia } from 'pinia';
|
||||
import { createApp } from 'vue';
|
||||
|
||||
import App from './App.vue';
|
||||
import router from './router';
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
app.use(createPinia());
|
||||
app.use(router);
|
||||
app.use(ElementPlus);
|
||||
|
||||
app.mount('#app');
|
||||
8
frontend/src/pages/DashboardPage.vue
Normal file
8
frontend/src/pages/DashboardPage.vue
Normal file
@@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<section class="page-panel">
|
||||
<div class="page-panel__header">
|
||||
<h2>工作台</h2>
|
||||
<span>Console</span>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
6
frontend/src/pages/NotFoundPage.vue
Normal file
6
frontend/src/pages/NotFoundPage.vue
Normal file
@@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<main class="not-found">
|
||||
<h1>404</h1>
|
||||
<RouterLink to="/dashboard">返回工作台</RouterLink>
|
||||
</main>
|
||||
</template>
|
||||
8
frontend/src/pages/RagDocumentsPage.vue
Normal file
8
frontend/src/pages/RagDocumentsPage.vue
Normal file
@@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<section class="page-panel">
|
||||
<div class="page-panel__header">
|
||||
<h2>知识文档</h2>
|
||||
<span>Documents</span>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
8
frontend/src/pages/RagStoresPage.vue
Normal file
8
frontend/src/pages/RagStoresPage.vue
Normal file
@@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<section class="page-panel">
|
||||
<div class="page-panel__header">
|
||||
<h2>知识库</h2>
|
||||
<span>RAG</span>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
8
frontend/src/pages/SystemAttachmentsPage.vue
Normal file
8
frontend/src/pages/SystemAttachmentsPage.vue
Normal file
@@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<section class="page-panel">
|
||||
<div class="page-panel__header">
|
||||
<h2>附件管理</h2>
|
||||
<span>Files</span>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
8
frontend/src/pages/SystemEnumsPage.vue
Normal file
8
frontend/src/pages/SystemEnumsPage.vue
Normal file
@@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<section class="page-panel">
|
||||
<div class="page-panel__header">
|
||||
<h2>系统枚举</h2>
|
||||
<span>System</span>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
17
frontend/src/router/__tests__/router.spec.ts
Normal file
17
frontend/src/router/__tests__/router.spec.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { routes } from '../index';
|
||||
|
||||
describe('router', () => {
|
||||
it('defines the admin shell routes', () => {
|
||||
const paths = routes.map((route) => route.path);
|
||||
|
||||
expect(paths).toContain('/');
|
||||
expect(paths).toContain('/dashboard');
|
||||
expect(paths).toContain('/system/enums');
|
||||
expect(paths).toContain('/system/attachments');
|
||||
expect(paths).toContain('/rag/stores');
|
||||
expect(paths).toContain('/rag/documents');
|
||||
expect(paths).toContain('/:pathMatch(.*)*');
|
||||
});
|
||||
});
|
||||
109
frontend/src/router/index.ts
Normal file
109
frontend/src/router/index.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
import { createRouter, createWebHistory } from 'vue-router';
|
||||
|
||||
import DashboardPage from '@/pages/DashboardPage.vue';
|
||||
import NotFoundPage from '@/pages/NotFoundPage.vue';
|
||||
import RagDocumentsPage from '@/pages/RagDocumentsPage.vue';
|
||||
import RagStoresPage from '@/pages/RagStoresPage.vue';
|
||||
import SystemAttachmentsPage from '@/pages/SystemAttachmentsPage.vue';
|
||||
import SystemEnumsPage from '@/pages/SystemEnumsPage.vue';
|
||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||
|
||||
export const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/dashboard',
|
||||
},
|
||||
{
|
||||
path: '/dashboard',
|
||||
name: 'dashboard',
|
||||
component: DashboardPage,
|
||||
meta: { title: '工作台' },
|
||||
},
|
||||
{
|
||||
path: '/system/enums',
|
||||
name: 'system-enums',
|
||||
component: SystemEnumsPage,
|
||||
meta: { title: '系统枚举' },
|
||||
},
|
||||
{
|
||||
path: '/system/attachments',
|
||||
name: 'system-attachments',
|
||||
component: SystemAttachmentsPage,
|
||||
meta: { title: '附件管理' },
|
||||
},
|
||||
{
|
||||
path: '/rag/stores',
|
||||
name: 'rag-stores',
|
||||
component: RagStoresPage,
|
||||
meta: { title: '知识库' },
|
||||
},
|
||||
{
|
||||
path: '/rag/documents',
|
||||
name: 'rag-documents',
|
||||
component: RagDocumentsPage,
|
||||
meta: { title: '知识文档' },
|
||||
},
|
||||
{
|
||||
path: '/:pathMatch(.*)*',
|
||||
name: 'not-found',
|
||||
component: NotFoundPage,
|
||||
meta: { title: '页面不存在' },
|
||||
},
|
||||
];
|
||||
|
||||
const routerRoutes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/dashboard',
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
component: AdminLayout,
|
||||
children: [
|
||||
{
|
||||
path: 'dashboard',
|
||||
name: 'dashboard',
|
||||
component: DashboardPage,
|
||||
meta: { title: '工作台' },
|
||||
},
|
||||
{
|
||||
path: 'system/enums',
|
||||
name: 'system-enums',
|
||||
component: SystemEnumsPage,
|
||||
meta: { title: '系统枚举' },
|
||||
},
|
||||
{
|
||||
path: 'system/attachments',
|
||||
name: 'system-attachments',
|
||||
component: SystemAttachmentsPage,
|
||||
meta: { title: '附件管理' },
|
||||
},
|
||||
{
|
||||
path: 'rag/stores',
|
||||
name: 'rag-stores',
|
||||
component: RagStoresPage,
|
||||
meta: { title: '知识库' },
|
||||
},
|
||||
{
|
||||
path: 'rag/documents',
|
||||
name: 'rag-documents',
|
||||
component: RagDocumentsPage,
|
||||
meta: { title: '知识文档' },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/:pathMatch(.*)*',
|
||||
name: 'not-found',
|
||||
component: NotFoundPage,
|
||||
meta: { title: '页面不存在' },
|
||||
},
|
||||
];
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes: routerRoutes,
|
||||
});
|
||||
|
||||
export default router;
|
||||
7
frontend/src/stores/app.ts
Normal file
7
frontend/src/stores/app.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
export const useAppStore = defineStore('app', {
|
||||
state: () => ({
|
||||
environmentName: '本地开发',
|
||||
}),
|
||||
});
|
||||
128
frontend/src/styles/global.css
Normal file
128
frontend/src/styles/global.css
Normal file
@@ -0,0 +1,128 @@
|
||||
:root {
|
||||
color: #1f2937;
|
||||
background: #f4f6f8;
|
||||
font-family:
|
||||
Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
|
||||
sans-serif;
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
min-width: 320px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#app {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.admin-layout {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.admin-sidebar {
|
||||
border-right: 1px solid #d9dee7;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
height: 64px;
|
||||
padding: 0 18px;
|
||||
border-bottom: 1px solid #e5e9f0;
|
||||
color: #182433;
|
||||
font-size: 17px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.side-menu {
|
||||
border-right: 0;
|
||||
padding: 10px 8px;
|
||||
}
|
||||
|
||||
.side-menu .el-menu-item {
|
||||
height: 42px;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.admin-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 64px;
|
||||
border-bottom: 1px solid #d9dee7;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.admin-header h1 {
|
||||
margin: 0;
|
||||
color: #172033;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.admin-header p {
|
||||
margin: 4px 0 0;
|
||||
color: #667085;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.admin-main {
|
||||
padding: 20px;
|
||||
background: #f4f6f8;
|
||||
}
|
||||
|
||||
.page-panel {
|
||||
min-height: calc(100vh - 104px);
|
||||
border: 1px solid #d9dee7;
|
||||
border-radius: 6px;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.page-panel__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
min-height: 56px;
|
||||
padding: 0 18px;
|
||||
border-bottom: 1px solid #edf0f4;
|
||||
}
|
||||
|
||||
.page-panel__header h2 {
|
||||
margin: 0;
|
||||
color: #1f2937;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.page-panel__header span {
|
||||
color: #768195;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.not-found {
|
||||
display: grid;
|
||||
min-height: 100vh;
|
||||
place-content: center;
|
||||
gap: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.not-found h1 {
|
||||
margin: 0;
|
||||
font-size: 48px;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
12
frontend/tsconfig.app.json
Normal file
12
frontend/tsconfig.app.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
"types": ["vite/client"],
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"]
|
||||
}
|
||||
11
frontend/tsconfig.json
Normal file
11
frontend/tsconfig.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.node.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
17
frontend/tsconfig.node.json
Normal file
17
frontend/tsconfig.node.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
"target": "ES2023",
|
||||
"lib": ["ES2023"],
|
||||
"module": "ESNext",
|
||||
"types": ["node"],
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"strict": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
25
frontend/vite.config.ts
Normal file
25
frontend/vite.config.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { fileURLToPath, URL } from 'node:url';
|
||||
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
port: 5173,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:8080',
|
||||
changeOrigin: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
test: {
|
||||
environment: 'jsdom',
|
||||
},
|
||||
});
|
||||
41
script/testdoc/city_information.txt
Normal file
41
script/testdoc/city_information.txt
Normal file
@@ -0,0 +1,41 @@
|
||||
中国城市信息测试文档
|
||||
|
||||
北京是中国的首都,也是全国政治、文化、国际交往和科技创新中心。北京拥有故宫、天坛、颐和园等历史文化资源,同时聚集了大量高校、科研机构和互联网企业。
|
||||
|
||||
上海位于长江入海口,是中国重要的国际经济、金融、贸易和航运中心。上海拥有陆家嘴金融区、外滩、张江科学城等代表性区域,现代服务业和先进制造业都很发达。
|
||||
|
||||
广州是广东省省会,地处珠江三角洲北缘,是国家中心城市之一。广州商贸传统深厚,广交会长期在这里举办,汽车、电子、食品和现代服务业发展较快。
|
||||
|
||||
深圳位于广东省南部,毗邻香港,是中国改革开放后快速发展起来的创新型城市。深圳聚集了大量科技企业,在电子信息、金融科技、人工智能和机器人产业方面具有优势。
|
||||
|
||||
杭州是浙江省省会,位于长江三角洲南翼。杭州以西湖闻名,也因数字经济、电商平台、云计算和文化旅游产业而受到关注。
|
||||
|
||||
成都是四川省省会,是中国西部重要的经济、科技和交通中心。成都生活服务业、电子信息产业、游戏产业和美食文化都很有代表性。
|
||||
|
||||
武汉是湖北省省会,位于长江和汉江交汇处,是中部地区重要的交通枢纽和科教城市。武汉拥有光电子信息、生物医药、汽车制造等产业基础。
|
||||
|
||||
西安是陕西省省会,也是中国历史文化名城。西安拥有兵马俑、大雁塔、古城墙等文化资源,同时在航空航天、电子信息和高等教育方面实力较强。
|
||||
|
||||
南京是江苏省省会,位于长江下游地区,是长三角重要中心城市。南京高校资源丰富,软件和信息服务业、智能电网、生物医药等产业具有优势。
|
||||
|
||||
重庆是中国直辖市之一,位于长江上游地区。重庆山地城市特征明显,汽车制造、电子信息、装备制造和火锅餐饮文化都很有代表性。
|
||||
|
||||
天津是中国直辖市之一,位于渤海湾西岸,是北方重要港口城市。天津拥有港口物流、装备制造、航空航天和现代服务业基础。
|
||||
|
||||
苏州位于江苏省东南部,是长三角重要制造业城市。苏州以园林文化闻名,同时在电子信息、生物医药、精密制造和外向型经济方面实力突出。
|
||||
|
||||
郑州是河南省省会,位于中国中部地区,是重要铁路和物流枢纽。郑州在食品加工、装备制造、汽车和商贸物流方面具有较强基础。
|
||||
|
||||
青岛位于山东半岛南部,是重要的沿海开放城市。青岛拥有港口、海洋经济、家电制造、啤酒产业和旅游资源,城市品牌辨识度较高。
|
||||
|
||||
大连位于辽宁省南部,是东北地区重要的港口、工业和旅游城市。大连在船舶制造、软件服务、海洋工程和现代物流方面有一定基础。
|
||||
|
||||
厦门位于福建省东南沿海,是经济特区和海滨旅游城市。厦门港航服务、电子信息、文化旅游和对外贸易较为活跃。
|
||||
|
||||
福州是福建省省会,位于闽江下游。福州拥有三坊七巷等历史街区,在数字经济、新能源材料、海洋经济和纺织鞋服相关产业方面持续发展。
|
||||
|
||||
长沙是湖南省省会,位于湘江下游,是中部地区重要的文化和消费城市。长沙的工程机械、传媒娱乐、食品消费和教育科技产业较有特色。
|
||||
|
||||
昆明是云南省省会,因气候温和被称为春城。昆明是面向南亚东南亚的重要门户城市,花卉、旅游、高原特色农业和生物资源产业具有代表性。
|
||||
|
||||
贵阳是贵州省省会,位于云贵高原东部。贵阳近年来在大数据产业、政务云服务、生态旅游和现代山地特色产业方面发展较快。
|
||||
22
script/testdoc/company_records.txt
Normal file
22
script/testdoc/company_records.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
公司信息测试文档
|
||||
|
||||
北辰智能制造有限公司位于北京,主营工业机器人控制系统和产线数字化改造,创办于2014年,目前未上市。
|
||||
海澜云数科技股份有限公司总部在上海,主要做企业数据中台、云迁移服务和安全运维,成立于2012年,已在科创板上市。
|
||||
珠江冷链物流集团设在广州,主营生鲜冷链运输、城市仓配和供应链金融,创办年份为2008年,暂未上市。
|
||||
前海量化信息技术有限公司位于深圳,业务集中在金融风控模型、智能投研工具和合规数据服务,2016年创办,未上市。
|
||||
西湖语义计算科技有限公司在杭州运营,主营自然语言处理平台、知识库问答和企业搜索,成立于2019年,目前没有上市。
|
||||
蓉城互动娱乐有限公司来自成都,主要开发移动游戏、互动剧情产品和游戏美术外包,创办于2015年,未上市。
|
||||
楚天光电仪器股份有限公司坐落于武汉,主营激光传感器、光通信检测设备,创办于2006年,已在创业板上市。
|
||||
长安微能科技有限公司位于西安,做储能管理系统、充电桩控制器和新能源运维平台,成立于2018年,尚未上市。
|
||||
金陵软件服务有限公司总部在南京,主营政企软件定制、数据治理和低代码平台,创办于2011年,未上市。
|
||||
山城智行汽车科技有限公司位于重庆,主营车联网终端、智能座舱软件和售后数据分析,成立于2017年,未上市。
|
||||
津门港航设备股份有限公司在天津,主要生产港口自动化设备、船岸通信设备,创办于2004年,已在北交所上市。
|
||||
苏园生物医药有限公司位于苏州,主营细胞培养试剂、实验室耗材和生物样本管理系统,成立于2013年,未上市。
|
||||
中原粮食科技集团总部在郑州,主营粮食加工设备、仓储温控系统和农产品追溯,创办于2001年,已在主板上市。
|
||||
琴岛海洋食品有限公司位于青岛,主要经营海产品深加工、预制菜和跨境电商销售,成立于2010年,未上市。
|
||||
星海船舶工程股份有限公司设在大连,主营船舶维修、海工装备配套和港口工程服务,创办于1998年,已在港交所上市。
|
||||
鹭岛航运服务有限公司位于厦门,主营国际货代、船务代理和航线数据服务,成立于2009年,未上市。
|
||||
榕城新能源材料有限公司总部在福州,主营锂电隔膜、储能材料检测和电池回收咨询,创办于2016年,未上市。
|
||||
星沙教育科技有限公司位于长沙,主营在线职业教育、题库系统和教学数据分析,成立于2014年,未上市。
|
||||
春城农业科技有限公司来自昆明,主营高原特色农产品育种、智慧温室和冷链销售,创办于2007年,未上市。
|
||||
黔云数据服务有限公司位于贵阳,主营政务云运维、大数据治理和数据资产评估,成立于2015年,已在新三板挂牌。
|
||||
52
script/testdoc/hotel_introductions.txt
Normal file
52
script/testdoc/hotel_introductions.txt
Normal file
@@ -0,0 +1,52 @@
|
||||
酒店信息测试文档
|
||||
|
||||
北京云栖庭酒店位于北京东城区,酒店主打胡同院落体验,招牌菜是北京烤鸭和炸酱面。
|
||||
上海浦江悦榕酒店坐落在上海黄浦区,特色是江景客房,餐厅推荐本帮红烧肉和响油鳝糊。
|
||||
广州花城雅居酒店在广州天河区,适合商务差旅,特色菜品包括白切鸡、艇仔粥。
|
||||
深圳湾海景酒店位于深圳南山区,靠近科技园和海岸线,主厨菜是客家酿豆腐和潮汕牛肉丸。
|
||||
杭州西溪书院酒店在杭州西湖区,环境清静,招牌菜为西湖醋鱼、龙井虾仁。
|
||||
成都锦里小院酒店位于成都武侯区,带川西院落风格,特色菜是麻婆豆腐和夫妻肺片。
|
||||
武汉江汉云舍酒店坐落于武汉江汉区,交通方便,餐厅供应热干面和武昌鱼。
|
||||
西安雁塔唐风酒店位于西安雁塔区,主打唐风装饰,特色菜品是羊肉泡馍和凉皮。
|
||||
南京秦淮水岸酒店在南京秦淮区,临近夫子庙,推荐盐水鸭和鸭血粉丝汤。
|
||||
重庆山城观景酒店位于重庆渝中区,拥有江景房,特色菜是重庆火锅和辣子鸡。
|
||||
天津海河悦庭酒店位于天津和平区,临近海河,招牌菜为煎饼果子和锅巴菜。
|
||||
苏州园林逸居酒店在苏州姑苏区,庭院精致,餐厅主推松鼠桂鱼和碧螺虾仁。
|
||||
郑州中原商务酒店位于郑州金水区,适合会议住宿,特色菜是烩面和胡辣汤。
|
||||
青岛栈桥海风酒店在青岛市南区,靠近海边,推荐辣炒蛤蜊和鲅鱼水饺。
|
||||
大连星海湾酒店位于大连沙河口区,海景视野好,特色菜是海胆蒸蛋和海鲜焖子。
|
||||
厦门鼓浪海韵酒店位于厦门思明区,适合休闲度假,餐厅提供沙茶面和海蛎煎。
|
||||
福州三坊雅舍酒店在福州鼓楼区,临近三坊七巷,特色菜为佛跳墙和荔枝肉。
|
||||
长沙橘子洲酒店位于长沙岳麓区,客房看江景,主打剁椒鱼头和臭豆腐。
|
||||
昆明滇池花园酒店在昆明西山区,临近滇池,特色菜是过桥米线和汽锅鸡。
|
||||
贵阳黔灵山居酒店位于贵阳云岩区,环境清爽,推荐酸汤鱼和丝娃娃。
|
||||
南宁青秀湖酒店位于南宁青秀区,适合家庭出行,特色菜是老友粉和柠檬鸭。
|
||||
海口骑楼海景酒店在海口龙华区,靠近骑楼老街,餐厅供应海南鸡饭和清补凉。
|
||||
哈尔滨中央大街酒店位于哈尔滨道里区,冬季游客多,招牌菜是锅包肉和红肠拼盘。
|
||||
长春净月湖酒店位于长春南关区,适合会务活动,特色菜有雪衣豆沙和酱骨头。
|
||||
太原晋阳湖酒店在太原晋源区,主打湖景房,餐厅推荐刀削面和过油肉。
|
||||
呼和浩特草原迎宾酒店位于呼和浩特赛罕区,特色是草原主题宴会,菜品有手把肉和奶茶。
|
||||
兰州黄河岸酒店在兰州城关区,临近黄河风情线,特色菜是兰州牛肉面和手抓羊肉。
|
||||
银川贺兰山酒店位于银川金凤区,适合自驾游客,主打烩小吃和羊肉臊子面。
|
||||
乌鲁木齐天山宾馆在乌鲁木齐天山区,交通便利,特色菜是大盘鸡和烤包子。
|
||||
西宁青唐驿酒店位于西宁城西区,适合高原旅行中转,特色菜为手抓羊肉和酸奶。
|
||||
拉萨布达拉观景酒店在拉萨城关区,拥有观景露台,推荐藏面和酥油茶。
|
||||
宁波东钱湖酒店位于宁波鄞州区,湖景安静,特色菜是宁波汤圆和雪菜黄鱼。
|
||||
无锡太湖雅居酒店在无锡滨湖区,靠近太湖,招牌菜为酱排骨和太湖三白。
|
||||
佛山岭南庭院酒店位于佛山禅城区,主打岭南建筑风格,特色菜是顺德双皮奶和均安蒸猪。
|
||||
东莞松山湖酒店在东莞松山湖片区,适合科技企业接待,菜品有烧鹅和莞香鸡。
|
||||
珠海情侣路酒店位于珠海香洲区,海岸景观明显,特色菜是横琴蚝和白灼虾。
|
||||
泉州刺桐客栈在泉州鲤城区,临近古城街巷,推荐面线糊和姜母鸭。
|
||||
南昌滕王阁酒店位于南昌东湖区,江景开阔,特色菜是瓦罐汤和藜蒿炒腊肉。
|
||||
合肥包河商务酒店在合肥包河区,适合短途商务,主打臭鳜鱼和庐州烤鸭。
|
||||
石家庄燕赵酒店位于石家庄长安区,会议设施齐全,特色菜是金毛狮子鱼和驴肉火烧。
|
||||
洛阳牡丹庭酒店在洛阳老城区,春季游客多,特色菜是洛阳水席和浆面条。
|
||||
济南泉城人家酒店位于济南历下区,靠近泉水景区,推荐九转大肠和甜沫。
|
||||
烟台海岸葡园酒店在烟台芝罘区,适合海滨休闲,特色菜是鲅鱼饺子和葱烧海参。
|
||||
沈阳盛京公馆酒店位于沈阳沈河区,装饰带东北民俗元素,菜品有锅包肉和熏肉大饼。
|
||||
襄阳古城驿酒店在襄阳襄城区,临近古城墙,特色菜是襄阳牛肉面和夹沙肉。
|
||||
宝鸡陈仓驿酒店位于宝鸡渭滨区,适合秦岭旅行中转,推荐臊子面和擀面皮。
|
||||
绵阳科技城酒店在绵阳涪城区,服务科技园商务客,特色菜是绵阳米粉和江油肥肠。
|
||||
岳阳洞庭湖酒店位于岳阳岳阳楼区,临近洞庭湖,主打洞庭银鱼和姜辣蛇。
|
||||
大理洱海风月酒店在大理古城附近,适合度假,特色菜是砂锅鱼和乳扇。
|
||||
遵义红城酒店位于遵义红花岗区,红色旅游客源多,招牌菜是豆花面和羊肉粉。
|
||||
108
script/testdoc/people_profiles.txt
Normal file
108
script/testdoc/people_profiles.txt
Normal file
@@ -0,0 +1,108 @@
|
||||
人物信息测试文档
|
||||
|
||||
张伟,34岁,目前在北京一家智能制造企业担任算法工程师,祖籍河北石家庄。
|
||||
李娜今年29岁,是上海某跨境电商公司的产品经理,籍贯江苏南京。
|
||||
王强,45岁,现任广州一家物流集团运营总监,老家在广东佛山。
|
||||
赵敏,31岁,深圳金融科技公司的数据分析师,籍贯湖南长沙。
|
||||
陈磊,38岁,在杭州从事云计算架构工作,出生于浙江宁波。
|
||||
刘洋,27岁,成都游戏公司的角色原画师,籍贯四川绵阳。
|
||||
杨帆今年42岁,武汉光电企业研发主管,祖籍湖北襄阳。
|
||||
黄静,36岁,西安高校讲师,籍贯陕西宝鸡。
|
||||
周涛,33岁,在南京一家软件公司担任后端开发工程师,老家安徽合肥。
|
||||
吴倩,28岁,重庆文旅公司市场策划,籍贯重庆万州。
|
||||
徐明,51岁,天津港口企业安全负责人,祖籍山东济南。
|
||||
孙悦今年30岁,苏州生物医药企业注册专员,籍贯江苏扬州。
|
||||
胡斌,39岁,郑州食品加工厂生产经理,老家河南洛阳。
|
||||
朱琳,26岁,在青岛做品牌设计师,籍贯山东烟台。
|
||||
高峰,44岁,大连船舶企业项目经理,祖籍辽宁沈阳。
|
||||
林晓,32岁,厦门互联网公司的测试工程师,籍贯福建泉州。
|
||||
何俊,37岁,福州新能源公司采购主管,老家江西南昌。
|
||||
郭婷,35岁,长沙教育科技公司教研负责人,籍贯湖南岳阳。
|
||||
马超,41岁,昆明农业科技企业销售总监,祖籍云南大理。
|
||||
罗兰今年29岁,贵阳大数据企业运维工程师,籍贯贵州遵义。
|
||||
梁晨,46岁,南宁建筑设计院结构工程师,老家广西桂林。
|
||||
宋佳,25岁,海口酒店集团客户经理,籍贯海南三亚。
|
||||
唐凯,40岁,哈尔滨装备制造企业工艺工程师,祖籍黑龙江齐齐哈尔。
|
||||
冯雪,33岁,长春汽车零部件公司质量经理,籍贯吉林吉林市。
|
||||
韩旭,48岁,太原煤机企业技术顾问,老家山西大同。
|
||||
曹阳,31岁,呼和浩特乳制品企业渠道主管,籍贯内蒙古包头。
|
||||
彭敏,27岁,兰州医疗器械公司售后工程师,祖籍甘肃天水。
|
||||
曾辉,43岁,银川葡萄酒企业酿造师,籍贯宁夏吴忠。
|
||||
邓洁,30岁,乌鲁木齐贸易公司外贸专员,老家新疆喀什。
|
||||
薛亮,36岁,西宁环保企业项目负责人,籍贯青海海东。
|
||||
魏然,52岁,拉萨旅游服务公司总经理,祖籍四川成都。
|
||||
蒋悦,24岁,宁波港航企业单证员,籍贯浙江绍兴。
|
||||
沈航,34岁,无锡物联网企业嵌入式工程师,老家江苏常州。
|
||||
任洁,29岁,佛山家电企业用户研究员,籍贯广东中山。
|
||||
姚坤,47岁,东莞电子制造企业厂长,祖籍广东梅州。
|
||||
卢欣,31岁,珠海医药企业法务专员,籍贯广东湛江。
|
||||
蔡敏,39岁,泉州鞋服企业供应链经理,老家福建莆田。
|
||||
丁一,28岁,南昌传媒公司视频编导,籍贯江西九江。
|
||||
潘越,35岁,合肥新能源车企系统工程师,祖籍安徽芜湖。
|
||||
邱宁,42岁,石家庄制药企业质量负责人,籍贯河北保定。
|
||||
袁浩,26岁,洛阳机器人公司机械设计师,老家河南开封。
|
||||
程雪,37岁,济南软件园人力资源经理,籍贯山东潍坊。
|
||||
莫凡,33岁,烟台海洋食品企业电商运营,祖籍山东威海。
|
||||
贺琳,45岁,沈阳航空企业采购经理,籍贯辽宁鞍山。
|
||||
叶晨,30岁,襄阳汽车零部件企业工艺员,老家湖北宜昌。
|
||||
孔明,49岁,宝鸡钛材企业设备主管,祖籍陕西咸阳。
|
||||
孟琪,27岁,绵阳电子信息企业硬件工程师,籍贯四川德阳。
|
||||
秦川,41岁,岳阳石化企业安全工程师,老家湖南常德。
|
||||
邵雨,32岁,大理民宿品牌运营经理,籍贯云南丽江。
|
||||
白露,25岁,遵义茶叶公司新媒体专员,祖籍贵州安顺。
|
||||
夏然,38岁,桂林旅游平台产品运营,籍贯广西柳州。
|
||||
陆鸣,44岁,三亚度假酒店餐饮总监,老家海南海口。
|
||||
范哲,36岁,齐齐哈尔装备厂电气工程师,祖籍黑龙江牡丹江。
|
||||
田野,29岁,吉林市化工企业实验员,籍贯吉林四平。
|
||||
于洋,50岁,大同能源企业矿山工程师,老家山西忻州。
|
||||
段明,35岁,包头稀土材料企业销售经理,祖籍内蒙古赤峰。
|
||||
康宁,31岁,天水农产品公司渠道专员,籍贯甘肃平凉。
|
||||
钱峰,43岁,吴忠食品企业财务经理,老家宁夏中卫。
|
||||
石磊,34岁,喀什贸易公司仓储主管,籍贯新疆伊宁。
|
||||
乔安,28岁,海东教育培训机构课程顾问,祖籍青海西宁。
|
||||
赖青,39岁,绍兴纺织企业生产计划员,籍贯浙江嘉兴。
|
||||
熊浩,46岁,常州工程机械公司售前经理,老家江苏镇江。
|
||||
庞宇,33岁,中山照明企业工业设计师,祖籍广东江门。
|
||||
纪辰,40岁,湛江水产企业品控经理,籍贯广东茂名。
|
||||
向晴,27岁,莆田鞋业公司视觉设计师,老家福建福州。
|
||||
杜航,35岁,九江港口企业调度员,祖籍江西景德镇。
|
||||
穆清,48岁,芜湖汽车企业总装车间主任,籍贯安徽马鞍山。
|
||||
童乐,26岁,保定新能源材料企业实验助理,老家河北唐山。
|
||||
毛宁,37岁,开封文创公司策展人,祖籍河南郑州。
|
||||
欧阳洁,32岁,潍坊农业装备公司行政主管,籍贯山东临沂。
|
||||
施展,45岁,威海海工企业项目负责人,老家山东青岛。
|
||||
严妍,29岁,鞍山钢铁企业环保专员,祖籍辽宁大连。
|
||||
祝凯,41岁,宜昌化工企业物流经理,籍贯湖北荆州。
|
||||
颜晨,30岁,咸阳食品公司品牌经理,老家陕西西安。
|
||||
舒曼,36岁,德阳装备制造企业采购工程师,祖籍四川成都。
|
||||
梅婷,28岁,常德农旅公司内容运营,籍贯湖南长沙。
|
||||
费翔,49岁,丽江旅行社线路设计师,老家云南昆明。
|
||||
毕然,34岁,安顺航空配套企业质量工程师,祖籍贵州贵阳。
|
||||
蓝心,31岁,柳州汽车公司成本分析师,籍贯广西南宁。
|
||||
左宁,42岁,牡丹江林产品公司销售总监,老家黑龙江哈尔滨。
|
||||
米兰,27岁,四平食品企业直播运营,祖籍吉林长春。
|
||||
卜凡,52岁,忻州煤化工企业设备顾问,籍贯山西太原。
|
||||
焦阳,38岁,赤峰农牧企业牧场经理,老家内蒙古呼和浩特。
|
||||
邢云,30岁,平凉制药企业研发助理,祖籍甘肃兰州。
|
||||
仲夏,35岁,中卫新能源企业运维主管,籍贯宁夏银川。
|
||||
欧文,44岁,伊宁跨境贸易公司商务经理,老家新疆乌鲁木齐。
|
||||
单晴,26岁,嘉兴服装企业买手助理,祖籍浙江杭州。
|
||||
甄浩,33岁,镇江材料企业工艺工程师,籍贯江苏南京。
|
||||
解语,29岁,江门摩托车企业市场专员,老家广东广州。
|
||||
卫东,47岁,茂名石化企业设备经理,祖籍广东深圳。
|
||||
游佳,31岁,景德镇陶瓷品牌设计师,籍贯江西南昌。
|
||||
宁川,40岁,马鞍山钢材企业计划经理,老家安徽合肥。
|
||||
房琪,28岁,唐山陶瓷公司电商客服主管,祖籍河北石家庄。
|
||||
艾明,36岁,临沂商贸企业物流调度,籍贯山东济南。
|
||||
楚然,32岁,荆州文化传媒公司编导,老家湖北武汉。
|
||||
褚航,39岁,江门智能装备公司售后经理,祖籍广东佛山。
|
||||
苗雨,25岁,镇江香醋品牌新媒体运营,籍贯江苏苏州。
|
||||
谷峰,46岁,长沙工程咨询公司项目总监,老家湖南湘潭。
|
||||
贝宁,34岁,宁波跨境电商企业采购经理,祖籍浙江温州。
|
||||
桑榆,37岁,厦门航运服务公司客服主管,籍贯福建漳州。
|
||||
路遥,43岁,重庆汽车公司供应链总监,老家四川南充。
|
||||
景甜,30岁,北京文化科技公司交互设计师,祖籍天津。
|
||||
辛宇,41岁,上海医疗信息企业解决方案架构师,籍贯江苏无锡。
|
||||
温岚,27岁,广州食品科技公司营养师,老家广东汕头。
|
||||
霍然,35岁,深圳机器人公司视觉算法工程师,祖籍湖北武汉。
|
||||
简宁,48岁,杭州数字经济园区招商经理,籍贯浙江金华。
|
||||
@@ -1,19 +1,16 @@
|
||||
package com.bruce.common.config;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@ConfigurationProperties(prefix = "common.attachment")
|
||||
public class AttachmentProperties {
|
||||
|
||||
private String basePath = "data/attachments";
|
||||
|
||||
public String getBasePath() {
|
||||
return basePath;
|
||||
}
|
||||
|
||||
public void setBasePath(String basePath) {
|
||||
this.basePath = basePath;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,28 @@
|
||||
package com.bruce.common.controller;
|
||||
|
||||
import com.bruce.common.entity.SysAttachment;
|
||||
import com.bruce.common.domain.entity.SysAttachment;
|
||||
import com.bruce.common.domain.model.RequestResult;
|
||||
import com.bruce.common.dto.request.SysAttachmentUploadRequest;
|
||||
import com.bruce.common.service.ISysAttachmentService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@Tag(name = "系统附件管理")
|
||||
@RestController
|
||||
@RequestMapping("/api/attachments")
|
||||
public class SysAttachmentController {
|
||||
|
||||
private final ISysAttachmentService sysAttachmentService;
|
||||
|
||||
public SysAttachmentController(ISysAttachmentService sysAttachmentService) {
|
||||
this.sysAttachmentService = sysAttachmentService;
|
||||
}
|
||||
@Autowired
|
||||
private ISysAttachmentService sysAttachmentService;
|
||||
|
||||
@Operation(summary = "上传附件")
|
||||
@PostMapping("/upload")
|
||||
public SysAttachment upload(@RequestParam("file") MultipartFile file,
|
||||
@RequestParam String sourceType,
|
||||
@RequestParam(required = false) Long sourceId) {
|
||||
return sysAttachmentService.upload(file, sourceType, sourceId);
|
||||
public RequestResult<SysAttachment> upload(@ModelAttribute SysAttachmentUploadRequest request) {
|
||||
return RequestResult.success(sysAttachmentService.upload(request));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
package com.bruce.common.controller;
|
||||
|
||||
import com.bruce.common.entity.SysEnum;
|
||||
import com.bruce.common.domain.entity.SysEnum;
|
||||
import com.bruce.common.domain.model.RequestResult;
|
||||
import com.bruce.common.dto.request.SysEnumQueryRequest;
|
||||
import com.bruce.common.dto.request.SysEnumSaveRequest;
|
||||
import com.bruce.common.service.ISysEnumService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
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;
|
||||
@@ -20,33 +23,30 @@ import java.util.List;
|
||||
@RequestMapping("/api/sys-enums")
|
||||
public class SysEnumController {
|
||||
|
||||
private final ISysEnumService sysEnumService;
|
||||
|
||||
public SysEnumController(ISysEnumService sysEnumService) {
|
||||
this.sysEnumService = sysEnumService;
|
||||
}
|
||||
@Autowired
|
||||
private ISysEnumService sysEnumService;
|
||||
|
||||
@Operation(summary = "查询全部系统枚举")
|
||||
@GetMapping
|
||||
public List<SysEnum> list() {
|
||||
return sysEnumService.list();
|
||||
public RequestResult<List<SysEnum>> list() {
|
||||
return RequestResult.success(sysEnumService.list());
|
||||
}
|
||||
|
||||
@Operation(summary = "根据模块和类型查询系统枚举")
|
||||
@GetMapping("/query")
|
||||
public List<SysEnum> queryByCatalogAndType(@RequestParam String catalog, @RequestParam String type) {
|
||||
return sysEnumService.listByCatalogAndType(catalog, type);
|
||||
@PostMapping("/query")
|
||||
public RequestResult<List<SysEnum>> queryByCatalogAndType(@RequestBody SysEnumQueryRequest request) {
|
||||
return RequestResult.success(sysEnumService.listByCatalogAndType(request));
|
||||
}
|
||||
|
||||
@Operation(summary = "新增或修改系统枚举")
|
||||
@PostMapping
|
||||
public boolean saveOrUpdate(@RequestBody SysEnum sysEnum) {
|
||||
return sysEnumService.saveOrUpdate(sysEnum);
|
||||
public RequestResult<Boolean> saveOrUpdate(@RequestBody SysEnumSaveRequest request) {
|
||||
return RequestResult.success(sysEnumService.saveOrUpdate(request));
|
||||
}
|
||||
|
||||
@Operation(summary = "删除系统枚举")
|
||||
@DeleteMapping("/{id}")
|
||||
public boolean deleteById(@PathVariable Long id) {
|
||||
return sysEnumService.removeById(id);
|
||||
public RequestResult<Boolean> deleteById(@PathVariable Long id) {
|
||||
return RequestResult.success(sysEnumService.removeById(id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package com.bruce.common.entity;
|
||||
package com.bruce.common.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.bruce.common.domain.model.BaseEntity;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.bruce.common.entity;
|
||||
package com.bruce.common.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.bruce.common.domain.model.BaseEntity;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.bruce.common.entity;
|
||||
package com.bruce.common.domain.model;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.bruce.common.domain.model;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class RequestResult<T> {
|
||||
|
||||
public static final String FAIL_CODE = "-1";
|
||||
|
||||
public static final String SUCCESS_CODE = "0";
|
||||
|
||||
@Schema(description = "错误消息")
|
||||
private String message;
|
||||
|
||||
@Schema(description = "消息代码")
|
||||
private String resultcode;
|
||||
|
||||
@Schema(description = "数据")
|
||||
private T data;
|
||||
|
||||
public RequestResult(T data) {
|
||||
this.resultcode = SUCCESS_CODE;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public static <T> RequestResult<T> success() {
|
||||
return build(SUCCESS_CODE, null, null);
|
||||
}
|
||||
|
||||
public static <T> RequestResult<T> success(T data) {
|
||||
return build(SUCCESS_CODE, null, data);
|
||||
}
|
||||
|
||||
public static <T> RequestResult<T> success(String message, T data) {
|
||||
return build(SUCCESS_CODE, message, data);
|
||||
}
|
||||
|
||||
public static <T> RequestResult<T> fail(String message) {
|
||||
return build(FAIL_CODE, message, null);
|
||||
}
|
||||
|
||||
public static <T> RequestResult<T> fail(String resultcode, String message) {
|
||||
return build(resultcode, message, null);
|
||||
}
|
||||
|
||||
private static <T> RequestResult<T> build(String resultcode, String message, T data) {
|
||||
RequestResult<T> result = new RequestResult<>();
|
||||
result.setResultcode(resultcode);
|
||||
result.setMessage(message);
|
||||
result.setData(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.bruce.common.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@Data
|
||||
@Schema(description = "附件上传请求")
|
||||
public class SysAttachmentUploadRequest {
|
||||
|
||||
@Schema(description = "上传文件")
|
||||
private MultipartFile file;
|
||||
|
||||
@Schema(description = "来源类型")
|
||||
private String sourceType;
|
||||
|
||||
@Schema(description = "来源业务ID")
|
||||
private Long sourceId;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.bruce.common.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "系统枚举查询请求")
|
||||
public class SysEnumQueryRequest {
|
||||
|
||||
@Schema(description = "模块目录")
|
||||
private String catalog;
|
||||
|
||||
@Schema(description = "枚举类型")
|
||||
private String type;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.bruce.common.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "系统枚举保存请求")
|
||||
public class SysEnumSaveRequest {
|
||||
|
||||
@Schema(description = "主键ID")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "模块目录")
|
||||
private String catalog;
|
||||
|
||||
@Schema(description = "枚举类型")
|
||||
private String type;
|
||||
|
||||
@Schema(description = "枚举名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "整型值")
|
||||
private Integer value;
|
||||
|
||||
@Schema(description = "字符串值")
|
||||
private String strvalue;
|
||||
|
||||
@Schema(description = "排序")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.bruce.common.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.bruce.common.entity.SysAttachment;
|
||||
import com.bruce.common.domain.entity.SysAttachment;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.bruce.common.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.bruce.common.entity.SysEnum;
|
||||
import com.bruce.common.domain.entity.SysEnum;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.bruce.common.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.bruce.common.entity.SysAttachment;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import com.bruce.common.domain.entity.SysAttachment;
|
||||
import com.bruce.common.dto.request.SysAttachmentUploadRequest;
|
||||
|
||||
public interface ISysAttachmentService extends IService<SysAttachment> {
|
||||
|
||||
SysAttachment upload(MultipartFile file, String sourceType, Long sourceId);
|
||||
SysAttachment upload(SysAttachmentUploadRequest request);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package com.bruce.common.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.bruce.common.entity.SysEnum;
|
||||
import com.bruce.common.domain.entity.SysEnum;
|
||||
import com.bruce.common.dto.request.SysEnumQueryRequest;
|
||||
import com.bruce.common.dto.request.SysEnumSaveRequest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ISysEnumService extends IService<SysEnum> {
|
||||
|
||||
List<SysEnum> listByCatalogAndType(String catalog, String type);
|
||||
List<SysEnum> listByCatalogAndType(SysEnumQueryRequest request);
|
||||
|
||||
boolean saveOrUpdate(SysEnumSaveRequest request);
|
||||
}
|
||||
|
||||
@@ -2,9 +2,11 @@ package com.bruce.common.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.bruce.common.config.AttachmentProperties;
|
||||
import com.bruce.common.entity.SysAttachment;
|
||||
import com.bruce.common.domain.entity.SysAttachment;
|
||||
import com.bruce.common.dto.request.SysAttachmentUploadRequest;
|
||||
import com.bruce.common.mapper.SysAttachmentMapper;
|
||||
import com.bruce.common.service.ISysAttachmentService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
@@ -22,17 +24,19 @@ public class SysAttachmentServiceImpl extends ServiceImpl<SysAttachmentMapper, S
|
||||
|
||||
private static final String STORAGE_TYPE_LOCAL = "LOCAL";
|
||||
|
||||
private final AttachmentProperties attachmentProperties;
|
||||
|
||||
public SysAttachmentServiceImpl(AttachmentProperties attachmentProperties) {
|
||||
this.attachmentProperties = attachmentProperties;
|
||||
}
|
||||
@Autowired
|
||||
private AttachmentProperties attachmentProperties;
|
||||
|
||||
@Override
|
||||
public SysAttachment upload(MultipartFile file, String sourceType, Long sourceId) {
|
||||
public SysAttachment upload(SysAttachmentUploadRequest request) {
|
||||
if (request == null) {
|
||||
throw new IllegalArgumentException("上传请求不能为空");
|
||||
}
|
||||
MultipartFile file = request.getFile();
|
||||
if (file == null || file.isEmpty()) {
|
||||
throw new IllegalArgumentException("上传文件不能为空");
|
||||
}
|
||||
String sourceType = request.getSourceType();
|
||||
if (!StringUtils.hasText(sourceType)) {
|
||||
throw new IllegalArgumentException("sourceType不能为空");
|
||||
}
|
||||
@@ -54,7 +58,7 @@ public class SysAttachmentServiceImpl extends ServiceImpl<SysAttachmentMapper, S
|
||||
|
||||
SysAttachment attachment = new SysAttachment();
|
||||
attachment.setSourceType(sourceType);
|
||||
attachment.setSourceId(sourceId);
|
||||
attachment.setSourceId(request.getSourceId());
|
||||
attachment.setOriginalName(originalName);
|
||||
attachment.setFileName(storedFileName);
|
||||
attachment.setFileSuffix(suffix);
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package com.bruce.common.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.bruce.common.entity.SysEnum;
|
||||
import com.bruce.common.domain.entity.SysEnum;
|
||||
import com.bruce.common.dto.request.SysEnumQueryRequest;
|
||||
import com.bruce.common.dto.request.SysEnumSaveRequest;
|
||||
import com.bruce.common.mapper.SysEnumMapper;
|
||||
import com.bruce.common.service.ISysEnumService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -12,11 +15,32 @@ import java.util.List;
|
||||
public class SysEnumServiceImpl extends ServiceImpl<SysEnumMapper, SysEnum> implements ISysEnumService {
|
||||
|
||||
@Override
|
||||
public List<SysEnum> listByCatalogAndType(String catalog, String type) {
|
||||
public List<SysEnum> listByCatalogAndType(SysEnumQueryRequest request) {
|
||||
if (request == null) {
|
||||
throw new IllegalArgumentException("查询请求不能为空");
|
||||
}
|
||||
return lambdaQuery()
|
||||
.eq(SysEnum::getCatalog, catalog)
|
||||
.eq(SysEnum::getType, type)
|
||||
.eq(StringUtils.hasText(request.getCatalog()), SysEnum::getCatalog, request.getCatalog())
|
||||
.eq(StringUtils.hasText(request.getType()), SysEnum::getType, request.getType())
|
||||
.orderByAsc(SysEnum::getSort)
|
||||
.list();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveOrUpdate(SysEnumSaveRequest request) {
|
||||
if (request == null) {
|
||||
throw new IllegalArgumentException("保存请求不能为空");
|
||||
}
|
||||
|
||||
SysEnum sysEnum = new SysEnum();
|
||||
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());
|
||||
return super.saveOrUpdate(sysEnum);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
package com.bruce.rag.controller;
|
||||
|
||||
import com.bruce.common.domain.model.RequestResult;
|
||||
import com.bruce.rag.dto.request.RagDocumentQueryRequest;
|
||||
import com.bruce.rag.entity.RagDocument;
|
||||
import com.bruce.rag.service.IRagDocumentService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
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.RestController;
|
||||
|
||||
@@ -15,15 +20,18 @@ import java.util.List;
|
||||
@RequestMapping("/api/rag/documents")
|
||||
public class RagDocumentController {
|
||||
|
||||
private final IRagDocumentService ragDocumentService;
|
||||
|
||||
public RagDocumentController(IRagDocumentService ragDocumentService) {
|
||||
this.ragDocumentService = ragDocumentService;
|
||||
}
|
||||
@Autowired
|
||||
private IRagDocumentService ragDocumentService;
|
||||
|
||||
@Operation(summary = "查询全部知识库文档")
|
||||
@GetMapping
|
||||
public List<RagDocument> list() {
|
||||
return ragDocumentService.list();
|
||||
public RequestResult<List<RagDocument>> list() {
|
||||
return RequestResult.success(ragDocumentService.list());
|
||||
}
|
||||
|
||||
@Operation(summary = "按条件查询知识库文档")
|
||||
@PostMapping("/query")
|
||||
public RequestResult<List<RagDocument>> query(@RequestBody RagDocumentQueryRequest request) {
|
||||
return RequestResult.success(ragDocumentService.query(request));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
package com.bruce.rag.controller;
|
||||
|
||||
import com.bruce.common.domain.model.RequestResult;
|
||||
import com.bruce.rag.dto.request.RagStoreQueryRequest;
|
||||
import com.bruce.rag.entity.RagStore;
|
||||
import com.bruce.rag.service.IRagStoreService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
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.RestController;
|
||||
|
||||
@@ -15,15 +20,18 @@ import java.util.List;
|
||||
@RequestMapping("/api/rag/stores")
|
||||
public class RagStoreController {
|
||||
|
||||
private final IRagStoreService ragStoreService;
|
||||
|
||||
public RagStoreController(IRagStoreService ragStoreService) {
|
||||
this.ragStoreService = ragStoreService;
|
||||
}
|
||||
@Autowired
|
||||
private IRagStoreService ragStoreService;
|
||||
|
||||
@Operation(summary = "查询全部知识库")
|
||||
@GetMapping
|
||||
public List<RagStore> list() {
|
||||
return ragStoreService.list();
|
||||
public RequestResult<List<RagStore>> list() {
|
||||
return RequestResult.success(ragStoreService.list());
|
||||
}
|
||||
|
||||
@Operation(summary = "按条件查询知识库")
|
||||
@PostMapping("/query")
|
||||
public RequestResult<List<RagStore>> query(@RequestBody RagStoreQueryRequest request) {
|
||||
return RequestResult.success(ragStoreService.query(request));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.bruce.rag.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "RAG知识库文档查询请求")
|
||||
public class RagDocumentQueryRequest {
|
||||
|
||||
@Schema(description = "知识库ID")
|
||||
private Long storeId;
|
||||
|
||||
@Schema(description = "附件ID")
|
||||
private Long attachmentId;
|
||||
|
||||
@Schema(description = "解析状态")
|
||||
private String parseStatus;
|
||||
|
||||
@Schema(description = "索引状态")
|
||||
private String indexStatus;
|
||||
|
||||
@Schema(description = "是否启用")
|
||||
private Boolean enabled;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.bruce.rag.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "RAG知识库查询请求")
|
||||
public class RagStoreQueryRequest {
|
||||
|
||||
@Schema(description = "知识库编码")
|
||||
private String storeCode;
|
||||
|
||||
@Schema(description = "知识库名称")
|
||||
private String storeName;
|
||||
|
||||
@Schema(description = "状态")
|
||||
private String status;
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package com.bruce.rag.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.bruce.common.entity.BaseEntity;
|
||||
import com.bruce.common.domain.model.BaseEntity;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.bruce.rag.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.bruce.common.entity.BaseEntity;
|
||||
import com.bruce.common.domain.model.BaseEntity;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
package com.bruce.rag.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.bruce.rag.dto.request.RagDocumentQueryRequest;
|
||||
import com.bruce.rag.entity.RagDocument;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IRagDocumentService extends IService<RagDocument> {
|
||||
|
||||
List<RagDocument> query(RagDocumentQueryRequest request);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
package com.bruce.rag.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.bruce.rag.dto.request.RagStoreQueryRequest;
|
||||
import com.bruce.rag.entity.RagStore;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IRagStoreService extends IService<RagStore> {
|
||||
|
||||
List<RagStore> query(RagStoreQueryRequest request);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,29 @@
|
||||
package com.bruce.rag.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.bruce.rag.dto.request.RagDocumentQueryRequest;
|
||||
import com.bruce.rag.entity.RagDocument;
|
||||
import com.bruce.rag.mapper.RagDocumentMapper;
|
||||
import com.bruce.rag.service.IRagDocumentService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class RagDocumentServiceImpl extends ServiceImpl<RagDocumentMapper, RagDocument> implements IRagDocumentService {
|
||||
|
||||
@Override
|
||||
public List<RagDocument> query(RagDocumentQueryRequest request) {
|
||||
if (request == null) {
|
||||
throw new IllegalArgumentException("查询请求不能为空");
|
||||
}
|
||||
return lambdaQuery()
|
||||
.eq(request.getStoreId() != null, RagDocument::getStoreId, request.getStoreId())
|
||||
.eq(request.getAttachmentId() != null, RagDocument::getAttachmentId, request.getAttachmentId())
|
||||
.eq(request.getParseStatus() != null, RagDocument::getParseStatus, request.getParseStatus())
|
||||
.eq(request.getIndexStatus() != null, RagDocument::getIndexStatus, request.getIndexStatus())
|
||||
.eq(request.getEnabled() != null, RagDocument::getEnabled, request.getEnabled())
|
||||
.orderByDesc(RagDocument::getId)
|
||||
.list();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,28 @@
|
||||
package com.bruce.rag.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.bruce.rag.dto.request.RagStoreQueryRequest;
|
||||
import com.bruce.rag.entity.RagStore;
|
||||
import com.bruce.rag.mapper.RagStoreMapper;
|
||||
import com.bruce.rag.service.IRagStoreService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class RagStoreServiceImpl extends ServiceImpl<RagStoreMapper, RagStore> implements IRagStoreService {
|
||||
|
||||
@Override
|
||||
public List<RagStore> query(RagStoreQueryRequest request) {
|
||||
if (request == null) {
|
||||
throw new IllegalArgumentException("查询请求不能为空");
|
||||
}
|
||||
return lambdaQuery()
|
||||
.eq(StringUtils.hasText(request.getStoreCode()), RagStore::getStoreCode, request.getStoreCode())
|
||||
.like(StringUtils.hasText(request.getStoreName()), RagStore::getStoreName, request.getStoreName())
|
||||
.eq(StringUtils.hasText(request.getStatus()), RagStore::getStatus, request.getStatus())
|
||||
.orderByAsc(RagStore::getStoreCode)
|
||||
.list();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,16 +3,19 @@ package com.bruce.common.attachment;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.bruce.common.config.AttachmentProperties;
|
||||
import com.bruce.common.controller.SysAttachmentController;
|
||||
import com.bruce.common.entity.SysAttachment;
|
||||
import com.bruce.common.domain.model.RequestResult;
|
||||
import com.bruce.common.dto.request.SysAttachmentUploadRequest;
|
||||
import com.bruce.common.domain.entity.SysAttachment;
|
||||
import com.bruce.common.mapper.SysAttachmentMapper;
|
||||
import com.bruce.common.service.ISysAttachmentService;
|
||||
import com.bruce.common.service.impl.SysAttachmentServiceImpl;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@@ -31,18 +34,27 @@ class SysAttachmentComponentStructureTests {
|
||||
void sysAttachmentShouldExposeUploadMethods() throws NoSuchMethodException {
|
||||
Method serviceMethod = ISysAttachmentService.class.getMethod(
|
||||
"upload",
|
||||
MultipartFile.class,
|
||||
String.class,
|
||||
Long.class
|
||||
SysAttachmentUploadRequest.class
|
||||
);
|
||||
Method controllerMethod = SysAttachmentController.class.getMethod(
|
||||
"upload",
|
||||
MultipartFile.class,
|
||||
String.class,
|
||||
Long.class
|
||||
SysAttachmentUploadRequest.class
|
||||
);
|
||||
|
||||
assertNotNull(serviceMethod);
|
||||
assertNotNull(controllerMethod);
|
||||
assertEquals(SysAttachment.class, serviceMethod.getReturnType());
|
||||
assertEquals(RequestResult.class, controllerMethod.getReturnType());
|
||||
}
|
||||
|
||||
@Test
|
||||
void attachmentPropertiesShouldExposeBindableBasePath() {
|
||||
AttachmentProperties properties = new AttachmentProperties();
|
||||
|
||||
assertEquals("data/attachments", properties.getBasePath());
|
||||
|
||||
properties.setBasePath("custom/attachments");
|
||||
|
||||
assertEquals("custom/attachments", properties.getBasePath());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,9 @@ package com.bruce.common.entity;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.Version;
|
||||
import com.bruce.common.domain.model.BaseEntity;
|
||||
import com.bruce.common.domain.entity.SysAttachment;
|
||||
import com.bruce.common.domain.entity.SysEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
||||
@@ -4,14 +4,19 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.bruce.common.controller.SysEnumController;
|
||||
import com.bruce.common.entity.SysEnum;
|
||||
import com.bruce.common.domain.entity.SysEnum;
|
||||
import com.bruce.common.domain.model.RequestResult;
|
||||
import com.bruce.common.dto.request.SysEnumQueryRequest;
|
||||
import com.bruce.common.dto.request.SysEnumSaveRequest;
|
||||
import com.bruce.common.mapper.SysEnumMapper;
|
||||
import com.bruce.common.service.ISysEnumService;
|
||||
import com.bruce.common.service.impl.SysEnumServiceImpl;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@@ -28,20 +33,30 @@ class SysEnumComponentStructureTests {
|
||||
|
||||
@Test
|
||||
void sysEnumShouldExposeQueryMethodForCatalogAndType() throws NoSuchMethodException {
|
||||
Method serviceMethod = ISysEnumService.class.getMethod("listByCatalogAndType", String.class, String.class);
|
||||
Method controllerMethod = SysEnumController.class.getMethod("queryByCatalogAndType", String.class, String.class);
|
||||
Method serviceMethod = ISysEnumService.class.getMethod("listByCatalogAndType", SysEnumQueryRequest.class);
|
||||
Method controllerMethod = SysEnumController.class.getMethod("queryByCatalogAndType", SysEnumQueryRequest.class);
|
||||
|
||||
assertNotNull(serviceMethod);
|
||||
assertNotNull(controllerMethod);
|
||||
assertEquals(List.class, serviceMethod.getReturnType());
|
||||
assertEquals(RequestResult.class, controllerMethod.getReturnType());
|
||||
}
|
||||
|
||||
@Test
|
||||
void sysEnumShouldExposeSaveOrUpdateAndDeleteInterfaces() throws NoSuchMethodException {
|
||||
Method saveOrUpdateMethod = SysEnumController.class.getMethod("saveOrUpdate", SysEnum.class);
|
||||
Method saveOrUpdateMethod = ISysEnumService.class.getMethod("saveOrUpdate", SysEnumSaveRequest.class);
|
||||
Method controllerSaveOrUpdateMethod = SysEnumController.class.getMethod("saveOrUpdate", SysEnumSaveRequest.class);
|
||||
Method deleteMethod = SysEnumController.class.getMethod("deleteById", Long.class);
|
||||
Method listMethod = SysEnumController.class.getMethod("list");
|
||||
|
||||
assertNotNull(saveOrUpdateMethod);
|
||||
assertNotNull(controllerSaveOrUpdateMethod);
|
||||
assertNotNull(deleteMethod);
|
||||
assertNotNull(listMethod);
|
||||
assertEquals(boolean.class, saveOrUpdateMethod.getReturnType());
|
||||
assertEquals(RequestResult.class, controllerSaveOrUpdateMethod.getReturnType());
|
||||
assertEquals(RequestResult.class, deleteMethod.getReturnType());
|
||||
assertEquals(RequestResult.class, listMethod.getReturnType());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.bruce.common.enumconfig;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.bruce.common.entity.SysEnum;
|
||||
import com.bruce.common.domain.entity.SysEnum;
|
||||
import com.bruce.common.enums.CommonStatusEnum;
|
||||
import com.bruce.common.enums.EnableStatusEnum;
|
||||
import com.bruce.common.service.ISysEnumService;
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.bruce.common.model;
|
||||
|
||||
import com.bruce.common.domain.model.RequestResult;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
class RequestResultStructureTests {
|
||||
|
||||
@Test
|
||||
void dataConstructorShouldDefaultToSuccessCode() {
|
||||
RequestResult<String> result = new RequestResult<>("payload");
|
||||
|
||||
assertEquals(RequestResult.SUCCESS_CODE, result.getResultcode());
|
||||
assertNull(result.getMessage());
|
||||
assertEquals("payload", result.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
void successFactoryShouldBuildSuccessfulResult() {
|
||||
RequestResult<String> result = RequestResult.success("payload");
|
||||
|
||||
assertEquals(RequestResult.SUCCESS_CODE, result.getResultcode());
|
||||
assertNull(result.getMessage());
|
||||
assertEquals("payload", result.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
void successFactoryWithMessageShouldKeepCustomMessage() {
|
||||
RequestResult<String> result = RequestResult.success("操作成功", "payload");
|
||||
|
||||
assertEquals(RequestResult.SUCCESS_CODE, result.getResultcode());
|
||||
assertEquals("操作成功", result.getMessage());
|
||||
assertEquals("payload", result.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
void failFactoryShouldBuildFailureResult() {
|
||||
RequestResult<Void> result = RequestResult.fail("校验失败");
|
||||
|
||||
assertEquals(RequestResult.FAIL_CODE, result.getResultcode());
|
||||
assertEquals("校验失败", result.getMessage());
|
||||
assertNull(result.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
void failFactoryWithCustomCodeShouldKeepCustomCode() {
|
||||
RequestResult<Void> result = RequestResult.fail("VALIDATE_ERROR", "校验失败");
|
||||
|
||||
assertEquals("VALIDATE_ERROR", result.getResultcode());
|
||||
assertEquals("校验失败", result.getMessage());
|
||||
assertNull(result.getData());
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,12 @@ package com.bruce.rag;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.bruce.common.domain.model.RequestResult;
|
||||
import com.bruce.rag.constant.RagSystemConstants;
|
||||
import com.bruce.rag.controller.RagDocumentController;
|
||||
import com.bruce.rag.controller.RagStoreController;
|
||||
import com.bruce.rag.dto.request.RagDocumentQueryRequest;
|
||||
import com.bruce.rag.dto.request.RagStoreQueryRequest;
|
||||
import com.bruce.rag.entity.RagDocument;
|
||||
import com.bruce.rag.entity.RagStore;
|
||||
import com.bruce.rag.mapper.RagDocumentMapper;
|
||||
@@ -17,6 +20,8 @@ import com.bruce.rag.service.impl.RagStoreServiceImpl;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
@@ -33,6 +38,25 @@ class RagComponentStructureTests {
|
||||
assertTrue(ServiceImpl.class.isAssignableFrom(RagDocumentServiceImpl.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void ragControllersShouldExposeRequestResultAndQueryDtoMethods() throws NoSuchMethodException {
|
||||
Method storeListMethod = RagStoreController.class.getMethod("list");
|
||||
Method storeQueryMethod = RagStoreController.class.getMethod("query", RagStoreQueryRequest.class);
|
||||
Method storeServiceQueryMethod = IRagStoreService.class.getMethod("query", RagStoreQueryRequest.class);
|
||||
|
||||
Method documentListMethod = RagDocumentController.class.getMethod("list");
|
||||
Method documentQueryMethod = RagDocumentController.class.getMethod("query", RagDocumentQueryRequest.class);
|
||||
Method documentServiceQueryMethod = IRagDocumentService.class.getMethod("query", RagDocumentQueryRequest.class);
|
||||
|
||||
assertEquals(RequestResult.class, storeListMethod.getReturnType());
|
||||
assertEquals(RequestResult.class, storeQueryMethod.getReturnType());
|
||||
assertEquals(List.class, storeServiceQueryMethod.getReturnType());
|
||||
|
||||
assertEquals(RequestResult.class, documentListMethod.getReturnType());
|
||||
assertEquals(RequestResult.class, documentQueryMethod.getReturnType());
|
||||
assertEquals(List.class, documentServiceQueryMethod.getReturnType());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ragSourceTypesAndDocumentRelationShouldExist() throws NoSuchFieldException {
|
||||
Field storeIdField = RagDocument.class.getDeclaredField("storeId");
|
||||
|
||||
Reference in New Issue
Block a user