Files
lpt-be/AGENTS.md

177 lines
7.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# LPT 后端服务(lpt-be
> Java Spring Boot 后端,提供 REST API、数据库访问、认证鉴权。
## Git 提交规范
- 提交信息必须简短且使用中文,不要使用英文长句。
- 格式:`类型: 简述`,例如 `feat: 新增用户登录接口``fix: 修复多租户拦截器空指针``docs: 补充API文档`
## 技术栈
- Java 17
- 框架:Spring Boot 3.2.5
- ORMMyBatis-Plus 3.5.5
- 数据库:MySQL 8.0
- 认证:Sa-Token 1.38.0Cookie 名 `satoken`
- 数据库迁移:Flyway
- 对象映射:MapStruct 1.5.5
- 连接池:Druid 1.2.8
- 密码加密:jBCrypt 0.4
- API 文档:Knife4j (OpenAPI 3)
- Lombok@Data, @Slf4j
## 项目结构
```text
lpt-be/src/main/java/com/guo/learningprogresstracker/
├── controller/ → REST 接口层
├── service/ → 业务逻辑层(接口 + impl)
├── mapper/ → MyBatis-Plus BaseMapper
├── entity/ → 数据库实体
├── dto/ → 请求/响应 DTO
├── config/ → Spring 配置
├── common/ → 全局异常处理、Ops
├── mapStruct/ → MapStruct Converter
├── enums/ → 枚举类
├── exception/ → 自定义异常
└── utils/ → 工具类
```
- controller 只做参数校验和路由。
- service/impl 承载业务逻辑。
- mapper 使用 MyBatis-Plus BaseMapper。
- entity 使用 `@TableName``@TableField` 映射数据库字段。
- mapStruct 承载 DTO 转换。
## API 端点总览(32 个)
| 模块 | 端点数 | 端点 |
|------|--------|------|
| 学习会话 | 9 | `POST /study-sessions/{taskNum}/start`, `PUT .../pause`, `PUT .../resume`, `PUT .../end`, `POST .../fragments`, `GET .../history`, `PUT .../expectation`, `GET .../expectation`, `GET .../report-draft` |
| 标准思维导图 | 6 | `GET /review/standard-mind-map/{taskNum}`, `POST .../regenerate`, `PUT ...`, `POST .../recall`, `POST .../find-node`, `GET .../recall-records` |
| 复习模块 | 6 | `GET /review/feed`, `GET /review/task/{taskNum}`, `GET /review/report/{id}`, `GET /review/fragment/{id}`, `GET /review/standard-mind-map/recall-records/{recordId}`, `GET /review/tasks` |
| 任务管理 | 6 | `GET /tasks`, `POST /tasks`, `PUT /tasks/{taskNum}`, `DELETE /tasks/{taskNum}`, `GET /tasks/priority-weights`, `PUT /tasks/priority-weights` |
| 应用场景 | 4 | `GET/POST/PUT/DELETE /tasks/{taskNum}/applications[/{id}]` |
| 工具 | 1 | `GET /utils/fetch-title?url=...` |
## 数据库(10 个核心表)
`users`, `tasks`, `study_sessions`, `study_reports`, `study_report_fragments`, `study_expectations`, `task_applications`, `review_standard_mind_maps`, `review_recall_records`, `user_priority_weights`
## 关键机制
- 多租户隔离:`TenantLineInnerInterceptor` 自动注入 `WHERE created_by = #{当前用户}`,排除 `user`/`flyway_schema_history` 表。
- 自动填充:`MetaObjectHandler` 自动填充 `created_by`/`updated_by`/`created_time`/`updated_time`
- 认证鉴权:Sa-Token 拦截 `/**` 排除 `/login`Cookie `satoken`,支持 `@SaCheckPermission` 注解式权限。
- 响应格式:`CommonResult<T>` 统一包装 `{ code, message, data }`
- CORS:可配置,local profile 允许所有来源。
## 响应规范
- 成功:`CommonResult.success(data)`code=200。
- 业务错误:`CommonResult.error(msg)`code=400HTTP 200。
- 未登录:`GlobalExceptionHandler.handleNotLogin()`HTTP 401 + code=401。
- 参数校验失败:`MethodArgumentNotValidException`code=400。
- 用户可见错误文案必须口语化、可理解,避免“无法生成”“不存在”等技术化表述;技术细节写入日志。例如无学习报告时应提示“这个任务还没开始学习哦,学习后产生学习报告后再来吧”,而不是“没有学习报告,无法生成思维导图”。
## DTO 转换
- MapStruct 编译期生成 `*ConvertImpl.java`,同名属性自动映射。
- 默认 `unmappedTargetPolicy = IGNORE`
- 自定义映射使用 `@Mapping(source, target)`
- 增删 DTO 字段后必须重新编译,否则生成代码不含新字段。
## CORS
-`CorsProperties` 读取各 profile 的 `cors.allowed-origins`
- `allowed-origins: "*"` 时自动切换为 `allowedOriginPatterns("*")`,兼容 `allowCredentials`
## 标题抓取
- `TitleFetcher`:静态工具类,支持 HTTP→HTTPS 重定向和宽松 SSL。
- `UtilsController``GET /utils/fetch-title?url=...` 代理端点。
## 运行环境
- 默认 profilelocal`application.yml``spring.profiles.active: local`)。
- 编译命令:`mvn clean compile -DskipTests`,需要 JDK 17。
## 配置文件
| Profile | 文件 |
|---------|------|
| local | `application-local.yml` |
| dev | `application-dev.yml` |
| uat | `application-uat.yml` |
| prod | `application-prod.yml` |
| 公共 | `application.yml` |
## AI 服务依赖
- 配置:`lpt.ai-service.url=http://localhost:5199`
- 超时:600s
- AI 服务不可用时自动降级到内置规则引擎(`BuiltinMindMapGenerator`
## 启动命令
```bash
mvn clean compile -DskipTests # 编译(需要 JDK 17
mvn spring-boot:run # 启动(默认 profile: local
```
## 关联项目
| 项目 | 路径 | 端口 | 说明 |
|------|------|------|------|
| lpt-fe | `../lpt-fe/` | 5158 | Vue 3 前端,通过 `/api` 代理调用本服务 |
| lpt-ai | `../lpt-ai/` | 5199 | AI 服务,本服务通过 HTTP 调用其异步任务接口 |
## 项目规范
## 数据库迁移(Flyway Migration
### 命名规则
- 脚本格式:`V{YYYYMMDD}_{序号}__{描述}.sql`
- 日期必须使用**实际编写日期**,不得使用过去的日期
- 序号从 1 开始,同一天多个脚本递增
- 描述使用下划线分隔的英文短语
### 核心原则
- **不可变性**:已执行的迁移脚本永远不得修改
- **只增不减**:数据库变更只能通过新增迁移脚本实现
- **向后兼容**:新脚本应兼容已有数据
### 操作规范
- 删除表:创建新迁移脚本,使用 `DROP TABLE IF EXISTS`
- 修改表结构:使用 `ALTER TABLE` 语句
- 新增表:创建新迁移脚本,使用 `CREATE TABLE`
## 框架机制
### 行级数据隔离(多租户拦截器)
- **配置类:** `MybatisPlusConfig` 注册 `TenantLineInnerInterceptor`
- **租户字段:** `created_by`(每个业务表的创建人字段)
- **租户值来源:** `StpUtil.getLoginIdAsString()`(当前登录用户)
- **自动注入:** 所有 SELECT/UPDATE/DELETE 语句自动追加 `WHERE created_by = #{当前用户}`
- **归属校验:** 查询单条记录时拦截器自动校验 `created_by`,非本人数据直接返回空
- **排除表:** `user``flyway_schema_history``databasechangelog``databasechangeloglock`
### 自动填充(MetaObjectHandler
- **created_by / updated_by** 插入/更新时自动填充为当前登录用户
- **created_time / updated_time** 插入/更新时自动填充当前时间
### 认证鉴权(Sa-Token
- **会话管理:** 基于 `StpUtil` 的登录/登出/会话查询
- **权限校验:** `@SaCheckPermission` 注解式权限控制
- **未登录处理:** 全局异常处理器捕获 `NotLoginException` 返回 401
## 编码规范
### 数据访问层
- **优先使用纯 MyBatis-Plus Java API**`Wrappers.<T>lambdaQuery()``selectList``selectById` 等),避免手写 XML SQL
- 复杂查询通过 `Wrappers` 链式构建条件,必要时使用 `.apply()` 拼接原生 SQL 片段