5.0 KiB
5.0 KiB
业务规则(agent 自行落地的稳定约束)
不依赖 Electron、不依赖任何 daemon。下列规则从 src/main/adapters/fansibote-fuzhong/adapter.ts 提炼,agent 在做任何写操作前必须自己执行这些校验。
时间块归一
slot 的 endDatetime 形如 HH:59:00,代表该 slot 占据"开始时间所在整点"整块。
08:00-08:59= 8 点这一小时块19:00-19:59= 19 点这一小时块- 连续组合判定时把 slot 投影到
[beginHour, endHour),按整点对齐 helper find-continuous内部已实现该归一,agent 不必手算
连续组合判定
判定"19-21 点完整连续组合"的步骤(agent 也可以直接调 pospal.mjs find-continuous):
- 过滤出
status in {available, limited}且无 enrollment 覆盖的 slots - 按
txtClassroomUid分组 - 在每个分组内按
beginDatetime升序排序 - 在每个分组内滑窗
[targetStartHour, targetEndHour):- 窗口第一个 slot 的
beginHour == targetStartHour - 窗口最后一个 slot 的
endHour == targetEndHour - 相邻 slot 整点连续(
prev.endHour == cur.beginHour,无空缺) - 全部同一
txtClassroomUid - 全部不在 enrollment 覆盖区间
- 窗口第一个 slot 的
- 任一不满足即视为无完整连续组合
整段"19-21 点" ≠ "19-20 和 20-21 分别可订"——必须是同一场地、连续、无活动、可订。
写操作前实时复核
锁场前必须重新查询一次目标日期的列表,不信任之前缓存的:
- 全部目标 slot 仍
available/limited - 全部目标 slot 无 enrollment 覆盖
- 全部 slot 仍同一
txtClassroomUid - 全部 slot 仍按目标时间整点连续
- 总价仍等于
sum(cost),单个 cost 未变 - 日期未变
任一不符则放弃本次锁场,不重试。
错误分类与重试边界
| 现象 | 分类 | 处置 |
|---|---|---|
HTTP 2xx + successed: true |
success | 一次性操作,session 结束本次写 |
HTTP 2xx + successed: false |
rejected | 可换时段重试(同一会话同一时段最多 1 次) |
| HTTP 4xx | rejected | 同上 |
| HTTP 5xx | uncertain | 不重试,告诉用户去小程序核实 |
| 网络错误 / 超时 | uncertain | 同上 |
| JSON 解析失败 | uncertain | 同上 |
绝不在结果不确定时重试。 已经发起的锁场,agent 必须假设服务端可能已经接受,必须提示用户去小程序核对。
写操作幂等与熔断
- 同一 session 中:
- 同一时段只允许一次"明确失败 → 换时段重试"
- 任何 uncertain 后立即停止本 session 的写操作,等用户去小程序确认
- 锁场成功后,session 内禁止再对该用户发起任何写操作;告诉用户去小程序支付
- 锁场成功后
script.expireTime已过:锁场已失效,告知用户
脱敏与日志
- 日志中禁止出现:
PSPLVISITORID原值、完整请求头、完整响应、支付签名、Cookie、预约人姓名/手机号 - 访客凭证指纹:
pospal.mjs fingerprint <visitor-id>→ 12 字符 hex,仅用于日志关联 - 响应脱敏:
pospal.mjs redact < raw.json→ 移除script.paySign、所有PSPL*头、任何customerName/customerTel,输出可记录版本 - 待支付摘要只保存:
apptUid、venueAppointUids、expireTime、金额、场地名、时间;绝不保存script对象
重复通知与去重
agent 自己实现轮询时的去重:
- 维护"已知完整连续组合"集合,key =
txtClassroomUid + 日期 + 起止整点 + 总价 - 只对"本轮新出现"且"相对上一轮状态变化"的组合产生通知
- 持续可订不重复通知
- 消失后重现可以再次通知
- 同一组合的多次出现合并为一条通知
轮询节奏
- 默认每 10 秒一轮,由 agent 自行 sleep 实现
- 整点(如
19:00:00.000)到达时绕过冷却立即查一次,但仍要遵守"同一 date 的并发查询只允许一个在飞" - 同一球场的写操作天然串行(锁场 / 释放 不并发);同一 session 不允许同时发起两笔锁场
PSPLVISITORID 处理
- agent 在首次需要写操作时向用户索取,使用隐藏式输入:
- bash:
read -rs PSPAL_VISITOR_ID - Node:
readline关闭回显 - 任何等价手段
- bash:
- 输入后只在 session 内存保存为
process.env.POSPAL_VISITOR_ID,绝不落盘 - session 结束或被新 session 替换时立即
unset POSPAL_VISITOR_ID - helper 子命令只从 env 或
--visitor参数读取;优先用 env避免值出现在进程列表 - 任何情况下都不得在日志、对话、git、临时文件中出现
PSPLVISITORID原值 - 调试用指纹:
pospal.mjs fingerprint "$POSPAL_VISITOR_ID"→ 12 字符 hex
不允许做的事
- ❌ 用任何形式的"自动支付"或"自动点确认"
- ❌ 缓存
script对象 /paySign - ❌ 把
PSPLVISITORID写入 Skill 文件、.env、配置、日志、对话 - ❌ 改写
STOREID/userId/PROJECT_UID(除非接口契约明确变更) - ❌ 在结果不确定时重试
- ❌ 拆单(把"19-21"拆成"19-20"和"20-21"两次请求)
- ❌ 静默忽略字段缺失