charging-cabinet/docs/tech/小程序用户端.md
12451 401f84bda6 chore: 清掉误入库的 agent 工具记忆目录与 Vite 模板 README(内容先按主题归位)
## 起因

用户问"现在确定所有文档都是按分类存放没重复和过期内容了吧" ——
**不能凭感觉答"是"**,于是做了一次针对**已跟踪文件**的体检(重复/断链/过期陈述三维度)。
第一轮体检结果:重复 0 组、断链 0 处,但**又扫出 3 处该处理的东西**。

## 一、`.workbuddy/memory/` —— 另一个 agent 工具的记忆目录被误入库

3 个文件(`MEMORY.md` / `2026-07-02.md` / `2026-07-07.md`),**全仓零引用**,
最后一次"提交"是搭车进 `feat(protocol): TCP v1.1 协议对齐` 这类无关提交 ——
与之前那几张误提交的图片同一模式。

- ⚠️ `2026-07-02.md` 里含一处**生图 API Key 片段**(`sk-ib7i...M2z`);
- `.gitignore` 里 `.codebuddy/` 早有,但 `.workbuddy/` **漏了** ⇒ 才被顺手提交进来。

### 处理:内容先归位,再删目录(不是一删了之)

按 `docs/README.md` 已确立的原则"**过时就删;仍有价值的归到正确的类**",
把 `MEMORY.md` 里仍有价值的三类内容分别落位:

| 内容 | 归到哪 | 为什么 |
|---|---|---|
| UI 颜色系统 / 背景容器 / 36 仓通道卡布局 | **`docs/tech/小程序用户端.md`** 新增「附:UI 设计规范(视觉口径,改动界面前必读)」 | 这是页面视觉口径,属小程序文档 |
| Ardot `batch_edit` 的 `fills`/`strokes`/`cornerRadius` 写法坑 + 设计稿 7 屏 ID | **`docs/compound-notes/2026-07-ardot-batch-edit-属性写法.md`** | 工具特有的单点教训,正是 compound-notes 的定义 |
| "开发前必须确认需求"、"生产环境须授权"、落地页明亮风格偏好 | **`AGENTS.md` 新增「操作规则」小节** | 项目级规则,须常驻加载 |

然后把 `.workbuddy/` 从版本控制移除并**加入 `.gitignore`**(连同 `.opencode/`、`.codegraph/`),
堵住同一个坑再犯。

## 二、`software/web/README.md` —— 建站模板残留

23 行里 9 行是 Vite/plugin-react/Oxlint 的模板话术(英文、与项目无关)。
`api-server` / `device-server` / `h5-uniapp` 三个工程**都没有** README —— 构建方式统一在
`AGENTS.md` 与 `docs/tech/部署手册.md` 里。⇒ **删除**。

## 三、一个我自己的误判(已纠正,记录以免后人重复)

我一度怀疑 `docs/tech/API.md` 接口表"少 12 行"(我数出 68 行,而 `mod.rs` 有 80 个 `.route(`)。
复核发现 **`API.md` 自己写明了计数约定**:"一行 = 一次 `.route(...)` 注册,共 80 行;
展开后 93 对" —— 而我的正则 `^\|\s*(GET|POST)\s*\|` **漏掉了 `| GET / POST |` 这种链式写法**。
用能匹配链式的正则重数 = **正好 80** ⇒ **`API.md` 是准确的,我的怀疑是错的**。

## 校验(只扫 git 已跟踪文件)

- 已跟踪文件 **217**(原 221 → 删 3 个工具记忆 + 1 个模板 README);
- 文档内容重复:**0 组**;
- 已跟踪 md 的本地链接:**0 断链**;
- `.workbuddy` 已跟踪文件数 = 0、工作区已不存在。

## 仍**刻意保留**的(不是遗漏)

- `生产环境.txt`:**唯一凭据清单**且被 `docs/tech/小程序用户端.md` 引用为密钥记录处 ——
  上一提交已把"不入代码仓库"这句与事实矛盾的表述改准;是否改走 `.gitignore`+密码管理器由你定。
- `docs/README.md` 的「旧名→现名映射」表 / 处置记录 / 自证方法:**必须**出现旧名,属预期。
- `docs/compound-notes/TEMPLATE.md`(94 B):新建笔记的模板,给该目录提供格式约束。
- `tasks/002-未结待办-*.md`:两项未结(`energy` 等硬件答复、长函数重构),
  `tasks/` 本身已 gitignore、不入库。
2026-09-20 05:04:53 +08:00

12 KiB
Raw Permalink Blame History

微信小程序用户端(AGENTS.md 的详细参考篇)

本文件由 AGENTS.md 的「微信小程序用户端」整段搬迁而来(内容逐字保留,未删改)。 页面清单、构建/上传流程、状态位与 RSSI 的分层展示口径都在这里。 门禁命令(vue-tsc 类型检查 + build:mp-weixin)与「产物不上传服务器」已留在 AGENTS.md。


微信小程序用户端

小程序配置

参数 值
AppID wx226061c550c52e85
AppSecret f11dfee9dc73265b75bf54277ebb1047
账号 13951516272@163.com
消息加解密方式 明文模式
数据格式 XML
服务器地址(URL) https://app.anzhizhichong.com/api/wechat/callback
Token(令牌) 7GW16XdnFyak0mE32rc8ZPij
EncodingAESKey kRijggSqeCFWi3JhfGyK0nzc6VmdygWWkoqyKPl0bYu

⚠️ 以上密钥只记录在仓库根目录的 生产环境.txt(内网 Gitea,明文密码见 AGENTS.md「代码审查豁免」)—— 绝不写进任何源码、.env 或配置文件。 该文件是唯一凭据清单(阿里云/小程序 AppSecret/腾讯地图/SSL AccessKey 都在其中), 不要删除;需要新凭据时追加到那里,不要散落在别处。

构建命令

cd software/h5-uniapp
npm run build:mp-weixin
# 产物在 dist/build/mp-weixin/,用微信开发者工具导入上传

API 域名配置

小程序请求后端 API 必须配置合法域名(HTTPS):

  • 后台「开发管理 → 开发设置 → 服务器域名」
  • request 合法域名:https://app.anzhizhichong.com
  • 开发阶段可在 manifest.json 设 urlCheck: false 跳过校验

API base URL

小程序用全 URL(api/index.ts 条件编译):

// #ifdef MP-WEIXIN
const BASE_URL = 'https://app.anzhizhichong.com/api/pms'
// #endif
// #ifndef MP-WEIXIN
const BASE_URL = '/api/pms'
// #endif

信号强度显示(RSSI)

设备卡片右上角显示 RSSI 信号强度。

✅ 2026-09 已收敛为「两端同源同名、统一 5 档」(此前两端不一致,见下方"行为变化")。 唯一权威实现:web/src/utils/deviceStatus.ts 与 h5-uniapp/src/utils/deviceStatus.ts 的 rssiSignal() / rssiBarColors() / RSSI_TIERS(两端逐字一致,改一边必须同步另一边)。 一致性用例:node test/rssi_signal.test.mjs(遍历 0–99 逐值断言两端 level/bars/color 相同)。 ⛔ 页面里不得再自己写一套阈值(历史上两端各写一套,就是不一致的根因)。

统一分档表(协议口径:rssi 是 0–99 的信号等级,不是 dBm)

阈值 level 颜色 5 格制格数 PC 图标亮的 <path>
≥ 70 4 strong #16a34a 绿 5 4 条全亮
≥ 55 3 good #2563eb 蓝 4 3 条
≥ 40 2 fair #f59e0b 黄 3 2 条
≥ 30 1 weak #f97316 橙 2 1 条
< 30 或 null 0 none #9ca3af 灰 1(底格) 0 条(只剩底部圆点 M2 20h.01)

两端如何消费同一份纯函数:

  • PC(web/src/pages/devices/CabinetGrid.tsx::SignalIconComponent):rssiSignal(rssi) 取 color 与 level, 4 条 <path> 按 level > i 决定亮几条;卡片头部数值颜色用同一个 color。
  • 小程序(home/index.vue、device-detail/index.vue): v-for="(color, i) in rssiBarColors(cab.rssi)" 渲染 bars 根 <view class="signal-bar">, 颜色内联 :style="{ background: color }";格数真的随档位变化。
  • 颜色只在 TS 里定义一次(不再有 .signal.good/.mid/.low/.off 这类 CSS 颜色规则; 页面 CSS 只负责尺寸/间距)。

后端(两端共用): 设备列表接口用 pipeline 批量 HGET device:{imei} rssi (routes/cabinet_tree.rs::attach_realtime_status,字段下标见 DEVICE_LIST_FIELDS); 小程序侧 routes/h5.rs::get_cabinets 同样带 Redis。

相关文件:

  • 纯函数(权威): software/web/src/utils/deviceStatus.ts、software/h5-uniapp/src/utils/deviceStatus.ts
  • PC 消费点: software/web/src/pages/devices/CabinetGrid.tsx(SignalIconComponent + 卡片头部)
  • 小程序消费点: software/h5-uniapp/src/pages/home/index.vue、pages/device-detail/index.vue
  • 类型: software/h5-uniapp/src/api/index.ts — CabinetItem.rssi
  • 一致性用例: test/rssi_signal.test.mjs

⚠️ 行为变化(2026-09,小程序端显示会变 —— 这是有意为之) 收敛前:PC 5 档(≥70 绿/≥55 蓝/≥40 黄/≥30 橙/<30 灰,格数随档位增删); 小程序只有 4 档(无 ≥70)、30–39 用红色、且恒渲染 5 格只改颜色。 收敛后小程序:新增 ≥70 档(绿/满格)、30–39 由红改橙、≥55 由绿改蓝、 离线灰由 #94a3b8 改为 #9ca3af,且格数随档位变化(不再恒 5 格)。 影响页面:小程序首页设备卡片、小程序设备详情页头部。

近期 UI 改动

  1. 设备卡片头部:PC 管理端(web/src/pages/devices/CabinetGrid.tsx)去掉"在线/离线"文字 badge, 改为信号图标 + RSSI 数值。 ✅ 小程序保留"在线/离线"文字 badge —— 这是有意差异,不是缺陷,不要当 bug 改掉: h5-uniapp/src/pages/home/index.vue:43(设备卡片,cab.status===1 ? '在线' : '离线')与 pages/device-detail/index.vue:18(设备详情;同文件 :74 还有板级的在线/离线 badge)。 理由(产品决定):PC 用"信号图标 + RSSI 数值"表达;小程序屏幕小、文字 badge 更直观。

    PC 卡片:图标 + 数值;小程序:文字 badge(有意保留)。 另外 PC 设备详情页(web/src/pages/devices/cabinet.tsx:503)也仍用 Tag 渲染 CABINET_STATUS_MAP(0 离线 / 1 在线 / 2 禁用)—— 只是卡片去掉了,别理解成"全平台都去掉了"。

  2. 通道统计:图标+数字一行排列,图标用 <svg> 内联 Lucide(不加容器,flex 居中)
  3. 右上角扫码:scan-line 图标(Lucide),灰色 #6b7280,居中垂直对齐

状态位展示约定(状态位按归属层级展示)

以下三条是协议 §3.8 / §4.2 / §5.7 的既定结论(产品已确认),后续不要再反复确认。

  1. 柜级状态位 → 显示在"柜子"层面
    • 数据:⚠️ 这两个名字不是 DB 列(实测 cabinets 表没有 status1/status2 列, 它的列是 id/project_id/abstract_id/imei/iccid/auth_str/name/address/status/created_at/updated_at/ latitude/longitude/work_mode/auth_str_issued/cell)。它们是: Redis 字段 device:{imei} 的 cabinet_status1 / cabinet_status2(设备上报原样落,2 位 hex) → 接口原样透出为响应字段 status1 / status2。 写:report_worker.rs::write_device_state;读:cabinet_tree.rs::attach_realtime_status(列表)、 cabinets.rs::fetch_cabinet_realtime(详情)。
    • 位定义(§3.8):status1 bit0 断电|bit1 烟雾|bit2 水位|bit3 倾倒|bit4 柜门打开| bit5 加热风扇|bit6 接触器断开|bit7 喷淋;status2 bit0 声光报警|bit1 柜门打开超时 (阈值硬件定义,平台只呈现、不自行判定);status2 bit2–7 为保留位,不展示。
    • 页面:PC web/src/pages/devices/cabinet.tsx、小程序 h5-uniapp/src/pages/device-detail/index.vue。
    • 取不到返回 null(未知),绝不落 "00" —— "00" 是"全部正常"的有效值。
  2. 仓体级状态位 → 显示在"仓体"层面
    • 数据:通道 18 字符 hex 的 batt_status(byte0) / pow_status(byte1);保留位 (batt_status bit2–3、pow_status bit3–6)不展示。
    • 页面:PC 柜子详情里的通道卡片、小程序 pages/compartment-detail/index.vue。
    • 仓体层面不显示任何"门"状态:协议没有"按仓体(舱门)开门"的指令(§4.2 L403)。
  3. 柜门是柜级独立动作,不与业务联动,全平台只有一个开门按钮
    • open 是柜级指令(不带 id),语义是**「柜门解锁,下发后由人拉开,门不会自动开」**; 没有"关门"指令(关门也是人为动作)。全平台只有一扇柜门,不按仓体区分。
    • 柜门状态永远来自 status_post(约 60 秒一次),不是实时量 —— 界面上必须标注 "来自最近一次上报(约 60 秒一次),非实时"(§4.2 L428)。
    • 开门是独立流程,与电池插入、充电开始/结束没有因果关系也没有先后要求(§5.7、§7.2): 不得出现在"开始充电"流程里、不得改任何订单状态、不得要求存在订单。
    • 入口只有一个:PC 详情页按钮(<Permission code="charge:open_door"> 包裹)+ 小程序设备详情页按钮;仓体(通道)详情页不放开门按钮。
    • 权限码 charge:open_door(后端 check_permission 硬校验;device:door:unlock 是历史误用的 不存在权限码,禁止再用)。
  4. 解析函数两端必须同源同名:web/src/utils/deviceStatus.ts 与 h5-uniapp/src/utils/deviceStatus.ts 各有一组 parseCabinetStatus1 / parseCabinetStatus2 / cabinetDoorState / cabinetStatusTags(注释互相指认对方文件,改一边务必同步另一边)。 端到端一致性用例见 test/device_status_parse.test.mjs(node test/device_status_parse.test.mjs, 逐位遍历 0x00–0xFF 断言两端一致)。
  5. cabinets.status 是「启用/禁用」配置态,不表示在线 —— 1 启用 / 2 禁用(0 是建表默认值 产生的取值,代码不显式写它,语义等同启用);实时在线一律以 Redis device:{imei}.online 为准 (唯一权威判据 routes/cabinet_tree.rs::device_online_flag,接口字段 is_online), energy_stats 的 online_count、H5 首页 online_cabinets、PC/小程序的「在线/离线」全部消费它 (CABINET_STATUS_MAP 只保留启用/禁用两档,不要再出现"离线"档)。

开发约束(uni-app 特定)

  1. <text> 不支持 display: flex — uni-app 的 <text> 组件是纯文本组件,不接受 flex 布局。需要 flex 的地方用 <view>
  2. SVG 图标居中:.stat-chip svg { display: block; flex-shrink: 0 } 用 CSS 统一控制尺寸,比内联属性更可靠
  3. 内联 SVG + v-if:Vue 3 支持在 <path> 上用 v-if 条件渲染,适用于动态图标
  4. CSS 覆盖 SVG 尺寸:用 .xxx svg { width: Xpx; height: Xpx } 统一控制图标大小,不在每个 <svg> 上写死

附:UI 设计规范(视觉口径,改动界面前必读)

来源:H5 设计稿(Ardot 文件 ID 701384244279693「安知充H5应用设计稿」,7 屏: 登录 / 首页 / 设备详情 / 设备列表 / 仓体详情 / 订单 / 我的)落地时沉淀的口径。 改动任何页面配色或布局前先对照本节,避免同一系统内出现两套视觉。

颜色系统(状态色 + 背景色成对使用)

语义 主色 浅底
主色(品牌蓝) #2563eb #eff6ff
在线 #16a34a #f0fdf4
离线 #9ca3af #f9fafb
故障 #dc2626 #fef2f2
停止 #f59e0b #fffbeb
充电中 #2563eb #eff6ff
满电 #7c3aed —

背景与容器

  • 页面底色统一 #f5f5f5(含 tab 页与子页面:设备详情 / 设备列表 / 仓体详情);
  • 登录页用渐变 linear-gradient(135deg, #f0f4ff, #e0e7ff, #f0f9ff);
  • 所有卡片/容器为白底 #FFFFFF + 圆角。

设备详情(36 仓)

  • 布局为 6 板 × 6 通道 = 36;通道卡片白底 + 顶部 3px 彩色状态条(色条取上表状态色)。