# Core 模块业务逻辑

## 模块职责

Core 模块提供跨业务复用的基础能力，包括网络请求、缓存存储和通用设计常量。业务页面不应直接重复实现这些能力。

主要子模块：
- `Networking`：统一 API 请求、响应解析、错误处理和 token 注入。
- `Storage`：统一登录 token、账号快照和 App 偏好的本地存储。
- `Design`：统一颜色、字号、间距、控件尺寸和圆角。
- `Upload`：统一阿里云 OSS 上传、图片压缩、上传策略和 Kingfisher 网络图片展示。

## Networking

`APIClient` 是统一网络客户端，负责：
- 根据 `APIRequest` 生成 `URLRequest`。
- 注入公共 Header：`Content-Type`、`Accept`、App 版本和系统类型。
- 通过 token provider 或 `tokenOverride` 注入登录 token。
- 发送请求并处理 URLSession 错误。
- 校验 HTTP 状态码。
- 解码后端统一 `APIEnvelope`。
- 将 HTTP 错误、业务错误、解析错误转成 `APIError`。

业务模块只应该封装自己的 API 类，例如 `AuthAPI`、`ProfileAPI`，然后调用 `APIClient.send`。页面和 ViewModel 不应直接拼接 URL 或处理原始响应体。

### 响应约定

后端响应通过 `APIEnvelope<Response>` 解包：
- `code` 表示业务状态。
- `msg` 表示业务提示。
- `data` 是真正业务数据。

`APIEnvelope.isSuccess` 为 false 时，`APIClient` 抛出 `APIError.serverCode`。

### token 失效判断

`APIError.isAuthenticationExpired` 用于判断是否需要清空登录态：
- HTTP 401 / 403 视为登录失效。
- 业务码 `200001` 视为登录失效。
- 错误文案包含 token、过期、登录失效、重新登录、unauthorized、验证失败等关键词时视为登录失效。

## Storage

本地缓存按安全级别拆分：
- `SessionTokenStore`：使用 Keychain 保存正式 token。
- `AccountSnapshotStore`：使用 UserDefaults 保存非敏感账号快照。
- `AppPreferencesStore`：使用 UserDefaults 保存上次手机号、协议同意状态等偏好。

缓存边界：
- 正式 token 只放 Keychain。
- 临时 token 只放内存。
- 密码、验证码、OSS STS token、一次性扫码结果和错误提示不落盘。
- 头像、证件照等图片缓存交给 Kingfisher，Core 不保存图片 Data。

`AccountSnapshot` 保存可重建的账号展示和业务上下文：
- `AccountProfile`
- 账号类型和业务账号 ID
- 当前角色 ID
- 景区作用域和门店作用域
- 当前景区 ID 和当前门店 ID

## Design

`AppDesign` 管理跨页面颜色。`AppMetrics` 管理常用字号、间距、控件尺寸、行距和圆角。

新增页面时优先使用 `AppMetrics` 和 `AppDesign`。只有明显属于单个页面的特殊尺寸，才保留在页面本地。

### 全局 Loading

`GlobalLoadingCenter` 是全局 Loading 的命令中心，只负责展示加载状态，不保存业务数据。业务 View 只能通过 Environment 获取它并调用 `show`、`hide`、`updateMessage`、`withLoading` 或 `withOptionalLoading`，不要在 `body` 中读取 `isVisible`、`message` 等展示状态。

Loading 的可观察状态只在 `GlobalLoadingOverlayHost` 内部订阅，并由 `RootView` 挂载到应用根部。这样切换 Loading 显隐时，只会刷新根部 Overlay，不会让当前页面、Tab 根视图或业务子视图形成观察依赖。

当前全局 Loading UI 不展示文案；`message` 字段保留为内部展示状态，便于后续需要时恢复文案展示。

全局 Loading 只用于阻塞型等待，例如冷启动恢复、登录、首屏加载、提交表单和核销。列表加载更多、上传进度、按钮内局部反馈继续保留在页面局部状态中。

### 全局 Toast

`ToastCenter` 只用于展示轻量提示文案，业务页面通过 `toastCenter.show(...)` 发出提示命令，不直接读取 Toast 展示状态。

Toast UI 是顶部全宽横幅，背景使用不透明主色并延伸到顶部安全区和屏幕左右边；文案居中展示，无关闭按钮，默认 2.2 秒自动隐藏。新的 Toast 会覆盖旧 Toast 并重新计时。

## Upload

`UploadAPI` 通过 `/api/app/config/get-sts-token` 获取阿里云 OSS 临时上传配置。`OSSUploadService` 负责校验文件、生成 objectKey、调用 `AlibabaCloudOSS` SDK 并返回最终文件 URL。

上传模块只保存内存状态：
- STS token 不写入 Keychain 或 UserDefaults。
- 用户选择的本地图片 Data 不落盘。
- 上传进度只用于当前页面展示。

网络图片统一使用 `RemoteImage` / `RemoteAvatarImage`，内部由 Kingfisher 负责下载和缓存。业务页面不要再直接使用 `AsyncImage` 加载网络图片。
