Commit f5014db6 authored by wenwen.tang's avatar wenwen.tang 😕

docs(memory): 记录访客按页授权(guest 仅电费通知单)+ 线上落地与验证

parent 35f0fbce
......@@ -417,3 +417,71 @@ env -u http_proxy -u https_proxy -u HTTP_PROXY -u HTTPS_PROXY git push origin ma
- ⚠️ `rentfree.windows()` 的返回键是 **`begin` / `end`**(不是 from/to),另有 `months`/`year`/`type`/`key`
- 本轮未 push/部署前已确认:`rentfree` 只作**标注**(`rent_preview` 的每行/每单据 `rentfree` + 前端「免租期内」标签),
**不参与 `billable` 判定** → 回填再多行也不会自动停单,风险可控
---
## 第十轮:访客账号按页授权(guest 仅「电费通知单」可操作)(10-13 15:5x)
需求:「访客账号 guest 密码 guest@2026,仅电费通知单加操作权限」。
### 权限模型:role + perms 能力清单
- 原来只有 `role`(admin / viewer 一刀切),现在账号与会话都多一个 **`perms`**:
`auth.CAPS = ("elec",)`,`CAP_LABELS = {"elec": "电费通知单"}`
- `auth.normalize_perms(perms, role)` / `caps(account)` / `can(account, cap)`:
管理员 → `{'*'}`(隐含全部,且强制清空 perms);viewer → 只留认识的键;未知角色 → 空集
- 写闸门从「非 admin 一律 403」改成 **逐接口解析所需能力**:
`server._write_cap(method, path)` + `_guard_write(user, cap)`
- **电费**:`POST /api/batches`(录明细出单)、`POST /api/attachments`、
`DELETE /api/attachments/{id}`、`POST /api/batches/{id}/(rerender|send)`、`DELETE /api/batches/{id}`
- **仅管理员**:其余全部(房租出单 / 收款登记 / 排期触发 / 台账 / 公司 / 配置 / 账号 / 改密码)
- ⚠️ **批次类接口再按批次 `kind` 复核** —— 房租车位费批次的删/重出/发送/收款,
即便账号有 elec 能力也 403(`_batch_kind()` 读不到批次时按 elec 处理)
- 403 文案会说明范围:「账号「guest」只能操作:电费通知单」
- 会话/登录/`/api/session` 都带上 `perms` + `cap_labels`;`/api/accounts` 下发 perms;
新增 `PUT /api/accounts/{name}`(改 role/perms/note);`auth.set_account()`;
**主账号不可降级**、role=admin 时 perms 自动清空
### 老配置一次性回填
线上/本地老 `config.json` 的 guest 没有 `perms` 字段 → 新增独立标记 `guest_perms_seeded`,
`ensure_default_account()` 只在**字段缺失**时补默认能力;管理员之后关掉(`perms=[]`)不会被重开。
### 前端能力感知
- `body.cap-elec` 才显示 `[data-cap="elec"]`;`[data-ro]` 仍是「仅管理员」;
管理员同样打 `cap-elec`(否则自己也会被门控藏掉)
- 电费页录入卡 + 「发送全部待发/重出单据」由 `data-ro` 改 `data-cap="elec"`;
批次列表「删除」「单据行发送/强制发送」**按 kind 生成**两种属性
- `api()` 前端拦截改成 `visitorMayWrite(method, path)`;顶栏 chip 与横幅文案按能力显示
(「访客 · 仅电费」/ 只读)
- 账号管理加「权限」列(管理员=全部 / 访客=能力标签 / 空=只读)+ 新增账号勾选
+ 已有访客一键开通/关闭
### 落地
- 本地 + 线上均设:`guest` / `guest@2026`,`perms=['elec']`,备注「电费通知单操作员」
- 线上原配置备份到 `data/_snap/config.online-before-guest-perms.json`
- 提交 `35f0fbc` → push → `deploy_git.py sync` 全绿;容器重建后**自动回填**已生效
- 线上实测(curl):guest 登录 OK;`/api/session` → `perms=['elec']`;
`PUT /api/config`·`POST /api/rent/batches`·`/api/scheduler/fire`·`/api/accounts` → **403**;
`POST /api/batches`(空明细)→ 400、`DELETE /api/batches/NO-SUCH` → 200 ok:false —— 闸门按能力放行
- 线上数据零扰动:批次仍 7(elec 3 + rent 4)、台账 85 行 × 31 列
### 顺手修的 bug
`DELETE /api/batches/{不存在的 id}` → `service.rollback_receipt_sync(None)` 抛
`AttributeError: 'NoneType' object has no attribute 'get'` → 500。已在 `_sync_records()` 里
加 `isinstance(batch, dict)` 兜底,现返回 200 `{ok:false}`。
### 验证
- 单测 **245 项**全绿(accounts 34 → **60**:能力模型、增删改、会话、老配置回填、写闸门矩阵)
- **新增 `tools/ui_verify10.py`(69/0)**:guest / admin / 纯只读三方对照 ——
会话能力、控件可见性、**guest 真能录一行电费并生成通知书**、14 个非电费写接口 403、
房租批次四类操作 403 且批次未被改动、纯只读访客连电费也 403、admin 全可见、无 JS 报错
- `ui_verify7.py` 改造后 69/0:只读样本改用脚本自建的 `ro_verify`,新增 A3 段验证 guest 语义
- `ui_verify2.py` 72/0 · `ui_verify.py` 全绿(均走 `ui_verify_runner.py` 沙箱)
- 真实 `data/` 除 config 那一次有意写入外零改动
### 踩坑
1. `ui_verify7` 原来直接拿 guest 当「只读访客」样本 → guest 现在有 elec 能力,断言全废。
改成脚本内 `auth.add_account("ro_verify", ..., perms=[])` 另造样本。
2. `#btn-send-all` 在 `#detailCard`(默认 display:none)里,必须**先 openBatch** 才能判可见性;
否则 `is_visible` 恒为 False。
3. 单测里 `DELETE /api/batches/NO-SUCH` 才把上面那个 None 崩溃暴露出来 ——
给「应当放行」的用例补了 `assertLess(st, 500)`,防止接口悄悄 500 还判成通过。
......@@ -62,7 +62,16 @@
- 确认框是 `.modal-mask.show`(**不是 `.open`**)
## 系统骨架 / 渲染
- 启动 `python -m rentos.server --port 8899`;`--no-scheduler` 只开 Web;admin / 1989@rent05;另设只读访客 guest
- 启动 `python -m rentos.server --port 8899`;`--no-scheduler` 只开 Web;admin / 1989@rent05;
访客 **guest / guest@2026**(`role=viewer` + `perms=['elec']`,只放开「电费通知单」页的写操作)
- **权限模型(2026-10-13)**:`role`(admin|viewer)+ 账号/会话的 `perms` 能力清单(目前只有 `elec`)
- 管理员隐含 `{'*'}`;`viewer` 按 `perms` **逐接口**放行,`perms=[]` 即纯只读
- `auth.caps()/can()/normalize_perms()`;`server._write_cap(method,path)` 解析接口所需能力,
`_guard_write(user,cap)` 判定(403 文案「账号 X 只能操作:电费通知单」)
- **批次类接口按批次 `kind` 复核**:房租车位费的删/重出/发送/收款登记**仍只有管理员**能动
- 老配置一次性回填:`auth.guest_perms_seeded` 标记 + `DEFAULT_CONFIG` 同步加该键
- 前端:`body.cap-elec` 才显示 `[data-cap="elec"]`;`data-ro` 仍是「仅管理员」;
账号管理新增「权限」列 + 一键开通/关闭(`PUT /api/accounts/{name}`);**主账号不可降级**
- 导航 5 项:概览 / 电费通知单 / 房租车位费 / 公司管理 / 设置;真实 `<a href="#/...">` + hash 路由,
实现在 `app.js` 的 `PAGES` + `applyNav()`;**新增页面必须同时在 PAGES 与 `#nav` 登记**
- CSS 表格类名 `edgrid`;侧栏/顶栏读 `/api/overview`(含 wecom-cli,慢)
......@@ -97,11 +106,14 @@
`* text=auto eol=lf`、`*.bat -text`、**Dockerfile 必须 LF**
## 验证脚本(tools/)
- 单测 **219 项**:`test_rules` 45 · `test_directory` 33 · `test_rent` 53 · `test_round5` 54 · `test_accounts` 34
- 单测 **245 项**:`test_rules` 45 · `test_directory` 33 · `test_rent` 53 · `test_round5` 54 · `test_accounts` 60
(`test_scheduler.py` 需活跃 8899 服务,无服务时失败属预期)
- UI 回归:**自带沙箱** `ui_verify7/8/9.py`;**需连服务** `ui_verify`·`2`·`3`·`4`·`5`·`6.py`
- UI 回归:**自带沙箱** `ui_verify7/8/9/10.py`;**需连服务** `ui_verify`·`2`·`3`·`4`·`5`·`6.py`
→ 依次覆盖 台账+电费录入 / 设置+锁定+批量暂存 / 带入+照明+图片 / 公司管理 / 房租出单全链 /
收款开票+导出 / 访客+收款同步 / 账期区间 / 删房产列+免租两类型+批量编辑
收款开票+导出 / 访客(纯只读 + guest 仅电费)+收款同步 / 账期区间 / 删房产列+免租两类型+批量编辑 /
**访客能力三方对照(guest 69 项)**
- ⚠️ `ui_verify7` 的只读样本是脚本自建的 `ro_verify`(无能力)—— guest 已改成「电费通知单操作员」,
不能再当纯只读样本用
- **`tools/ui_verify_runner.py <脚本…>`**:每个脚本起**全新沙箱**(复制 `data/` + 改写 config 常量 +
另起端口 + 注入 `RENTOS_BASE`),真实 `data/` 零改动 —— 跑本次的 3/4/5/6 必用
- 端到端 `e2e_round5.py`(PDF 出单 → 收款开票 → 导出 xlsx → 排期 → 删除并回滚)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment