## 起因
用户问"历史留痕?看看有没有过时的和无用的东西可以删除" ——
**不该把"归档"当免检区**。逐项查证后:确认了 3 类该删的、1 类归错类的、1 个不能删的。
## 一、删除:被"顺手 git add"误带进无关提交的产物
用 `git log --diff-filter=A` 追来历,发现它们**根本不是有意提交的**:
| 文件 | 来历 | 判据 |
|---|---|---|
| `换电柜用锂电池充电器1TN标准通信协议 - RS485 V1.0(1).pdf` | `652bde4`(nginx 路由修复) | 与 `docs/hardware/1TN协议标准_苏州翌工电源_V1.0.pdf` **MD5 完全相同**(`B723A1FC…`)—— 纯重复 |
| `package.json` + `package-lock.json` | `652bde4`(同上) | 唯一依赖 `html-docx-js`,**全仓无任何脚本引用**(当次把合同 md 转 docx 的一次性工具残留) |
| `9b0d3b8a…jpg` / `d6e6add1…png` / `ea4410ba…jpg` | `8bc7812`("23 个函数拆分")/ `999885c`(协议对齐) | **零引用**,且混在与图片毫无关系的重构提交里 —— 误提交的截图 |
## 二、归错类(**不是过时,所以不删**):`开发文档.md` → `hardware/硬件与系统设计文档.md`
原本被塞进 `archive/`,但通读目录后确认它含**硬件设计内容**,这些在 `AGENTS.md`/`docs/tech/` 里**没有**:
硬件架构、仓控/柜控主板设计、指示灯状态机、充电控制逻辑(握手/双模式/动态调整/无协议模式)、
BMS 协议适配、安全防护联动、性能指标、第三方服务配置。
⇒ 它是**归错类而非过时**:**硬件部分仍是权威参考**,只有第 7 章(软件平台)被现行文档取代。
已在 `docs/README.md` 写明"两者冲突时软件口径以 `AGENTS.md` 为准"。
## 三、删除:两份 2026-07-06 报告(**问题已全部解决**)
逐条核对了它们"发现的问题",**无一条仍然成立**:
| 报告 | 当时的问题 | 现状 |
|---|---|---|
| 深度分析报告 | 敏感信息泄露(AGENTS.md 明文密码) | 已成 `AGENTS.md` 的**明示豁免**条目 |
| | 目录名不一致(可能用 taro,需确认) | 已确认 `h5-uniapp`,无 taro |
| | `test/` 目录缺失 | `test/` 已存在且有 7 项验收脚本 |
| | 温度单位不一致(K×100 vs 0.1°C) | 已统一为 K×100 |
| 数据单位审查报告 | SOC/SOH 单位标注错误 | `API.md` 早已重写;该报告基准 `tcp-protocol.md` 已不存在 |
## 四、**不删**:`生产环境.txt`(并修掉自相矛盾的表述)
它是**唯一凭据清单**(阿里云账号/小程序 AppSecret/腾讯地图 key/SSL AccessKey 都在其中),
且被 `docs/tech/小程序用户端.md` 明确引用为"密钥记录处"。
但原文写着"仅记录在内网 Git 仓库 `生产环境.txt`,**不入代码仓库**"—— **与事实矛盾**
(`git ls-files` 实测它**已入库**)。已改为准确表述:只记录在该文件、**绝不写进任何源码/.env**、
**不要删除**(新凭据追加到那里,不要散落别处)。
> 🔐 **留给你的决定**:该文件含大量明文凭据。现策略与 `AGENTS.md`「明文密码」豁免一致(内网仓库),
> 若要收紧,建议把它加入 `.gitignore` 并另存到密码管理器 —— 但这会改变 agent 的直连方式,故未擅自改。
## 五、取消 `archive/` 目录本身
**不再设归档**。理由:被删内容在 **git 历史里完整可查**(`git log -- <原路径>`),
归档的"留痕"价值因此为零,却持续制造"这文件还算不算依据"的歧义。
已把该结论写进 `docs/README.md` 的维护约定:**过时就删;仍有价值的归到正确的类。**
## 六、其他归位
- `营业执照.jpg`(主体资质,小程序/备案用)→ `docs/contract/`
- 根目录现在只剩 5 个必要文件:`.gitignore` / `AGENTS.md` / `README.md` / `opencode.json` / `生产环境.txt`
## 校验
- docs 相对链接 **0 断链**(搬迁引入的 2 处已在上一提交修好,本次复检通过);
- 已删除文件的残留引用:**仅剩 `docs/README.md` 的处置记录/映射表**(旧名必须出现,属预期);
- `cargo check --all-targets` 两服务零 error/warning(本次只动文档与仓库清理,未碰源码逻辑)。
11 KiB
微信小程序用户端(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 改动
- 设备卡片头部: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 禁用)—— 只是卡片去掉了,别理解成"全平台都去掉了"。 - 通道统计:图标+数字一行排列,图标用
<svg>内联 Lucide(不加容器,flex 居中) - 右上角扫码:scan-line 图标(Lucide),灰色 #6b7280,居中垂直对齐
状态位展示约定(状态位按归属层级展示)
以下三条是协议 §3.8 / §4.2 / §5.7 的既定结论(产品已确认),后续不要再反复确认。
- 柜级状态位 → 显示在"柜子"层面
- 数据:⚠️ 这两个名字不是 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):
status1bit0 断电|bit1 烟雾|bit2 水位|bit3 倾倒|bit4 柜门打开| bit5 加热风扇|bit6 接触器断开|bit7 喷淋;status2bit0 声光报警|bit1 柜门打开超时 (阈值硬件定义,平台只呈现、不自行判定);status2bit2–7 为保留位,不展示。 - 页面:PC
web/src/pages/devices/cabinet.tsx、小程序h5-uniapp/src/pages/device-detail/index.vue。 - 取不到返回
null(未知),绝不落"00"——"00"是"全部正常"的有效值。
- 数据:⚠️ 这两个名字不是 DB 列(实测
- 仓体级状态位 → 显示在"仓体"层面
- 数据:通道 18 字符 hex 的
batt_status(byte0) /pow_status(byte1);保留位 (batt_statusbit2–3、pow_statusbit3–6)不展示。 - 页面:PC 柜子详情里的通道卡片、小程序
pages/compartment-detail/index.vue。 - 仓体层面不显示任何"门"状态:协议没有"按仓体(舱门)开门"的指令(§4.2 L403)。
- 数据:通道 18 字符 hex 的
- 柜门是柜级独立动作,不与业务联动,全平台只有一个开门按钮
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是历史误用的 不存在权限码,禁止再用)。
- 解析函数两端必须同源同名:
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 断言两端一致)。 cabinets.status是「启用/禁用」配置态,不表示在线 ——1启用 /2禁用(0是建表默认值 产生的取值,代码不显式写它,语义等同启用);实时在线一律以 Redisdevice:{imei}.online为准 (唯一权威判据routes/cabinet_tree.rs::device_online_flag,接口字段is_online),energy_stats的online_count、H5 首页online_cabinets、PC/小程序的「在线/离线」全部消费它 (CABINET_STATUS_MAP只保留启用/禁用两档,不要再出现"离线"档)。
开发约束(uni-app 特定)
<text>不支持display: flex— uni-app 的<text>组件是纯文本组件,不接受 flex 布局。需要 flex 的地方用<view>- SVG 图标居中:
.stat-chip svg { display: block; flex-shrink: 0 }用 CSS 统一控制尺寸,比内联属性更可靠 - 内联 SVG + v-if:Vue 3 支持在
<path>上用v-if条件渲染,适用于动态图标 - CSS 覆盖 SVG 尺寸:用
.xxx svg { width: Xpx; height: Xpx }统一控制图标大小,不在每个<svg>上写死