# 「全部功能」页业务逻辑

Android 工程对应文档：[`zhiflyfollow/docs/home/AllFunctions.md`](../../../zhiflyfollow/docs/home/AllFunctions.md)

## 模块职责

「全部功能」页（`HomeMoreFunctionsView`）展示当前角色可用的功能入口，分为两个分区：

- **常用应用**：已加入首页常用的入口，支持移除（`-`）
- **更多功能**：其余可用入口，支持添加为常用（`+`）

常用应用的增删会同步写入本地持久化，并与首页「常用应用」网格共用同一套配置（`HomeCommonMenuStore`）。

本页数据构建规则与 Android `AllFunctionsViewModel` 对齐，**不**复用首页 `HomeViewModel` 的 flatten + `preferredOrder` 逻辑。

---

## 涉及文件

| 文件 | 职责 |
| --- | --- |
| `Views/HomeMoreFunctionsView.swift` | 页面 UI、增删常用、触发重建 |
| `Services/HomeAllFunctionsBuilder.swift` | 菜单列表构建与常用/更多拆分 |
| `Services/HomeCommonMenuStore.swift` | 常用 URI 持久化与默认生成 |
| `Services/HomeAllFunctionsDiagnostics.swift` | Debug 诊断日志 |
| `App/State/PermissionContext.swift` | 提供当前角色顶层权限 |
| `Routing/HomeMenuRouter.swift` | 点击入口后的路由解析 |

---

## 数据流

```mermaid
flowchart TD
    API[role-permission 接口] --> PC[PermissionContext]
    PC --> TLP[topLevelPermissions 顶层节点]
    TLP --> BUILDER[HomeAllFunctionsBuilder.build]
    CMS[HomeCommonMenuStore.load] --> COMMON[commonURIs]
    COMMON --> BUILDER
    BUILDER --> ALL[allFunctions]
    BUILDER --> CF[commonFunctions]
    BUILDER --> MF[moreFunctions]
    CF --> UI1[常用应用网格]
    MF --> UI2[更多功能网格]
```

### 触发重建的时机

`HomeMoreFunctionsView.rebuildMenus()` 在以下情况执行：

1. 页面首次进入（`.task`）
2. 当前角色 `roleCode` 变化
3. `rolePermissions` 数量变化

用户点击 `+` / `-` 时只更新 `commonUris` 并调用 `applySnapshot`，不重新走持久化读取（除非权限上下文同时变化）。

---

## 第一步：读取顶层权限

数据来源：`PermissionContext.topLevelPermissions(for: roleCode)`

- 仅取当前匹配角色的 `role.permission` **顶层数组**
- **不**递归展开 `children` 子节点
- 顺序与 API 返回一致，不做本地重排
- 过滤掉 URI 为空的节点

对应 Android：`appStore.getPermission()` 中保存的顶层权限列表（MMKV 快照）。

---

## 第二步：白名单过滤（可用入口）

由 `HomeAllFunctionsBuilder.allMenuItems(from:)` 执行：

```
allFunctions = 顶层权限
    .按 API 顺序遍历
    .保留 uri ∈ HomeCommonMenuStore.androidHomeMenuURIs 的项
    .映射为 HomeMenuItem
```

`androidHomeMenuURIs` 与 Android `Constants.menuList` 登记 URI 一致。已登记且会出现在「全部功能」页的 URI 包括 `location_report`、`report_photographer`（举报摄影师）、`wallet`、`message_center` 等。

以下典型 URI **不会**出现在「全部功能」页，即使接口返回了顶层权限：

- `basic_info`、`photographer_stats`、`photographer_orders`
- `location_info`、`scan_qr`、`payment_qr`
- `album_list`、`material_upload` 等未登记 URI

子权限 URI 也不会出现（未 flatten）。

---

## 第三步：菜单项字段映射

每个保留的 `PermissionItem` 转为 `HomeMenuItem`：

| 字段 | 规则 |
| --- | --- |
| `uri` | 权限节点原始 URI，精确字符串 |
| `title` | API `name` 非空时用 `name`；否则 `HomeMenuRouter.title(for:)`；再经 `HomeMenuRouter.displayTitle` 统一部分同义入口文案 |
| `iconSrc` | API `icon_src`；为空时 UI 层用 `HomeIconCatalog` SF Symbol 兜底 |

Android 端标题/icon 来自本地 `Constants.menuList` drawable；iOS 优先接口字段 + 本地图标兜底。

---

## 第四步：读取常用 URI

由 `HomeCommonMenuStore.load` 提供 `commonUris`，规则详见 [`Home.md`](Home.md)「常用应用」章节，核心要点：

- 存储 key：`home.common.menu.uris.account.{accountScope}.role.{roleCode}`（无 accountScope 时用 `home.common.menu.uris.role.{roleCode}`）
- 首次无 saved：按顶层前 4 个 URI 与 menuList 求交生成默认，并落库
- 已有 saved：精确 URI 匹配当前可用顶层权限；全部失效时返回空，不回退默认
- 增删常用：精确 URI 匹配（不使用 `menuAliasKey`）

---

## 第五步：拆分为常用 / 更多

`HomeAllFunctionsBuilder.build(topLevelPermissions:commonURIs:)`：

```text
commonSet = Set(commonURIs)

commonFunctions = allFunctions.filter { commonSet.contains($0.uri) }
moreFunctions   = allFunctions.filter { !commonSet.contains($0.uri) }
```

要点：

- 匹配方式：**精确 URI**，不做别名归一
- **常用区顺序**：跟随 `allFunctions` 列表顺序（API 顺序 ∩ 白名单后的顺序），**不是** `commonUris` 数组的存储顺序
- **更多区顺序**：同样跟随 `allFunctions` 剩余项顺序

与 Android 一致：

```kotlin
val common = functions.filter { it.uri in commonUris }
val more   = functions.filter { it.uri !in commonUris }
```

---

## 展示层

### 布局

- 两节标题：「常用应用」「更多功能」
- 每节 3 列 `LazyVGrid`，卡片高度 112pt
- 卡片右上角：`常用` 显示红色 `-`，`更多` 显示蓝色 `+`

### 点击行为

- 点击卡片主体：`HomeMenuRouter.resolve(uri:title:)` 解析路由
  - Tab 切换、订单 Tab、Home 子路由、占位页等
  - `more_functions` URI 在页内忽略（防循环）
- 点击 `-`：`HomeCommonMenuStore.remove` → `applySnapshot`
- 点击 `+`：`HomeCommonMenuStore.add`（校验 URI 在顶层可用白名单内）→ `applySnapshot`

### 与首页的关系

| 维度 | 首页常用应用网格 | 全部功能页 |
| --- | --- | --- |
| 数据源 | `HomeCommonMenuStore` + `HomeViewModel`（展示标题/icon） | `HomeAllFunctionsBuilder` |
| 列表范围 | 仅 common URIs + 「更多功能」入口 | 全部可用入口分 common / more |
| 排序 | common 按 store 顺序映射 | common/more 均按 `allFunctions` API 顺序 |
| 持久化 | 共用 `HomeCommonMenuStore` | 共用 `HomeCommonMenuStore` |

---

## 与 HomeViewModel 的区别

| | `HomeViewModel`（首页等） | `HomeAllFunctionsBuilder`（全部功能） |
| --- | --- | --- |
| 权限范围 | 递归 flatten 整棵权限树 | 仅顶层 |
| 白名单 | 无 | `androidHomeMenuURIs` |
| 排序 | `preferredOrder` 硬编码权重 | API 顶层顺序 |
| 去重 | `menuAliasKey` 别名去重 | 无（顶层 URI 精确保留） |

「全部功能」页**不应**调用 `HomeViewModel.buildMenus()`。

---

## 诊断日志

Debug 构建下可用 Xcode Console 过滤 `HomeAllFunctions`：

| step | 含义 |
| --- | --- |
| `allFunctions` | 白名单过滤后的完整 URI 列表 |
| `commonFunctions` | 常用分区 URI |
| `moreFunctions` | 更多分区 URI |

常用应用持久化链路仍使用 `HomeCommonMenu` tag，见 `HomeCommonMenuStore` / 首页 `HomeView` 日志。

---

## 测试

单元测试：`suixinkanTests/HomeAllFunctionsBuilderTests.swift`

覆盖场景：

- menuList 白名单过滤
- API 顶层顺序保留（非 preferredOrder）
- 不展开子权限
- 精确 URI 拆分 common / more
- 常用区顺序跟随 `allFunctions`
- 与 Android 样例账号（27 顶层 → 21 可用 → 3 常用 + 18 更多）一致
