177 lines
7.4 KiB
Markdown
177 lines
7.4 KiB
Markdown
# LPT 后端服务(lpt-be)
|
||
|
||
> Java Spring Boot 后端,提供 REST API、数据库访问、认证鉴权。
|
||
|
||
## Git 提交规范
|
||
|
||
- 提交信息必须简短且使用中文,不要使用英文长句。
|
||
- 格式:`类型: 简述`,例如 `feat: 新增用户登录接口`、`fix: 修复多租户拦截器空指针`、`docs: 补充API文档`。
|
||
|
||
## 技术栈
|
||
|
||
- Java 17
|
||
- 框架:Spring Boot 3.2.5
|
||
- ORM:MyBatis-Plus 3.5.5
|
||
- 数据库:MySQL 8.0
|
||
- 认证:Sa-Token 1.38.0,Cookie 名 `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=400,HTTP 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=...` 代理端点。
|
||
|
||
## 运行环境
|
||
|
||
- 默认 profile:local(`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 片段
|