172 lines
6.9 KiB
Markdown
172 lines
6.9 KiB
Markdown
# conti-retail-app
|
||
|
||
大陆马门店 App。Flutter + Melos 单仓多包。
|
||
|
||
架构约束全部来自相邻仓库 `conti-docs/` 的 01–14 号文档。**本仓库对这些文档的
|
||
偏离,逐条记在 [SCAFFOLD-NOTES.md](SCAFFOLD-NOTES.md)**——那份文件是回写文档的
|
||
依据,改动之前先看一眼。
|
||
|
||
---
|
||
|
||
## 怎么跑起来
|
||
|
||
三步。不要跳过第二步。
|
||
|
||
```bash
|
||
# 0. 工具链(一次性)
|
||
fvm use 3.44.9 # 或者保证 PATH 上的 flutter 就是 3.44.9
|
||
dart pub global activate melos 8.2.2
|
||
|
||
# 1. 装依赖
|
||
melos bootstrap
|
||
|
||
# 2. 生成代码(riverpod 的 *.g.dart 不入库,不跑这步全仓库编译不过)
|
||
melos run gen
|
||
|
||
# 3. 跑起来
|
||
cd app
|
||
flutter run --flavor dev -t lib/main_dev.dart --dart-define-from-file=env/dev.json
|
||
```
|
||
|
||
`--flavor` / `-t` / `--dart-define-from-file` **三个参数缺一不可**:
|
||
|
||
- 少了 `--dart-define-from-file`,`AppEnv.fromDartDefine` 会在启动瞬间抛错。
|
||
这是故意的——带着空 baseUrl 跑起来,问题会在第一个请求 404 时才暴露。
|
||
- 少了 `-t`,Flutter 会去找 `lib/main.dart`,本仓库没有这个文件。
|
||
|
||
VS Code / Android Studio 用户建议把三条 flavor 配进 `launch.json`。
|
||
|
||
## 环境与工具链版本
|
||
|
||
跑通时的实测版本,与 `pubspec.lock` 一致:
|
||
|
||
| | 版本 | 备注 |
|
||
|---|---|---|
|
||
| Flutter | 3.44.9 | `.fvmrc` 里锁着 |
|
||
| Dart | 3.12.2 | **Flutter 自带的那个**,见下面的坑 |
|
||
| Melos | 8.2.2 | 配置内联在根 `pubspec.yaml`,没有 `melos.yaml` |
|
||
| flutter_riverpod | 3.3.2 | |
|
||
| riverpod_annotation | 4.0.3 | |
|
||
| riverpod_generator | 4.0.4 | |
|
||
| build_runner | 2.15.1 | |
|
||
| analyzer | 12.1.0 | |
|
||
|
||
> **riverpod 的版本被 `flutter_test` 卡着。** `flutter_test` pin 了
|
||
> `test_api 0.7.11`,往上升 riverpod 会拉起不兼容的 `analyzer`,解析直接失败。
|
||
> 想升级先确认这条链路,不要只看 pub.dev 上的最新版。
|
||
|
||
### 坑:机器上有两个 Dart SDK
|
||
|
||
如果你的 PATH 上装了独立的 Dart SDK(`dart --version` 不等于 3.12.2),那么
|
||
|
||
```bash
|
||
dart run build_runner build # ← 会用错 SDK,报一堆看不懂的解析错误
|
||
```
|
||
|
||
要显式用 Flutter 自带的那个:
|
||
|
||
```bash
|
||
export FDART="$(dirname "$(which flutter)")/cache/dart-sdk/bin/dart"
|
||
$FDART run build_runner build
|
||
# fvm 用户:~/fvm/versions/stable/bin/cache/dart-sdk/bin/dart
|
||
```
|
||
|
||
`melos run gen` 内部走的是 `dart run`,所以同样受影响。最省事的做法是让 PATH
|
||
上只有 Flutter 自带的 Dart。
|
||
|
||
### 坑:melos 命令
|
||
|
||
pub cache 里只有 `melos.bat`,git-bash 下直接敲 `melos` 可能找不到。用:
|
||
|
||
```bash
|
||
dart pub global run melos:melos <cmd>
|
||
```
|
||
|
||
## 常用命令
|
||
|
||
```bash
|
||
melos run gen # 代码生成(riverpod)
|
||
melos run gen:watch # 开发期常驻
|
||
melos run gen:pigeon # native_* 的 Pigeon 产物(产物入库)
|
||
melos run analyze # flutter analyze --fatal-infos,含 riverpod_lint
|
||
melos run format # dart format --set-exit-if-changed
|
||
melos run test # flutter test --coverage
|
||
```
|
||
|
||
`analyze` 带 `--fatal-infos`。不加等于没加 lint——绝大多数 riverpod_lint 规则
|
||
报的是 info 级。
|
||
|
||
`riverpod_lint` 由 `flutter analyze` 直接执行(顶层 `plugins:` 映射),**不再
|
||
需要 `custom_lint`**,也没有 `dart run custom_lint` 这一步。文档 03/14 写的还是
|
||
旧方案,见 SCAFFOLD-NOTES §B。
|
||
|
||
## 包结构与依赖规则
|
||
|
||
```
|
||
app/ 壳工程。唯一知道所有包的地方:环境注入、启动编排、路由聚合
|
||
packages/
|
||
core_foundation/ AppEnv / AppException 体系 / ApiCode ← 叶子包,谁都能依赖
|
||
core_logging/ AppLogger / CrashReporter / 脱敏
|
||
core_analytics/ Analytics 接口 + 事件常量表
|
||
core_storage/ Prefs(KV)。Drift 暂缓,见 SCAFFOLD-NOTES
|
||
core_auth/ AppSession 四态 / TokenStorage / SessionNotifier
|
||
core_network/ dio + 4 个拦截器 + ApiClient
|
||
core_router/ goRouterProvider / menuRouteMap / go_router re-export
|
||
core_ui/ 主题 / AsyncValueView / ErrorPresenter
|
||
core_webview/ UrlGuard / JSBridge / WebViewSession
|
||
feature_auth/ 登录、选店
|
||
feature_home/ 工作台
|
||
native_scan/ Pigeon 扫码接口(原生实现待补)
|
||
```
|
||
|
||
依赖规则(01):
|
||
|
||
- `feature_* → core_* / native_*`。**feature 之间禁止互相依赖**——两个 feature
|
||
要共享东西,说明那个东西属于某个 core_*。
|
||
- `core_* → core_*` 只允许三条边:`core_network → core_auth`、
|
||
`core_router → core_auth`、`core_webview → core_auth`。外加所有包都可以依赖
|
||
叶子包 `core_foundation`。
|
||
- `native_* ` 只依赖 Flutter SDK 和 Pigeon 产物,**一个 core_* 都不依赖**。
|
||
- feature 的 pubspec 里**不出现 `go_router`**:路由类型由 `core_router`
|
||
re-export。
|
||
|
||
### 这些规则靠 `--fatal-infos` 守,不是靠编译器(实测结论)
|
||
|
||
直觉上会以为「没在 pubspec 里声明就 import 不到」。**在 Pub Workspace 里这是错的**:
|
||
所有成员包共用根目录一份 `.dart_tool/package_config.json`,任何成员都能解析到
|
||
任何其他成员。实测在 `feature_home` 里 import `feature_auth` 而不声明依赖:
|
||
|
||
```
|
||
flutter test → All tests passed! ← 编译通过,跑得起来
|
||
flutter analyze → info: depend_on_referenced_packages
|
||
```
|
||
|
||
只有一条 **info**。所以:
|
||
|
||
> **`melos run analyze` 的 `--fatal-infos` 是这套包边界唯一的强制点。**
|
||
> 谁把它从 CI 里拿掉,或者在某个包里 ignore 掉
|
||
> `depend_on_referenced_packages`,边界当天就失效,而且没有任何别的信号。
|
||
|
||
想自己复现:在 `feature_home/lib/src/` 下扔一个 import `feature_auth` 的文件,
|
||
`flutter test` 是绿的,`flutter analyze --fatal-infos` 是红的。
|
||
|
||
### 那些 `throw UnimplementedError('必须在 bootstrap 里 override')`
|
||
|
||
依赖规则会挡住一些**合理**的调用(比如 core_auth 想发 HTTP、core_webview 想
|
||
换票)。这些地方一律用依赖反转解决:包内声明 `abstract interface` + 一个会抛错
|
||
的 provider,实现落在 `app/lib/bootstrap.dart` 里 override 进去。
|
||
|
||
新增一个这样的端口时,**必须同时在 bootstrap 里接上**——它是运行期才炸的,
|
||
编译器帮不了你。
|
||
|
||
## 待办 / 阻塞项
|
||
|
||
- **iOS flavor 未配置**:Scheme 和 Build Configuration 只能在 Xcode 里建,
|
||
步骤见 [`app/ios/FLAVORS.md`](app/ios/FLAVORS.md)。目前没有 Mac 构建机。
|
||
- **native_scan 没有原生实现**:Pigeon 接口和 Dart 侧齐了,Kotlin/Swift 侧是
|
||
模板。现在调 `startScan` 会抛 `MissingPluginException`,属预期。
|
||
- **神策 SDK 未采购**:`analyticsProvider` 是 `NoopAnalytics`。接入时注意必须在
|
||
用户同意隐私政策之后再初始化。
|
||
- **env/*.json 里全是占位域名**,`SENTRY_DSN` 全空,待运维确认。
|
||
- **CI 镜像名未定**,`.gitlab-ci.yml` 里标了 TODO(ops)。
|