init
This commit is contained in:
51
docs/architecture.md
Normal file
51
docs/architecture.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# 架构说明
|
||||
|
||||
## 为什么选 Electron
|
||||
|
||||
本项目的主要复杂度不是绘制桌面窗口,而是长期维护多个非统一的微信小程序接口:不同签名方式、Cookie、请求头、代理、登录状态和多步骤预约流程。Electron 主进程可以直接复用 Node.js 网络生态,并与 Chromium 渲染层保持清晰隔离,能以最低的适配成本覆盖这些差异。
|
||||
|
||||
Tauri 的安装包更小、空闲内存通常更低,但它会把接口适配层拆成 Rust 命令或 sidecar。当前业务没有强约束要求最小包体,却明确要求持续定制多个 JavaScript/HTTP 流程,因此 Electron 的开发与排障成本更低。
|
||||
|
||||
## 运行边界
|
||||
|
||||
```text
|
||||
React renderer
|
||||
| typed window.tennisBook API
|
||||
Preload allowlist
|
||||
| ipcRenderer.invoke
|
||||
Electron main process
|
||||
| AdapterRegistry + CourtSettingsStore
|
||||
CourtAdapter A / CourtAdapter B / ...
|
||||
| HTTP, cookies, signatures, booking workflow
|
||||
微信小程序后端 API
|
||||
```
|
||||
|
||||
渲染层只知道统一的 `CourtSummary` 和 `AvailabilityDay`。某个球场的原始响应、鉴权和预约步骤不能穿过 IPC 边界。
|
||||
|
||||
`AvailabilityDay` 可包含界面明确需要展示的预约人昵称和手机号,但适配器必须从第三方原始响应中逐项提取,不能把原始预约对象整体穿过 IPC;这些信息只保留在当前查询的内存状态中,不记录、不缓存、不落盘。
|
||||
|
||||
preload 必须构建为 CommonJS `.js`。项目启用了 Electron renderer sandbox,而 sandboxed preload 没有 ESM 上下文;不能通过关闭沙箱来迁就 `.mjs` 构建产物。
|
||||
|
||||
## 每球场运行时配置
|
||||
|
||||
`PSPLVISITORID` 等运行时凭证按 `courtId` 独立存储在 Electron `userData` 目录。范思伯特福中福的场地 ID 固定为适配器私有常量,只在请求边界映射到第三方 `userId` 字段和 `STOREID` 请求头,不经过 IPC,也不在配置界面暴露。设置弹窗通过专用白名单 IPC 读取凭证原值并回填密码框,默认遮挡;关闭弹窗立即清除渲染层中的输入值和设置对象,其他查询及预约接口不返回凭证。
|
||||
|
||||
配置文件使用临时文件加重命名的方式原子替换。共享层始终用字符串表达外部 ID,避免超出 JavaScript 安全整数范围;具体适配器负责校验接口是否接受该范围并转换为请求格式。
|
||||
|
||||
## 接入一个新球场
|
||||
|
||||
1. 在 `src/main/adapters/<court-id>/` 创建适配器。
|
||||
2. 实现 `CourtAdapter` 的 `court` 与 `getAvailability()`。
|
||||
3. 在适配器内部完成原始响应到共享模型的转换。
|
||||
4. 将适配器加入 `src/main/adapters/registry.ts`。
|
||||
5. 为登录失效、空数据和接口字段变化添加契约测试。
|
||||
|
||||
## 预约写操作
|
||||
|
||||
公共预约状态机为:`可订 -> 二次确认 -> 重新查询校验 -> 结果待确认记录 -> 下单锁场 -> 待支付 -> 取消释放/支付完成`。具体请求体、版本头、访客凭证和响应解析只存在于球场适配器中。所有写操作按球场在主进程串行化,待支付或结果不确定期间禁止修改该球场凭证。
|
||||
|
||||
下单前由主进程重新查询同一天的数据,并使用重新查询得到的场地 UID、时间和价格构造请求,拒绝已订、排课和活动报名时段。渲染层传入的价格只作为用户确认金额;如与最新价格不同则拒绝下单并要求重新确认。第三方 UID 不接受渲染层输入。
|
||||
|
||||
范思伯特福中福的 `SaveVenueAppointmentV2` 只创建待支付订单并锁场,不代表支付成功。支付脚本不经过 IPC、不记录日志、不落盘;待支付订单的非敏感摘要按球场持久化,以便应用重启后仍能恢复“取消释放”入口。POST 发送前先持久化结果待确认记录,网络超时或响应解析失败时保留“尝试释放”入口,避免生成不可恢复的孤儿锁场。
|
||||
|
||||
`CrmAutoReleaseVenueAppoint` 使用当前球场配置与空 JSON 请求体释放当前访客的锁场,并不能绑定订单号。发送释放请求前先持久化 `release-uncertain`;若响应超时或进程中断,系统禁止自动重试,用户必须在小程序核实后显式清理本机记录。远端确认释放后先持久化 `release-confirmed` 标记,再删除摘要,避免清理失败后重复调用全局释放接口。支付脚本的过期时间只用于展示,不作为场地锁已经释放的依据。
|
||||
Reference in New Issue
Block a user