Files
youlai-boot/AGENTS.md

153 lines
10 KiB
Markdown
Raw 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.
# AGENTS.md
本仓库是 [youlai-boot](https://gitee.com/youlaiorg/youlai-boot) 的 TTSTD 定制分支,用于**老年平板设备管理**:设备端(sn)、家属端(client)、运营后台(device / app)。
## 1. 与 Gitee 上游的关系(最高优先级)
远程仓库:
| remote | 地址 | 用途 |
| --- | --- | --- |
| `gitee` | `git@gitee.com:youlaiorg/youlai-boot.git` | **上游只读**(push 已禁用),只用于比对/同步 |
| `origin` | `ssh://git@gitea.ttstd.com:2222/tt/youlai-boot.git` | 我方仓库 |
**只有以下目录是我方新增的业务代码,可自由新增/修改:**
| 目录 | 职责 |
| --- | --- |
| `src/main/java/com/youlai/boot/app/` | 客户端应用管理:应用分类、应用包(APK/XAPK/APKM 解析、上传、Mongo 存储) |
| `src/main/java/com/youlai/boot/client/` | 家属端:App 用户、通讯录、设备操作、文件 |
| `src/main/java/com/youlai/boot/device/` | 设备管理:sn 设备、截图、壁纸、策略、告警、APK 安装、硬件/网络/安全/系统信息 |
| `src/main/java/com/youlai/boot/open/` | 家属端开放认证:短信登录、密码登录、注册、重置密码 |
| `src/main/java/com/youlai/boot/common/config/` | `FilePath`(本地存储路径)、`WebMvcConfig`(静态资源映射) |
**其余所有目录(`auth/`、`system/`、`framework/`、`support/`、`config/`、`module/`、`codegen/` 等)都来自上游,尽量保持不动。**
### 修改前必做检查
```bash
git fetch gitee
git ls-tree -r --name-only gitee/master -- src/main/java/com/youlai/boot/xxx # 上游是否存在该文件
git log --oneline gitee/master -- src/main/java/com/youlai/boot/xxx # 上游是否有近期改动
```
- **上游存在该文件 → 尽量不改**。优先在五个自有目录内新增类/扩展点(继承、实现接口、新增 Controller)来满足需求。
- **确实必须改上游文件时**:只做最小必要改动,不做格式化/重命名/顺带重构,提交信息注明原因,例如
`chore(upstream): 安全配置放行 /api/v1/xxx 以支持新模块`。
- **上游不存在该文件 → 可新增**,但应落在上述五个目录内。
### 已登记的例外清单
以下改动位于上游目录,属结构性必要或历史遗留。**请勿删除、重构或格式化**,修改前先确认调用方。
#### A. 上游目录中新增的文件(上游不存在,因框架结构必须留在原包)
| 位置 | 说明 |
| --- | --- |
| `common/constant/ClientTypeConstants.java` | 端标识(admin / client) |
| `common/util/{HashUtils, FileUtils, RedisSafeOps, CodeGeneratorUtil}.java` | 校验值、文件、Redis 安全操作、编码生成工具 |
| `config/{DataSourceConfig, RedissonConfig, MqttConfig, RedisHealthConfig}.java`、`config/property/MqttProperties.java` | 多数据源、分布式锁、MQTT、Redis 健康检查 |
| `framework/security/filter/{MobileApiSignatureFilter, CachedBodyRequestWrapper}.java` | 设备端 ECDSA 验签过滤器与请求体缓存 |
| `framework/security/signature/DeviceSignatureVerifier.java` | 设备签名校验 |
| `framework/security/authorization/ClientAccessAuthorizationManager.java` | 家属端令牌隔离 |
| `support/mqtt/{MqttController, MqttMessageHandler, MqttProducer}.java` | MQTT 上报/推送 |
| `support/webrtc/**` | WebRTC 信令(`/ws/signal`) |
| `support/sms/SmsResult.java` | 短信发送结果 |
| `resources/mapper/system/DeviceMapper.xml` | 设备分页查询 |
| `test/java/com/youlai/boot/app/packages/component/ApkMetaParserTest.java` | 安装包解析测试 |
#### B. 被修改的上游文件(只允许最小增补,不做重构/格式化)
| 文件 | 允许的改动范围 |
| --- | --- |
| `config/SecurityConfig.java` | 放行 `/api/v1/sn/**`、`/api/v1/open/**`、`/ws/signal`;`/api/v1/client/**` 走 `ClientAccessAuthorizationManager` |
| `config/JacksonConfig.java` | **保持上游原样**(不得添加未使用的 import / 注释代码) |
| `config/RedisConfig.java` | 仅 `@Lazy` 延迟初始化;序列化方式必须与上游一致 |
| `framework/security/config/SecurityProperties.java` | client 端令牌 TTL 两个字段 |
| `framework/security/model/{SecurityUser, SecurityUserDetails}.java` | `clientType` 字段 |
| `framework/security/token/JwtTokenManager.java` | client 端 TTL 解析、`clientType` claim、校验失败日志 |
| `common/constant/JwtClaimConstants.java`、`common/constant/RedisConstants.java` | `clientType` claim、设备在线 key、短信验证码 key |
| `common/enums/{ActionTypeEnum, LogModuleEnum}.java`、`common/result/ResultCode.java` | 仅追加新枚举值/错误码 |
| `framework/web/advice/GlobalExceptionHandler.java` | Redis 相关异常、SSE 超时、静态资源 404 |
| `framework/web/log/OperationLogAspect.java` | 设备无登录会话时的操作人兜底;`error_msg` 上限 2000 |
| `system/service/impl/ConfigServiceImpl.java` | 缓存预热失败不阻断启动 |
| `support/file/model/FileInfo.java`、`support/file/service/impl/LocalFileServiceImpl.java` | md5/sha1/sha256 校验值 |
| `support/sms/{SmsTypeEnum, service/SmsService, service/impl/AliyunSmsServiceImpl}.java` | `sendWithResult`,按响应码判定成败 |
| `support/sse/{SseService, SseSessionManager, constant/SseEventConstants}.java` | 设备事件主题 |
| `pom.xml` | 新增依赖(websocket、mongodb、mqtt、apk-parser、apkanalyzer、redisson 等) |
| `.gitignore` | 目前仅新增 `/uploadFile/` |
| `resources/application-dev.yml`、`resources/application-prod.yml` | 数据源(mysql/tdengine)、redis、mongodb、oss、sms、mqtt、安全白名单、令牌 TTL |
| `sql/youlai-admin.sql` | `sys_log.error_msg` 由 1000 调整为 2000 |
#### C. 数据与运维脚本(非 Java 代码,允许新增)
`sql/*.sql`、`docs/sql/*.sql`、`docker/*`。
#### D. 禁止事项
- 上游文件里做格式化、重命名、import 重排、注释掉代码等无关改动。
- 配置文件提交真实凭证:密码/AccessKey 一律用环境变量或本地 profile 覆盖,不写入版本库。
- 在上游目录新增 Java 类(应放到自有目录,例如 `ByteArrayMultipartFile` 放在 `app/packages/component/`)。
## 2. 技术栈
Java 17 · Spring Boot 4.0.5 · Spring Security · MyBatis-Plus 3.5.15 · MySQL · Redis + Redisson · MongoDB(`spring-boot-starter-data-mongodb`) · WebSocket · knife4j/springdoc · MapStruct + Lombok · Hutool · apk-parser / apkanalyzer
## 3. 构建与运行
```bash
mvn -DskipTests clean package # 打包,产物 target/youlai-boot-4.12.0.jar
mvn test # 测试
java -jar target/youlai-boot-4.12.0.jar --spring.profiles.active=dev
```
- 默认 profile 为 `dev`(`application.yml`),配置见 `application-dev.yml` / `application-prod.yml`;代码生成配置在 `codegen.yml`。
- 数据库连接请使用本地 `/ 环境变量` 覆盖,**不要提交真实账号密码**。
- Windows 上 shell 为 PowerShell,勿使用 `head`/`tail`/`cd /d` 等 cmd/Unix 语法。
## 4. 代码约定
包结构(自有模块统一遵循):
```
xxx/
├── controller/ # @RestController,路径 /api/v1/...
├── service/ # 接口
│ └── impl/ # 实现,继承 MyBatis-Plus ServiceImpl<Mapper, Entity>
├── converter/ # MapStruct 转换器
├── mapper/ # MyBatis-Plus Mapper(自有模块一般不写 XML)
├── repository/ # Mongo 仓储(接口 + impl,用 MongoTemplate)
└── model/
├── entity/ # MySQL 实体(设备相关以 Sn 前缀命名,如 SnDeviceInfo)
├── document/ # Mongo 文档(如 AppPackageDocument)
├── form/ req/ query/ # 入参
└── vo/ dto/ # 出参
```
- 统一返回 `Result<T>` / `PageResult<T>`,分页参数用 `PageQuery`,分页结果用 `IPage`。
- 构造器注入:类加 `@RequiredArgsConstructor`,字段 `private final`。
- 接口文档:`@Tag(name = "序号.名称")` + `@Operation(summary = ...)`。
- 操作日志:`@Log(module = LogModuleEnum.X, value = ActionTypeEnum.Y)`。
- 鉴权分区:
- `/api/v1/sn/**`:设备端,`MobileApiSignatureFilter` 做 ECDSA 设备签名校验(非 JWT)
- `/api/v1/client/**`:家属端,仅接受 `clientType=client` 的令牌
- `/api/v1/open/**`:免登录(登录/注册/找回密码)
- 后台接口(`/api/v1/device`、`/api/v1/app/**`、`/api/v1/developers` 等):走 Spring Security,按需 `@PreAuthorize`
- 业务文件统一通过 `FileService#uploadFile(MultipartFile, String directory)` 存到 `StorageDir` 定义的固定目录(`app/icon`、`app/package-icon`、`app/package`、`app/screenshot`、`app/avatar`、`app/wallpaper`、`app/category-icon`、`app/file`),实际存储由 `storage.type`(s3 / aliyun / local)决定;**不再按上传日期分目录**。
- 头像走专用接口 `POST /api/v1/files/avatar`(管理端)与 `POST /api/v1/client/files/avatar`(家属端),落 `app/avatar`;通用上传接口不接受目录参数。
- `FilePath` + `/static/{子目录}/**` 仅用于设备截图等纯本地资源,以及存量历史文件的访问。
- 新增后台菜单/表结构请补充 `sql/` 脚本,保持与代码同步。
## 5. 提交约定
采用 Conventional Commits(`feat(device): ...`、`fix(client): ...`、`refactor(config): ...`、`chore: ...`)。涉及上游文件的改动需在正文说明原因。
## 6. 不做兼容(强制)
变更直接定案,**不写向后兼容代码**:
- 接口入参/出参、字段语义、存储路径变更时,直接改成目标形态;禁止可选参数、双分支、"不传则走旧逻辑"、"新老格式都支持"之类的兜底写法。
- 禁止为旧前端/旧设备端保留旧接口、旧字段或旧 URL 前缀;需要它们同步修改时,在提交说明中列明改动点。
- 存储位置由业务语义决定:一类资源对应 `StorageDir` 中的一个固定常量,使用**专用接口**落盘(如 `POST /api/v1/files/avatar`、`POST /api/v1/client/files/avatar`),不得让调用方通过 `directory` 之类的参数决定目录。
- 存量历史数据(旧本地路径、旧对象目录、旧字段格式)只允许用一次性迁移脚本或管理接口处理,不得在业务代码中长期保留运行时回退分支。