Files
suixinkan_ios_new/suixinkan/Features/Home/AllFunctions.md

212 lines
7.2 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.

# 「全部功能」页业务逻辑
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``photographer_report`(举报摄影师)、`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` drawableiOS 优先接口字段 + 本地图标兜底。
---
## 第四步:读取常用 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 更多)一致