Files
flutter-template/CLAUDE.md
T

243 lines
11 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.
# CLAUDE.md — Flutter 企业级脚手架(sunny_mochi
> 本文件为 Claude Code 提供项目上下文。**所有 AI 辅助开发会话必须首先读取本文件**,
> 再按需加载 `docs/` 下的补充文档。
---
## 项目身份信息
> **以下带 `TODO` 的字段由业务项目接手时填入,脚手架默认保留占位符。**
| 字段 | 当前值(脚手架默认) | 说明 |
|------|-----------------|------|
| 项目名称 | sunny_mochi | TODO: 替换为实际项目名 |
| Application ID (Android) | com.example.sunny_mochi | TODO: 如 com.company.projectname |
| Bundle ID (iOS) | com.example.sunny-mochi | TODO: 与 Android 对应 |
| API Base URL (dev) | http://localhost:8080 | TODO: 填入 `dart-defines/dev.json` |
| API Base URL (staging) | https://staging.example.com | TODO: 填入 `dart-defines/staging.json` |
| API Base URL (prod) | https://api.example.com | TODO: 填入 `dart-defines/prod.json` |
| 后端鉴权框架 | sa-token(动态 header tokenName | TODO: 若使用 Bearer/JWT 修改 auth_interceptor |
| Sentry DSN | 空(未启用) | TODO: 填入 `dart-defines/prod.json` |
| RSA 公钥 | REPLACE_WITH_RSA_PUBLIC_KEY | TODO: 填入三个 dart-defines JSON |
---
## 技术栈(脚手架锁定版本)
| 分类 | 技术 | 版本 |
|------|------|------|
| Flutter SDK | Flutter | ≥ 3.41.0 |
| 状态管理 | Riverpod + riverpod_annotation | ^3.3.0 / ^4.0.0 |
| 路由 | GoRouter + go_router_builder | ^17.0.0 / ^4.3.0 |
| 网络 | Dio | ^5.9.0 |
| 本地数据库 | Drift + SQLCipher | 2.31.0(锁定)|
| 安全存储 | flutter_secure_storage | ^10.0.0 |
| 序列化 | Freezed + json_serializable | ^3.x / 6.13.0(锁定)|
| 日志 | Talker + talker_flutter | ^5.x |
| 错误追踪 | Sentry Flutter | ^9.0.0 |
| i18n | Slang | ^4.0.0 |
| UI 自适应 | flutter_screenutil(基准 375×812| ^5.9.0 |
| 图片缓存 | cached_network_image | ^3.4.0 |
| 字体 | HarmonyOS Sans SCRegular/Medium/Bold/Black| GB2312 子集 |
**版本锁定原因(勿随意升级):**
- `drift: 2.31.0` — 2.32+ 需要 analyzer ^10.x,与 riverpod_generator 的 analyzer ^9.x 冲突
- `json_serializable: 6.13.0` — 6.13.1+ 同样需要 analyzer ^10.x
---
## 目录结构
```
lib/
├── main.dart # 6步启动序列(顺序固定,勿调整)
├── app.dart # AppRoot → ScreenUtilInit → App → MaterialApp.router
├── i18n/ # Slang 翻译(zh-CN 基准 + en 备用)
├── core/
│ ├── config/
│ │ ├── env.dart # 从 dart-defines 读取环境变量(只读,勿改结构)
│ │ ├── api_config.dart # baseUrl 按 Flavor 决策
│ │ └── api_paths.dart # ★ 新项目必填:所有 API 路径常量
│ ├── router/
│ │ ├── app_router.dart # GoRouter providerkeepAlive
│ │ └── routes.dart # ★ 新项目扩展:添加 @TypedGoRoute
│ ├── network/
│ │ ├── dio_client.dart # 7 拦截器注册(顺序固定)
│ │ ├── api_response.dart # 统一 envelope 解析(parseEnvelope / unwrapVoid
│ │ ├── response_code.dart# 业务状态码(成功=00000token 类码分类)
│ │ ├── interceptors/ # 7 个拦截器(勿修改拦截顺序)
│ │ └── mock/ # MockAdapter + fixture JSONdev 调试用)
│ ├── storage/
│ │ ├── app_database.dart # Drift @DriftDatabaseschemaVersion 从 1 开始)
│ │ ├── secure_storage.dart# token / userId / tokenName 的唯一真源
│ │ ├── db_key_provider.dart# AES-256 密钥派生(勿修改策略)
│ │ ├── tables/ # Drift 表定义(Users + ErrorLogs + 业务表)
│ │ └── daos/ # DAO(只放纯 DB 操作)
│ ├── crash/ # CrashReporterpreInit/consumePending/installHooks
│ ├── error/
│ │ ├── failures.dart # Sealed Failure 层级(唯一错误分类)
│ │ └── exception_mapper.dart # 异常 → Failure 映射
│ ├── theme/ # 5 色板 + 深色模式(ThemeNotifier + SharedPreferences
│ ├── observability/ # Talker(全局 appTalker+ SentryRelease 上报)
│ ├── sync/ # Offline-First 同步框架骨架(SyncService
│ ├── crypto/ # RSA PKCS#1 v1.5RsaHelper.encrypt
│ ├── extensions/ # BuildContextX / StringX / NumX / DateTimeX
│ ├── utils/ # Validator / PasswordValidator / SmsCountdownMixin
│ ├── data/ # FreshnessPolicy(缓存新鲜度判断)
│ └── widgets/ # 通用 UI 组件(AppToast / AppButton / EmptyView 等)
└── features/
├── auth/ # ★ 认证(完整骨架,业务项目修改 LoginPage UI
├── dev_panel/ # Talker 面板(Internal 包,勿在 release 中暴露入口)
└── error_report/ # 本地错误日志 + 上报(完整实现)
```
---
## 开发命令
```bash
# 代码生成(修改 Dart 文件后运行)
make gen
# i18n 生成(修改 JSON 翻译文件后运行)
make gen-i18n
# 运行
make run-dev # dev 包(连 dev 服务器,mock 可开)
make run-staging # staging 包(连 test 服务器,release 模式)
# 构建
make build-apk-dev # dev APK(快速真机验证)
make build-staging # staging APK(交测试团队)
make build-prod # prod AAB(需 prod.json + key.properties
# 质量检查
flutter analyze # 目标 0 errors103 条 info 为正常)
flutter test # 全量测试
```
---
## 常用开发范式
### 新增 Feature
```
lib/features/{feature_name}/
├── domain/
│ ├── entities/{name}_entity.dart # @freezed,纯 Dart 字段
│ └── repositories/{name}_repository.dart # abstract interface
├── data/
│ ├── models/{name}_model.dart # @freezed + fromJson,含 toEntity()
│ ├── datasources/{name}_remote_datasource.dart # @riverpod,只调 Dio
│ └── repositories/{name}_repository_impl.dart # @riverpod,实现 domain 接口
└── presentation/
├── notifiers/{name}_notifier.dart # @riverpod class XxxNotifier
└── pages/{name}_page.dart # ConsumerWidget / ConsumerStatefulWidget
```
### 新增路由
`lib/core/router/routes.dart` 添加(然后运行 `make gen`):
```dart
@TypedGoRoute<MyNewRoute>(path: '/my-new-path')
class MyNewRoute extends GoRouteData with $MyNewRoute {
const MyNewRoute();
@override
Widget build(BuildContext context, GoRouterState state) => const MyNewPage();
}
```
### 新增 API 路径
`lib/core/config/api_paths.dart` 添加:
```dart
// ============== My Feature ==============
static const String myFeatureList = '/api/v1/my-feature/list';
```
### 新增 DB 表
1.`lib/core/storage/tables/` 新建表文件
2.`lib/core/storage/app_database.dart``@DriftDatabase(tables: [...])` 添加
3. 递增 `schemaVersion` 并在 `MigrationStrategy.onUpgrade` 中添加迁移
4. 运行 `make gen`
### 错误处理标准模式
```dart
// Notifier 层:捕获 + 映射
} on Object catch (e, st) {
final failure = ExceptionMapper().fromUnknown(e, st);
state = MyState.error(failure.message);
}
// UI 层:监听 + 显示
ref.listen(myProvider, (_, next) {
if (next is AsyncError) context.showError(ref, next.error!, next.stackTrace!);
});
```
---
## 零容忍规则(AI 必须遵守)
| 规则 | 正确 | 禁止 |
|------|------|------|
| API 路径 | `ApiPaths.xxx` 常量 | 字符串字面量散落在代码中 |
| 错误处理 | `ExceptionMapper().fromUnknown(e, st)` | 裸 `catch` 直接 `toString()` 显示 |
| Token 存储 | `SecureStorage`Keychain/EncryptedPrefs| 普通 `SharedPreferences` |
| 数据库加密 | SQLCipher(默认) | 无加密 `sqflite` |
| 日志输出 | `appTalker.info/warning/error()` | `print()` / `debugPrint()` |
| 路由跳转 | `XxxRoute().go(context)` / `XxxRoute().push(context)` | `Navigator.push()` |
| 状态管理 | Riverpod `@riverpod` / `@Riverpod(keepAlive: true)` | `setState` 跨组件共享 / `Provider` 包 |
| Riverpod 命名 | 生成 provider 名(`AuthNotifier``authProvider`| 手写 `authNotifierProvider` |
| 代码生成文件 | 只读,不手写 `.g.dart` / `.freezed.dart` | 手动修改生成文件 |
| Domain 层 | 纯 Dart,无 Flutter / Drift / Dio 依赖 | domain 层 import `package:dio` |
| Mock fixture | `assets/fixtures/{group}/{name}.json` | 在代码中硬编码 mock 数据 |
| 注释语言 | 中文(团队约定) | 英文注释(除公共 API doc)|
---
## 关键架构决策(勿重议)
1. **SQLCipher 不可替换**:企业合规要求,部署后无法无损切换到无加密数据库
2. **7 拦截器顺序固定**CertPinning→Auth→TokenRefresh→Retry→Error→AuthLogout→Log,错误处理链依赖此顺序
3. **TokenRefresh 使用 Completer 互斥**:防止并发 401 重复消耗 RefreshToken,不可改为简单 flag
4. **SecureStorage 是 token 唯一真源**DB 中的 refreshToken 仅作可观测性镜像
5. **GoRouter 用 `ref.read`(非 `ref.watch`)构建**router 是 keepAlive 单例,通过 `refreshListenable` 响应 auth 变化
6. **Failure 不可绕过**:所有网络/DB 异常必须经 ExceptionMapper 映射,UI 只看 Failure.message
---
## 新项目接手 Checklist
> 完成后删除本节或移至项目 wiki
- [ ] 更新本文件"项目身份信息"表格
- [ ] 填入 `lib/core/config/api_paths.dart` 所有路径
- [ ] 配置三个 `dart-defines/*.json`(含 RSA 公钥、Sentry DSN
- [ ] 修改 `android/app/build.gradle.kts` 中的 `applicationId`
- [ ] 修改 `pubspec.yaml` 中的 `name`(含相关 import 路径)
- [ ] 完成 `docs/setup-android.md` 中的 Android 签名配置
- [ ] 完成 `docs/setup-ios.md` 中的 iOS Scheme / Signing 配置
- [ ] 替换登录页 UI 品牌(`lib/features/auth/presentation/pages/login_page.dart`
- [ ] 替换 App IconAndroid mipmap / iOS AppIcon.appiconset
- [ ] 实现业务 Tab 页面(替换 `routes.dart` 中的 placeholder build
- [ ] 更新 README.md 项目描述
---
## 参考文档
| 文档 | 内容 |
|------|------|
| [README.md](README.md) | 项目概述、技术栈、快速开始 |
| [docs/setup-project.md](docs/setup-project.md) | 完整项目配置指南(所有开发者必读)|
| [docs/setup-android.md](docs/setup-android.md) | Android Flavor / 签名 / 构建详细配置 |
| [docs/setup-ios.md](docs/setup-ios.md) | iOS Scheme / CocoaPods / 签名详细配置 |