refactor: 移除java项目,改用hono + vercel function实现后端 (#1)

Co-authored-by: gin <gin-18@qq.com>
Co-authored-by: gin <dengxinmin@owlscm.com>
Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
2026-06-17 20:58:00 +08:00
parent 2757a4fb49
commit 1c3f8b39a3
605 changed files with 13301 additions and 31274 deletions
+83 -105
View File
@@ -1,155 +1,133 @@
# 后端业务功能开发规范
本文是本项目后端新增或修改业务功能时的权威规范。开发后台管理端业务模块时,必须优先遵循本项目现有架构,而不是套用通用 Spring Boot 三层模板
本文是本项目 Hono 后端新增或修改业务功能时的权威规范。后端代码位于 `backend`,基于 Hono、Prisma、Redis 和统一响应/错误协议实现
## 架构原则
后端业务代码按 `Controller -> ApplicationService -> Model -> db Service/Mapper` 组织。
后端业务代码按 `Route -> Feature Service -> Prisma/Redis/shared Service` 组织。
优先参考这些现有模块:
- `backend/agileboot-domain/src/main/java/com/agileboot/domain/system/post`
- `backend/agileboot-domain/src/main/java/com/agileboot/domain/system/user`
- `backend/agileboot-domain/src/main/java/com/agileboot/domain/system/role`
- `backend/agileboot-domain/src/main/java/com/agileboot/domain/system/notice`
- `backend/agileboot-admin/src/main/java/com/agileboot/admin/controller/system`
- `backend/src/routes/modules/system-user.ts`
- `backend/src/routes/modules/system-role.ts`
- `backend/src/routes/modules/system-menu.ts`
- `backend/src/routes/modules/system-notice.ts`
- `backend/src/features/system/system.service.ts`
- `backend/src/features/collaboration/collaboration.service.ts`
不要让 Controller 直接调用 Mapper,不要把业务规则写在 Controller,也不要直接把 Entity 或 DO 返回给前端
Route 只负责 HTTP 层编排,不直接堆业务规则。复杂查询或业务流程放到 `features/<domain>` 下的 service 中。不要把 Prisma 原始记录直接作为跨模块业务契约扩散,面向前端的响应应在 service 中整理为稳定 DTO 形态
## 开发准备
开发新后端业务功能前,应先确认需求所属领域、涉及的数据表、接口范围、权限标识和相似模块。本文是后端业务功能开发的统一规范。
开发新后端业务功能前,应先确认需求所属领域、涉及的数据表、接口范围、权限标识和相似模块。
开始编码前必须先做一次相似模块调研,确认目标功能最接近 `post``user``role``notice` 还是其他模块,再按相似模块的命名、包路径、注解、分页、异常、权限、日志和测试风格实现。
开始编码前必须先做一次相似模块调研,确认目标功能最接近 `user``role``menu``notice``config``collaboration` 还是其他模块,再按相似模块的命名、路由、分页、异常、权限、导出和测试风格实现。
## 目录结构
后台管理端入口放在 `agileboot-admin`
路由入口放在
```text
backend/agileboot-admin/src/main/java/com/agileboot/admin/controller/<area>/
└── XxxController.java
backend/src/routes/modules/<module>.ts
```
业务代码放在 `agileboot-domain`
业务代码放在:
```text
backend/agileboot-domain/src/main/java/com/agileboot/domain/<area>/xxx/
├── XxxApplicationService.java
├── command/
│ ├── AddXxxCommand.java
│ └── UpdateXxxCommand.java
├── query/
│ └── XxxQuery.java
├── dto/
│ ├── XxxDTO.java
│ └── XxxDetailDTO.java
├── model/
│ ├── XxxModel.java
│ └── XxxModelFactory.java
└── db/
├── XxxEntity.java
├── XxxDO.java
├── XxxMapper.java
├── XxxService.java
└── XxxServiceImpl.java
backend/src/features/<domain>/
├── <domain>.service.ts
├── <domain>.schemas.ts
└── public-config.ts
```
`XxxDO` 是可选文件。只有当查询结果包含 join、聚合、报表字段或其他不完全匹配 Entity 的字段时才创建。
共享能力放在:
如果业务属于已有领域,例如 `system`,放到现有领域下;如果是独立业务领域,创建新的 `<area>` 包名,例如 `business`
```text
backend/src/shared/
├── auth/
├── cache/
├── config/
├── db/
├── hono/
└── http/
```
数据库 schema 维护在:
```text
backend/prisma/schema.prisma
```
初始化 SQL 维护在:
```text
backend/sql/init.sql
```
## 各层职责
Controller 只负责 HTTP 层:
Route 负责 HTTP 层:
- 声明 `@RestController``@RequestMapping``@Validated`、Swagger 注解
- 接收 `Command``Query`、路径参数和请求参数
- 使用 `@PreAuthorize("@permission.has('xxx:yyy:zzz')")` 做权限控制
- 对新增、修改、删除、导出等操作使用 `@AccessLog`
- `XxxApplicationService`
- 使用 `ResponseDTO.ok(...)` 返回结果。
- 声明 Hono 路由和 HTTP 方法
- 使用 `requireAuth``requirePermission` 做认证和权限控制
- 使用 `zValidator` 校验请求体或查询参数
- 调用 Feature Service
- 使`ok()``page()` 或下载响应工具返回统一响应
ApplicationService 负责编排业务用例:
Feature Service 负责编排业务用例:
- 分页查询、详情查询、导出查询、新增、修改、删除。
- 创建或加载 `XxxModel`
- 调用领域模型完成业务校验和状态变化
- 调用 `db` 包中的 Service 完成查询和持久化
- 将 Entity 或 DO 转换为 DTO
- 分页结果使用 `PageDTO<T>`
- 处理业务校验和状态变化
- 调用 Prisma、Redis 或 shared service 完成持久化和缓存操作
- 将数据库记录转换为前端响应对象
- 业务失败时抛出 `ApiError`,不要返回布尔值让 Route 解释
Model 承载业务规则
Shared 层只放跨模块复用能力
- `AddXxxCommand``UpdateXxxCommand` 加载字段
- 封装唯一性校验、状态校验、是否允许删除、业务状态流转等规则
- 需要数据库判断时,通过构造注入的 `XxxService` 查询
- 业务失败时抛出 `ApiException`,不要返回布尔值让上层解释
ModelFactory 负责创建和加载模型:
- `create()` 返回带有依赖的空模型。
- `loadById(id)` 从数据库加载 Entity,并包装为 Model。
- 如果未找到必要数据,按项目现有异常风格处理。
db 包只负责持久化:
- `XxxEntity` 映射数据库表。
- `XxxDO` 承载复杂查询结果,例如关联表字段、统计字段、报表字段。
- `XxxMapper` 继承 MyBatis Plus `BaseMapper<XxxEntity>`
- `XxxService` 继承 `IService<XxxEntity>`,声明复用型查询方法。
- `XxxServiceImpl` 继承 `ServiceImpl<XxxMapper, XxxEntity>`,实现唯一性检查、关联检查、复杂查询等。
- 复杂 SQL 放到 mapper XML,简单条件优先使用 MyBatis Plus wrapper。
`XxxDO` 属于数据库查询结果对象,不是前端响应对象,也不是请求对象。Mapper 或 Service 可以返回 `XxxDO`,但 ApplicationService 必须转换为 `XxxDTO` 后再交给 Controller 返回。
Command、Query、DTO 的边界:
- `Command` 用于新增、修改等写请求,不要直接接收 Entity。
- `Query` 用于列表、搜索、分页条件,并提供 `toPage()``toQueryWrapper()` 等项目已有风格的方法。
- `DTO` 用于响应前端,不要直接暴露 Entity 或 DO。
- 批量删除优先使用 `BulkOperationCommand<Long>`
- 认证、权限、会话和当前用户
- Prisma 和 Redis 客户端
- 统一响应、错误处理、下载、Excel
- 菜单、配置等跨模块辅助逻辑
## 开发流程
1. 检索并阅读一个最相似的现有模块,确认命名、包路径、注解、分页、异常、测试风格。
2. 确认数据库表结构,保持主键、软删除、审计字段、字段命名与现有表一致
3. 创建 `db` 包:`Entity``Mapper``Service``ServiceImpl`;如查询结果包含关联表字段、统计字段或报表字段,再增加 `XxxDO`
4. 创建 API 契约:`AddXxxCommand``UpdateXxxCommand``XxxQuery``XxxDTO`,需要详情时添加 `XxxDetailDTO`
5. 创建 `XxxModel``XxxModelFactory`,把业务校验放进 Model
6. 创建 `XxxApplicationService`,编排查询、新增、修改、删除、导出等用例
7. 创建 `XxxController`,加路由、权限、操作日志和统一响应
8. 如功能出现在后台菜单,补充权限标识、菜单 SQL 或清楚说明需要配置的权限码
9. 增加聚焦测试,至少覆盖核心业务校验和主要用例。
10. 运行相关 Maven 测试或编译检查;如果不能运行,说明原因。
开发过程中如果发现本文未覆盖的模式,优先参考现有模块,而不是引入新的架构风格。确实需要新增模式时,应先更新本文,再按新规范实现。
1. 检索并阅读一个最相似的 Hono 模块,确认命名、路由、分页、异常、权限和导出风格。
2. 确认数据库表结构,必要时更新 `prisma/schema.prisma` 和初始化 SQL
3. `features/<domain>` 中实现业务函数,保持函数职责单一
4. `routes/modules/<module>.ts` 中添加路由,接入认证、权限、校验和统一响应
5. 如功能出现在后台菜单,补充权限标识和 `backend/sql/init.sql` 菜单数据
6. 如需要字典数据,优先使用 `features/system/system-dictionary.ts` 中的枚举式配置,不要默认创建字典管理表
7. 增加聚焦测试或至少运行 TypeScript 检查;高风险改动应补充接口级验证
8. 运行 `pnpm --dir backend typecheck`
## 权限、日志和错误
权限码必须和前端菜单或按钮权限保持一致,格式参考现有系统功能:
```java
@PreAuthorize("@permission.has('system:post:add')")
```ts
requirePermission("system:user:list")
```
操作日志按业务动作选择类型
业务错误使用 `ApiError``ErrorCodes`
```java
@AccessLog(title = "业务名称", businessType = BusinessTypeEnum.ADD)
@AccessLog(title = "业务名称", businessType = BusinessTypeEnum.MODIFY)
@AccessLog(title = "业务名称", businessType = BusinessTypeEnum.DELETE)
@AccessLog(title = "业务名称", businessType = BusinessTypeEnum.EXPORT)
```ts
throw new ApiError(ErrorCodes.failed, "操作失败");
```
业务错误使用 `ApiException``ErrorCode.Business`。新增业务错误时,按现有错误码结构补充枚举或常量,不要在 Controller 中拼接错误响应
接口返回使用统一响应
```ts
return c.json(ok(result));
return c.json(page(result.records, result.total));
```
导出接口优先使用 `createWorkbookResponse()`
## 验收清单
- 新代码目录结构与相似模块一致
- Controller 只有 HTTP 编排逻辑
- 业务校验位于 Model 或 ApplicationService,不散落在前端或 Controller
- 查询返回 DTO,分页返回 `PageDTO<T>`
- 写请求使用 Command
- 复杂查询结果对象使用 DO,并在 ApplicationService 转换为 DTO
- 权限、日志、异常、测试和现有项目风格一致。
- Route 只有 HTTP 编排逻辑
- 业务校验位于 Feature Service,不散落在前端或 Route
- 查询返回稳定 DTO,分页返回统一 `page()` 响应
- 写请求使用 zod schema 校验
- 权限码、菜单 SQL、前端 API 调用保持一致
- `pnpm --dir backend typecheck` 通过