Files
MailUI4Agents/docs/GUI-PLAN-HARMONY.md
JianFeeeee b806a05bfa 跨端: harmony 管理页(用户管理)+ P4c 壁纸上传入口 —— 「功能做全再交付」的两块
pi 的交付清单里缺的两块(`docs/GUI-PLAN-HARMONY.md` 原先把管理后台划在首版之外,
用户明确要求「功能做全再给我」之后收进来)。

标 `跨端:` 是因为判据落在 `client/electron/test/`(鸿蒙的判据目录一向量在那里),
代码本体全在 `client/harmony/`。

## 管理页(用户管理)

- `pages/AdminUsersPage.ets`:新建 / 编辑(显示名·角色·白名单)/ 启停 / 重置密码。
  排布照 `AdminUsersPage.tsx`,包括「受限」徽标口径(普通用户且白名单非空才显示)、
  最后登录缺席与空串都显示「从未登录」、管理员对白名单两项忽略。
- 入口在设置页底部,**仅管理员可见**(`role === 'admin'` 严格相等,与 `App.tsx` 同口径)。
  读不到身份时**不**显示也不报错(乐观放行会让每个普通用户看到点进去 403 的入口)。
- `api/AdminApi.ets` + `model/AdminUsers.ts`(纯逻辑,零 import ⇒ 判据能真跑)。
- 启停**只发 status 一个字段** —— 服务端是部分更新,多发字段会把显示名与白名单一起改掉。
- `model/Models.ets` 补管理端 DTO;`main_pages.json` 注册路由。

## P4c 壁纸上传

- `model/ImagePrep.ts`:阈值与两档策略(2560/0.85 → 1280/0.78,入口 20MB,压后上限 3.5MiB)。
  **一处有意不对齐 WebUI** 并写明理由:WebUI 卡 data-URL 长度(含 base64 膨胀),
  鸿蒙内存直传 ArrayBuffer,卡的是字节数。
- `common/BackgroundPicker.ets`:不设 / 预设 / 自定义图片 + 浓度与模糊滑杆。
  上传链:picker → 判可不可以 → 逐档按 desiredSize 解码压缩 → 上传 → **请页面以服务端为准重新同步**。
  失败**必带原因**(服务端 415/413 文案原样透出)。用户取消选图**不算失败**。
- `ApiClient.uploadBytes`:MultiFormData.data 收 ArrayBuffer(核了 SDK,since 11;本工程 23)
  ⇒ 内存直传,不需要 base64 也不需要临时文件。

## 两处真 bug(变异测试逼出来的,不是"新写坏的")

1. **压缩循环的第二档此前是死代码**:循环里的 break 与循环外那句 shouldRetryWithActual
   互相抵消 —— 把循环里那处改成 `if (true)`(永远只压一档)整套判据照样全绿。
   收成一处判定(overLimit),循环外只读结论,并加结构性判据(该函数在这条链上只许调用一次)。
2. **壁纸的模糊档一直是「只写不读」**(计划文档 §7.12 登记过):滑杆能拖、值能存、
   blurStyleFor 也写了,就是**没有调用点**,壁纸一点没糊。
   本次补上的调用点分两层:壁纸层 `.blur(px)` = **图片内容模糊**
   (与 WebUI 的 `filter: blur(var(--bg-blur))` 同一个量、同一个数,所以不需要映射表);
   而那张**材质档**映射表 `blurStyleFor` 也终于有了调用点(`navMaterialFor` 内部复用它)。
   `docs/HARMONY-ALIGN-PLAN.md` 的 §7.12 两行(材质 / 壁纸模糊度)已一并改准、不再互相矛盾。

## pi 复核后**改回来的**(这一笔里我自己犯的两处,都由 pi 抓出)

1. **导航条材质一度绑定到 `bg_blur`,`bg_blur=0` 时整个消失。**
   我把 `NavBar` 从固定档改成 `blurStyleFor(bgPlan.blurPx)`,而滑杆 `min: 0` 可达、
   `blurStyleFor(0) === 'NONE'` ⇒ 用户把壁纸调清晰时**导航条一点材质都没有**。
   而且它与本笔自己的论证**相反**:刚论证完"图片内容模糊"与"面板材质"是两个物理量,
   转头把面板材质接到壁纸模糊这个输入上。
   现在**分层**:`blurStyleFor` 是通用映射(**允许** NONE —— "0 px 不模糊"是它的正确语义);
   `navMaterialFor` 是**导航条专用、有下限**的入口(0 px ⇒ 最薄档)。
   判据钉**可达性**(滑杆 0..40 每个整数 + 界外值都不许 NONE,且三档都要出现 ——
   否则"恒定最薄档"会让滑杆成为死控件)。
2. **`Theme.navMaterial` 被我弄成了死令牌**,而看着它的判据**照样绿**
   (那条只断言"声明存在且不是 NONE" —— 守的是声明,坏的是活的调用路径)。
   现在导航条真的用它;并把同文件里**只覆盖 `Theme.overlay` 一个令牌**的死令牌规则
   **铺到 Theme 的全部 35 个令牌**(量**外部引用数**:只被 Theme 内部方法读、
   而那个方法自己有外部调用点 ⇒ 不算死 —— `chipSpentBg` 是这种;`navMaterial` 当时
   唯一的消费者是一张可整体删掉的局部表,所以必须被抓)。

## pi 复核后**补上的**(这一笔漏掉的接线,都是我造成的)

- **`test/run-all.mjs` 的 SUITE 没接两个新判据文件** ⇒ HEAD 上 `npm test`
  **一条判据都不跑、直接 exit 1**(套件自检 2 就是为这件事写的)。已接入,
  并把两条登记进 `STATIC_ONLY`(`.ets` 要设备 ⇒ 静态欠账)。
- **`debt-visibility` 是我自伤**:那两个新文件里有 5 处"边界声明"但一次都没登记。
  我当时报"2 条失败是改动前就红" —— **只对一半**:这条在父提交上是**绿的**。
  我那次 `git stash push -u -- client/harmony` 的对照是**无效对照**
  (`-- client/harmony` 把 `client/electron/test/` 整个排除在外,新判据文件根本没被 stash),
  所以两次跑都红、看着像"既有"。已按 pi 的建议改用 `git worktree` 到父提交做对照。
  现在两处都登记进 `docs/DEBTS.json`(含 `static-criteria` 5→7,Go 侧同一份登记同步改)。

## 一并修正的旧判据(都是"太宽/太窄/钉错东西",不是放宽标准)

- 「模糊归属」:原文「壁纸层不许有**任何**模糊调用」把**图片内容模糊**与**面板材质**
  混为一谈(WebUI 侧核实:`.app-backdrop` 的 filter 与它之上那层的 backdrop-filter
  是两个不同的量)⇒ 改成按两种模糊分别钉。
- 「bgBlur 只写不读,消费侧必须为 0」:值不再成立,**形状保留**(逐文件登记 + 计数 + 理由),
  标题与断言里的假话一并改掉。
- isDarkMode 那条 `/dark\s*\)/` 断的是**参数顺序**(加一个入参就误红)⇒ 改成"dark 在实参里"。
- 三条钉 `backgroundBlurStyle` **整条字面表达式**的断言 ⇒ 改成钉语义
  ("用系统材质 + 材质有下限"),不再匹配那一行的字符。

## 判据

新增 `harmony-admin.test.mjs`(22 条)、`harmony-imageprep.test.mjs`(29 条);
`harmony-presets.test.mjs` 加 1 条(模糊档搬运与归一,含 `-0` 那个洞:
`Math.round(-0.4)` 是 `-0` 而 `-0 < 0` 为 false ⇒ 改成判 `!(r > 0)`)。

**`node test/run-all.mjs`:22 个判据文件全部跑起来**,红的只有 1 个:
`build-stamp`(`dist` 是 `a5fc86b` 上构建的,`gitRev` 对不上当前 HEAD)。
这条**不是我的代码造成的**(可证:`a5fc86b..HEAD` 之间,`srcHash` 覆盖的那批文件
——`client/electron/src` 等——**一个都没动过**,所以 `srcHash` 没变,差的是 `gitRev`),
但也**不是"改动前就红"**:任何推进 HEAD 的提交都会让它变红,正确修法是重构建。

## 未验(如实标注)

- **本机无设备/无模拟器 ⇒ 全部观感未验**:管理页排版与卡片观感、滑杆手感、
  模糊在真机上的实际档位观感、系统材质在自绘悬浮条上的实际效果。代码齐 ≠ 真机验过。
- 预设档**没有**上模糊(壁纸在预设档下是一叠自绘矩形,系统材质对它不生效)——
  这是我**主动收的范围**,不是漏,真机看一眼再决定要不要补。
- **Go 侧的 `debt_registry_test.go` 我没能跑**(沙箱里没有 Go 模块缓存,`go test` 起不来),
  只做了 `gofmt` 校验;那处改动是一行 `Count: 5 → 7`。
2026-09-15 11:17:23 +08:00

229 lines
14 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.

# AgentMail 鸿蒙客户端(ArkUI)构筑计划
> 邮件驱动·多智能体协作平台 — HarmonyOS 客户端
> 构筑者:dsh | 版本:v0.1 | 日期:2026-09-07
> 上游接口契约:[docs/API.md](API.md) · 平台规划:[docs/PLAN.md](PLAN.md)
---
## 0. 定位与分工
- jianf(发起人)→ 收件方 pi 负责 **ele 客户端**(Win/Linux),并作为长期维护者给我方提供 API 解读与支持;
- dsh(我)→ 负责 **鸿蒙客户端**(ArkUI),本计划即鸿蒙侧的构筑蓝图。
- 平台后端是 **同一个 Gateway**(Go + SQLite,HTTP REST + SSE),鸿蒙客户端调用其**人类接口**
(`/api/v1/me/*` 等),与 WebUI 完全等价,没有私有通道。
- 交付形态:一套 Stage 模型 ArkTS 工程,以 HAP 形式运行于 HarmonyOS 手机/平板/2in1,
覆盖 WebUI 的「邮件收发 + 会话 + 联系人 + 权限决策」核心功能面。
**本机工具链(已勘察确认)**:
| 组件 | 版本/位置 |
|---|---|
| DevEco CLI | 26.0.0 Beta2,`/opt/huawei/command-line-tools`(`devecocli` 1.3.0 已包装在 `/usr/local/bin`) |
| HarmonyOS SDK | 6.1.0(API 23,Version 6.1.0.105 Release),`/opt/huawei/harmonyos/ohos-sdk/linux` |
| 包管理 | `ohpm`(command-line-tools/ohpm) |
| 模拟器 | `HarmonyPhone`(phone / HarmonyOS 6.1.1(24),KVM 加速,按需 `harmony-emu start`) |
| 真机 | 可选:hdc 无线调试(动态 IP,签名 hap 走 `devecocli signature generate`,材料在 `~/.ohos/config`) |
**鸿蒙生态约束(真机安装必须签名)**:模拟器可装 unsigned hap;真机一律要签名。
签名材料已就绪,`devecocli signature generate --force` 可随时重新生成。
---
## 1. 目标功能面(与 WebUI 对齐的裁剪集)
首版(M1-M3)做「个人邮件客户端」核心闭环,管理员面(用户管理/Agent 密钥/默认预算)不在鸿蒙侧首版
范围内 —— 那是 Web 管理台的职责,鸿蒙端聚焦日常使用。
| 模块 | 功能 | 对应 API |
|---|---|---|
| 登录/凭证 | 账号密码登录;用户密钥登录(跨设备场景,Bearer) | `POST /auth/login`、`GET /auth/me`、`POST /auth/logout`、`POST /me/keys` |
| 收件箱/发件箱 | 未读/全部列表、未读标记、发件箱 | `GET /me/mail/inbox?s=unread\|all`、`GET /me/mail/sent` |
| 会话列表 | 我参与的会话(卡片/列表视图)、未读数、预算徽标 | `GET /me/sessions`、`GET /contacts`、`GET /contacts/suggest` |
| 邮件详情 | Markdown 正文渲染、附件清单、抄送显示、对话树 | `GET /mail/{id}`、`GET /mail/{id}/thread` |
| 写信 | 收件人三段式补全、抄送、正文、往返预算、附件上传、会话别名(`.new` 时) | `POST /me/mail/send`、`POST /me/attachments` |
| 回复/转发 | 回复落回原会话、转发开新线索(附件随行) | `POST /me/mail/{id}/forward` |
| 权限决策 | 待决请求列表、同意/拒绝/备注 | `GET /permission/pending`、`POST /permission/decide` |
| 实时推送 | 新邮件、权限决策、会话更新(SSE) | `GET /events/stream?access_token=` |
| 会话管理 | 改名、预算调整、**权限档位调整(plan / workspace / full)**、改名提议展示/接受/驳回、归档 | `PUT /sessions/{id}/alias`、`GET\|PUT /sessions/{id}/budget`、`PUT /sessions/{id}/permission`、`rename-proposal` 组、`POST /contacts/archive` |
| 权限档位 | 会话头部展示当前档位与强制力(`permission_mode` / `permission_enforcement`),可随时调整 | 见会话管理行;档位三值 `plan / workspace / full` |
| 附件 | 下载(保存到本地/分享) | `GET /me/attachments/{id}` |
明确**不做**(首版):管理员后台、Agent 侧接口、日历(`/calendar/*`,Web 端新功能,待 api 解读后二版评估)。
---
## 2. 技术方案
### 2.1 工程形态
- Stage 模型 + `@ohos/http`(默认网络库,无需三方依赖),`entry` 单模块起步;
若 UI 面膨胀再拆 `common`/`feature` 多模块,首版不预先拆。
- 语言:**纯 ArkTS**(严格模式,`arkts-*` lint 全开),不用 JS/TS 混写。
- 状态管理:首版用组件内 `@State`/`@Link`/`@Provide` 即可;跨页共享(登录态、token、未读数)
提炼为**单例 Service 层**(`@ohos.data.preferences` 持久化凭证),不引第三方状态库。
### 2.2 API 层(核心设计)
对齐 WebUI 的 `src/api/` 思路,鸿蒙侧实现一份同构的客户端:
```
entry/src/main/ets/
├── ability/ EntryAbility.ets
├── pages/ 登录 / 主界面 / 会话 / 邮件详情 / 写信 / 权限
├── api/
│ ├── ApiClient.ets # 统一 request():base + headers + 错误归一化(400/401/403/404/409/429)
│ ├── AuthApi.ets # login / logout / me / keys
│ ├── MailApi.ets # inbox / sent / detail / thread / send / forward / read
│ ├── SessionApi.ets # sessions / alias / budget / rename-proposal
│ ├── ContactApi.ets # contacts / suggest / archive
│ ├── PermissionApi.ets # pending / decide
│ ├── AttachmentApi.ets # upload / download
│ └── SseClient.ets # SSE 流式解析 + 指数退避重连
├── model/ # 领域模型(Mail/Session/Contact/…,与 API.md 字段一一对应)
├── store/ # 单例:SessionStore / MailStore / UnreadStore(跨页共享)
├── components/ # MailListItem / SessionCard / AddressInput / PermissionCard / MarkdownView …
└── common/ # 常量(BASE_URL、事件名)、工具(相对时间、rune 截断、Markdown 渲染)
```
要点:
- **基地址与凭证**集中在 `common/config`:`apiBase` 可运行时配置(设置页填写网关地址),
与 WebUI 的 `window.__AGENTMAIL_API_BASE__` 同思路 —— 一个构建产物可指向任意后端。
- **认证双通道**:Cookie(账号密码登录,`@ohos.http` 手动管理 Cookie)与
`Authorization: Bearer <user_key>`(密钥登录)。SSE 与附件下载这两个浏览器侧只能走 query 令牌的端点,
鸿蒙侧可以带请求头,优先 Bearer,避免令牌进 URL 日志。
- **错误归一化**:统一把服务端 `{"error": "中文可操作描述"}` 与 HTTP 状态码翻译成
`ApiError { code, message }`;401 统一触发「回登录页」;429/403 文案原样上屏。
### 2.3 SSE 实时(关键难点)
`@ohos.http` 的流式响应(`on('dataReceive')`)用于解析 SSE 帧:
- 手工按 `\n\n` 分帧,解析 `event:` / `data:` 行;事件表与 API.md 一致:
`new_mail` / `permission_decision` / `session_update` / `session_archived` / `agent_online`。
(pi 梳理补充:`new_mail` 与 `session_update` 事件还带 `session_alias` /
`permission_mode` / `permission_enforcement` 字段 —— 鸿蒙端模型层按此定义,
会话卡片同时显示档位徽标。)
- **断线重连**:指数退避 1s→15s 上限(对齐 Web 端 `api/sse.ts`),`AbortController` 优雅停止。
- `new_mail` 到达 → 通知 MailStore 刷新收件箱与未读数;`permission_decision` → 刷新权限卡片。
- 生命周期:App 前台订阅、后台/退到 `onBackground` 暂停,回到前台重连(省电 + 避免无谓重连风暴)。
### 2.4 Markdown 渲染
鸿蒙没有现成 react-markdown。选择(按优先级):
1. **首版:轻量自研解析子集** —— 标题/粗体/斜体/行内代码/代码块/列表/引用/链接/分割线,
用 `Span`/`RichText` 逐段渲染;**raw HTML 一律不渲染**、链接协议白名单(http/https),
守住 Web 端 Markdown XSS 回归测试立下的同一条纪律(见 PLAN.md 6.4)。
2. 若正文复杂度超预期,评估 `@ohos/webview` 加载本地渲染页(需白名单校验后再注入,引入 XSS 面,慎用)。
### 2.5 附件
- 上传:`@ohos.request` / `http` multipart 到 `POST /me/attachments`(字段名 file),
返回 `attachment_id` 后随 `send` 的 `attachment_ids` 使用 —— 两步流程与 Web 端一致。
- 下载:`GET /me/attachments/{id}` → 写入应用沙箱文件目录,用 `@ohos.file.picker` /
`@ohos.share` 交出去(保存到相册/分享)。注意服务端**强制 octet-stream + attachment**,
按响应头 `Content-Disposition` 解析文件名(API.md 七、已暴露该响应头)。
### 2.6 UI 布局(ArkUI)
| 界面 | 结构 |
|---|---|
| 登录页 | 服务器地址 + 账号/密码 或 用户密钥;「保存地址」入 preferences |
| 主界面(手机) | 底部 Tab:收件 / 会话 / 联系人 / 权限 / 我的;顶栏未读徽标 |
| 主界面(平板/2in1) | 两栏/三栏自适应:左列表 + 右详情;`@ohos.mediaquery` 断点切换 |
| 会话列表 | 列表/卡片视图切换;卡片含主题、最新一封发件人+摘要、预算徽标(剩 1 橙 / 用尽红 / 不限不显示) |
| 邮件详情 | Markdown 正文 + 附件行 + 抄送行 + 权限卡片 + 底部「回复 / 转发」+ 对话树入口 |
| 对话树 | 缩进 + 连接线(不引图形库),分块加载:`dir=around` 首屏 + `up/down` 增量 |
| 写信页 | 收件人补全(三段式 suggest)+ 抄送 + 主题 + 正文 + 预算档位 + 附件 + 会话别名(仅 `.new`) |
| 设置 | 网关地址、退出登录、密钥管理(`/me/keys` 创建/吊销,展示 token_hint) |
窄屏三维地址补全:`/contacts/suggest` 无参 → name;带 name → path;带 name+path → 别名+`new`,
逐级展开,方向键选择(对齐 Web 端 AddressInput 交互)。
---
## 3. ArkTS 工程注意点(本机实操约束)
在写第一行 `.ets` 前先加载 `arkts-grammar-standards` 技能,按规范落地:
- 严格模式:禁 `any`、禁未声明类型、`undefined` 处理显式化(`??` / optional chain 白名单内)。
- 组件用 `struct` + `@Component`,状态用 `@State`/`@Prop`/`@Provide`;列表用 `LazyForEach`
(邮件量可能上百,`ForEach` 全量渲染会卡)。
- 网络模型用 `interface` 描述(与 API.md 字段对齐),服务端多余字段不枚举。
- Sendable / Actor 需求确认:首版无跨线程共享大对象,先不进;若 SSE 解析放 worker 再评估。
- `@ohos.http` 需在 `module.json5` 声明 `ohos.permission.INTERNET`(示例项目已带,照抄)。
- 构建:`devecocli build` / `hvigorw assembleHap` 产出 `entry-default-signed.hap`;
模拟器 `hdc install`(unsigned 亦可),真机走签名 hap + `hdc tconn`。
---
## 4. 里程碑与验收
### M0 脚手架(0.5 天)
- `devecocli create` 建 Stage 工程(包名原为 `com.agentmail.harmony`,**2026-09-14 已改为 `com.jianf.agentmail`**:AGC 实测拒绝含保留字 `harmony` 的包名,用户定新名;AGC 应用 APP ID `6917616450599975320`。包名必须与 AGC **完全一致**,否则 Push Kit 推不到设备),API 23;
- 空壳 App 在 HarmonyPhone 模拟器跑通(`harmony-emu start` → build → install → 截图);
- 签名配置写入 `build-profile.json5`(复用 `~/.ohos/config` 材料)。
**验收**:启动图标进桌面,`devecocli ui screenshot` 可见主页面。
### M1 API 层 + 登录(1-2 天)
- `ApiClient`(Base + 错误归一化 + 双认证通道)+ `AuthApi`;
- 登录页(账号密码 / 用户密钥)+ token/凭证持久化 + 401 全局回落登录。
**验收**:连本机 Gateway(`http://localhost:8180`)用真实账号登录成功,
`GET /auth/me` 返回当前用户;错密码显示服务端中文文案。
### M2 邮件闭环(3-5 天)
- 收件箱/发件箱列表(未读态、分页)、邮件详情(Markdown 渲染 + 附件行 + 抄送)、
回复/转发、写信页(三段式补全 + 预算档位 + 附件上传两步流 + `.new` 会话别名)。
**验收**:人 → Agent → 人 端到端:给 pi/opencode 发一封带抄送与附件的任务邮件,
Agent 回信后在本端看到详情并可继续回复;新信未读计数正确。
### M3 实时 + 会话 + 权限(2-3 天)
- `SseClient`(解析 + 退避重连 + 生命周期);未读徽标与列表随事件刷新;
- 会话列表(列表/卡片)、改名提议条(接受/驳回)、预算调整、**权限档位调整与档位徽标**;
- 权限待决列表 + 决策卡片。
**验收**:另一浏览器标签给本账号发信,鸿蒙端 ≤3s 内未读 +1(SSE 路径);
Agent 权限请求出现在待决列表,决策后 Agent 侧收到(对照 Web 端行为一致)。
### M4 对话树 + 附件落盘 + 打磨(2-3 天)
- 对话树分块加载(`around/up/down` + 滚动位置补偿);
- 附件下载到沙箱 → picker/share 导出;平板/2in1 自适应布局、深色模式(如有余力)。
**验收**:251 封长链线索在鸿蒙端分页取完、depth 连续;真机(若可用)安装签名 hap 全流程跑通。
### M5 联调 + 文档(1-2 天)
- 与 ele 端(pi)在**同一 Gateway** 上双端并行联调:双端同账号互斥/一致性问题暴露;
- 对照 `PLUGIN-CONTRACT.md` 的验收清单做客户端视角补测;
- 更新本计划为「已落地」,同步 README 接入说明。
**预计总工时**:约 2-3 周(含联调与返工缓冲)。
---
## 5. 风险与依赖
| 风险 | 对策 |
|---|---|
| `@ohos.http` 流式 SSE 稳定性(长连接、断流) | 退避重连 + 生命周期暂停;降级方案:轮询 `/me/mail/inbox`(SSE 仅作加速) |
| ArkTS 严格模式对三方库不友好 | 尽量零三方依赖;网络/存储/文件全走系统 API |
| Markdown 渲染面 | 首版渲染子集 + 协议白名单;复杂稿需时评估 webview 方案并单独过 XSS 评审 |
| 真机签名链路 | 材料已就绪;`devecocli signature generate --force` 兜底;无真机时模拟器验收为主 |
| 服务端接口变更 | 依赖方(pi)的 API 解读;所有模型层集中在 `model/`,变更只动一处 |
| 日历等 Web 新功能 | 首版明确排除,M5 后按 API 解读评估二版 |
**需要 jianf / pi 提供的支持**:
1. 一个稳定的测试 Gateway 地址(开发联调用,含可登录账号);
2. 接口变化时按 `docs/API.md` 格式及时更新 —— 鸿蒙端模型层严格对齐该文档。
---
## 6. 与 ele 端(pi)的协同点
- 两端共用**同一份 API.md 契约**,建议把「API 解读」沉淀为 `docs/API-INTERPRETATION.md`
(字段语义、边界行为、踩坑),供双端引用,避免各自重复踩;
- 后端新增字段时(如日历),双端各自按需跟进;鸿蒙端涉及协议层的问题由 pi 提供解读支持(按分工)。