feat: 增加门店身份注销流程

This commit is contained in:
2026-08-31 09:43:14 +08:00
parent 396597a160
commit e529bb5942
35 changed files with 6062 additions and 45 deletions
+342
View File
@@ -0,0 +1,342 @@
# 门店身份注销接口实测
实测时间:2026-08-28 11:39;追加 13:45–13:46 零资产确认(服务端响应时间)。
环境:`https://api-test.zhifly.cn`。首次 11:39 使用用户明确授权的 iPhone 当前 `store_user` 登录 Token,仅执行 `GET /eligibility` 与 `GET /status`,当时未调用修改接口。后续额外授权的两项零资产确认见第 5 节。请求头与当前 Debug App 一致:`token`、`X-APP-VERSION: 1.3.1`、`X-OS-TYPE: iOS`、JSON Accept/Content-Type。
以下为真实响应;仅 `store_user_id`、`finance_identity_id` 统一替换为数值 `0` 脱敏,不能把该值用作有效身份。没有保存 Token。
## 1. 注销条件
`GET /api/yf-handset-app/account-deregister/eligibility`
HTTP 200,业务码 `100000`:
```json
{
"code": 100000,
"msg": "success",
"data": {
"can_apply": false,
"store_user_id": 0,
"finance_identity_id": 0,
"wallet_balance_fen": 0,
"wallet_balance": "0.00",
"points_balance": 0,
"wallet_waived": false,
"points_waived": false,
"unfulfilled_count": 37,
"fulfillment_in_progress_count": 0,
"risk_end_at": "2026-08-27 15:47:32",
"eligible_at": "2026-09-03 15:47:32",
"risk_window_hours": 168,
"blockers": [
{
"code": "ORDER_UNFULFILLED",
"message": "账户仍有未履约订单或带单",
"action": "complete_orders"
},
{
"code": "RISK_WINDOW_NOT_EXPIRED",
"message": "最后一笔业务的风险结束时间尚未超过7天",
"action": "wait_risk_window"
},
{
"code": "WALLET_WAIVER_MISSING",
"message": "请先确认放弃现金余额",
"action": "confirm_wallet_waiver"
},
{
"code": "POINTS_WAIVER_MISSING",
"message": "请先确认放弃积分",
"action": "confirm_points_waiver"
}
],
"deregister": null
},
"time": "2026-08-28 11:39:51"
}
```
本次观测到的类型:
| 字段 | JSON 类型 / 含义 |
| --- | --- |
| `can_apply` | 布尔值,服务端是否允许申请 |
| `store_user_id`、`finance_identity_id` | 整数 |
| `wallet_balance_fen` | 整数,现金余额(分) |
| `wallet_balance` | 字符串,金额展示值;本次为 `"0.00"` |
| `points_balance` | 整数,积分余额 |
| `wallet_waived`、`points_waived` | 独立布尔值,是否已确认放弃 |
| `unfulfilled_count`、`fulfillment_in_progress_count` | 整数 |
| `risk_end_at`、`eligible_at` | 时间字符串,格式 `yyyy-MM-dd HH:mm:ss`;时区及无历史业务时的空值形式尚未确认 |
| `risk_window_hours` | 整数,本次为 `168` |
| `blockers` | 对象数组,每项有字符串 `code`、`message`、`action` |
| `deregister` | 本次为 `null`,尚未观测非空结构 |
本次余额与积分均为零,服务端仍返回两项放弃确认缺失。客户端不能因为资产为零就省略确认。当前还有 37 项未履约订单或带单,且风险等待期未结束,不能提交注销。`eligible_at` 是业务风险期截止时间,不是提交申请后的冷静期截止时间;达到该时间也不代表其他条件自动满足。
## 2. 注销状态
`GET /api/yf-handset-app/account-deregister/status`
HTTP 200,业务码 `100000`:
```json
{
"code": 100000,
"msg": "success",
"data": {
"deregister": null
},
"time": "2026-08-28 11:39:51"
}
```
当前查询没有返回注销申请。不能据此推断非空申请的字段名称、状态枚举或是否可撤销。
## 3. 首次只读探测后尚缺的信息(后续进展见第 5、6 节)
- 草稿、冷静期、阻断、撤销、完成时 `data.deregister` 的非空结构、状态字段和值。
- `cooling_until`、`remaining_seconds` 的实际位置及类型。
- 无历史业务时风险时间的空值形式、时间字符串所用时区。
- 重新登录或选择身份自动撤销的具体接口时机,以及受限 Token 是否允许切换其他身份。
这些情况需要现有后端响应样例/源码,或在获得明确授权的专用测试身份上验证。本次授权仅限只读查询,不为采样创建、撤销注销申请或确认资产放弃。
## 4. 后续零资产确认授权(2026-08-28 13:42)
用户随后明确允许:在测试环境重新核实现金余额与积分均为 0 后,分别调用两项资产放弃确认接口,并查询是否产生草稿。此授权不包含发送短信、提交或撤销注销申请,也不允许非零资产确认。
已确认 iPhone 11 连接,并读取本 App 的当前会话:没有登录 Token,保存的账号类型为 `photog`,不是旧注销接口要求的 `store_user`。因此此次没有发出任何接口请求或资产确认,设备偏好未修改,本地临时偏好副本已删除。需在测试版 App 登录门店身份后继续;不要为采样登录已有未完成注销申请的身份,以免触发自动撤销。
## 5. 已完成两项真实零资产确认(2026-08-28 13:45–13:46)
用户重新登录后,核实当前为有效 `store_user`。每项确认前均重新查询:身份与设备会话一致、现金余额(分和展示金额)与积分均为 0;只访问测试环境,禁止重定向,不自动重试修改请求。仅执行了已授权的 `POST /waivers/wallet`、`POST /waivers/points` 各一次,没有发送短信、提交或撤销注销申请。
本次身份初始没有未履约/交付处理中记录,`risk_end_at`、`eligible_at` 均为 JSON `null`,阻断仅为两项确认缺失。与第 1 节较早查询的身份情况不同,不能沿用之前的 37 项未履约记录。
| 阶段 | `wallet_waived` | `points_waived` | `can_apply` | `deregister` |
| --- | --- | --- | --- | --- |
| 确认前 | false | false | false | null |
| 现金确认后 | true | false | false | `status: 0` 草稿 |
| 积分确认后 | true | true | true | 同一未提交草稿 |
两个确认请求均 HTTP 200、`code: 100000`,`data` 返回 `asset_type`(分别为 `"wallet"`、`"points"`)、`wallet_balance_fen: 0`、`wallet_balance: "0.00"`、`points_balance: 0` 及两项确认时间。现金确认后 `wallet_waived_at: "2026-08-28 13:45:57"`、`points_waived_at: null`;积分确认后后者变为 `"2026-08-28 13:46:42"`。
两个 GET 中的 `data.deregister` 都返回以下结构;仅 `id` 脱敏为 0,实际是正整数:
```json
{
"id": 0,
"status": 0,
"status_label": "待确认",
"reason": "",
"apply_time": null,
"cooling_until": null,
"remaining_seconds": 0,
"cancel_time": null,
"blocked_code": "",
"blocked_reason": "",
"completed_at": null
}
```
**确认结论:第一次资产确认即产生非空草稿;`status` 为数字,0 对应“待确认”。非空记录不等于已提交,不能一律限制普通业务或后续资产确认。两项确认后即使仍有草稿,`can_apply` 也会变为 true。** `remaining_seconds: 0` 在草稿中存在,不能单独据此认定冷静期已结束或注销已完成。
已据此修正客户端草稿判断、继续确认/验证和启动核验,并补充 Mock 测试。只将完整且无提交、撤销、阻断或完成字段冲突的 `status: 0` 识别为草稿;未知状态仍不推断。所有本地会话临时副本已删除,未写回设备偏好,未保存 Token。
仍缺:冷静期、撤销、阻断、完成的实际状态值及对应时间数据;确认过期响应;登录/切换自动撤销的具体时机。继续真实验证需要另行授权短信、提交与撤销操作,当前授权不包含这些操作。
## 6. 短信、申请与立即撤销(2026-08-28 14:16–14:24)
用户另外授权在切换后的专用测试身份上发送短信、提交申请,获取状态后立即撤销。真机页面核实:现金/积分为0、两项确认已完成、无未履约及风险等待阻断。通过 iPhone 镜像发送短信,用户提供验证码后提交;未将验证码写入源码或文档。短信和申请的原始 POST 响应体没有保存,不能把后续 GET 当作 POST 原文。
- 申请原因:`API test; cancel immediately`。
- 申请时间:`2026-08-28 14:23:40`。
- 查询确认:`status: 1`、`status_label: "冷静期中"`,冷静期截止 `2026-09-04 14:23:40`。
- 核对当前身份、同一申请及原因后只调用一次 `/cancel`,不自动重试。
- 撤销成功时间:`2026-08-28 14:24:04`,返回 `status: 9`、`status_label: "已撤销"`。
- `14:24:05` 再次 GET `/status` 与 `/eligibility` 确认已撤销;未留下待注销申请,未执行最终注销。
- 本地临时会话副本已删除,未编辑设备偏好,未在文档中保存 Token、手机号或真实身份 ID。
### 冷静期状态
```json
{
"code": 100000,
"msg": "success",
"data": {
"deregister": {
"id": 0,
"status": 1,
"status_label": "冷静期中",
"reason": "API test; cancel immediately",
"apply_time": "2026-08-28 14:23:40",
"cooling_until": "2026-09-04 14:23:40",
"remaining_seconds": 604776,
"cancel_time": null,
"blocked_code": "",
"blocked_reason": "",
"completed_at": null
}
},
"time": "2026-08-28 14:24:03"
}
```
### 撤销响应
```json
{
"code": 100000,
"msg": "注销申请已撤销",
"data": {
"deregister": {
"id": 0,
"status": 9,
"status_label": "已撤销",
"reason": "API test; cancel immediately",
"apply_time": "2026-08-28 14:23:40",
"cooling_until": "2026-09-04 14:23:40",
"remaining_seconds": 0,
"cancel_time": "2026-08-28 14:24:04",
"blocked_code": "CANCELLED_BY_USER",
"blocked_reason": "用户主动撤销注销",
"completed_at": null
}
},
"time": "2026-08-28 14:24:04"
}
```
### 撤销后的条件
```json
{
"code": 100000,
"msg": "success",
"data": {
"can_apply": false,
"store_user_id": 0,
"finance_identity_id": 0,
"wallet_balance_fen": 0,
"wallet_balance": "0.00",
"points_balance": 0,
"wallet_waived": false,
"points_waived": false,
"unfulfilled_count": 0,
"fulfillment_in_progress_count": 0,
"risk_end_at": null,
"eligible_at": null,
"risk_window_hours": 168,
"blockers": [
{
"code": "WALLET_WAIVER_MISSING",
"message": "请先确认放弃现金余额",
"action": "confirm_wallet_waiver"
},
{
"code": "POINTS_WAIVER_MISSING",
"message": "请先确认放弃积分",
"action": "confirm_points_waiver"
}
],
"deregister": {
"id": 0,
"status": 9,
"status_label": "已撤销",
"reason": "API test; cancel immediately",
"apply_time": "2026-08-28 14:23:40",
"cooling_until": "2026-09-04 14:23:40",
"remaining_seconds": 0,
"cancel_time": "2026-08-28 14:24:04",
"blocked_code": "CANCELLED_BY_USER",
"blocked_reason": "用户主动撤销注销",
"completed_at": null
}
},
"time": "2026-08-28 14:24:05"
}
```
冷静期 GET `/eligibility` 已返回两项确认 false 和缺少确认的两个阻断,撤销后也是如此;冷静期不能因此重新确认资产。撤销记录保留旧冷静期时间;remaining_seconds 为0不等于注销完成。正式完成与终审阻断的状态值、自动撤销和所有身份注销后的登录响应仍未验证。此次授权操作已结束,不继续发送短信或创建申请。
## 2026-08-28:补查终态契约及新申请准备
用户随后允许继续申请注销,以尝试获取缺失的终态响应。本节是新的调查记录,不代表已经创建新的申请。
| 查找范围 | 结果 |
| --- | --- |
| 本机 iOS、Android 参考工程及桌面相关文件 | 未找到旧注销后端的 Controller、Resource、状态枚举或 migration 源码 |
| 测试 API 的 `/docs`、`/api/documentation`、`/openapi.json`、`/swagger.json` | HTTP 200,但业务体均为路由不存在,不能当作可用接口文档 |
| Gitea 匿名仓库搜索 | 没有返回匹配仓库;不代表私有仓库不存在 |
| Chrome 中已有登录的 Gitea 会话 | 可访问当前用户的仓库列表;代码搜索 `StoreUserDeregister`、`deregister` 均未匹配,未取得注销后端源码 |
| 已打开的飞书原始文档 | 页面显示最近修改为8月21日;相关规则与本地文档一致,未给出终态数字枚举、完整 JSON 或完成后的鉴权响应 |
| 真机当前 App 会话 | 无有效门店登录态,因此没有发起认证查询、短信、资产确认、申请或撤销 |
当前仍需先在真机登录一个允许最终注销的专用测试门店身份,再读取该身份的条件。若保留申请等待正式完成,需经过服务端7天冷静期;不能修改本机时间来替代服务端等待,也不能为制造终审阻断而擅自修改订单、资产或数据库。按旧文档,重新登录或选中申请中的同一身份会自动撤销申请,等待期间应避免这种操作。
“终审阻断”需要到期复核发现条件变化,单纯提交并等待不能保证得到该状态。此次只读查找未新增任何终态实测结论。
## 2026-08-28 15:23:按用户要求改在模拟器准备新申请
- 用户明确要求操作其已登录的 iPhone Air 模拟器。本次是人工界面操作,不是模拟器单元测试;不改变此前真机测试结论。
- 页面当前身份为北大科技园,显示现金0元、积分0;初始只有两项资产确认阻断,上次申请已撤销。
- 用户授权分别确认两项零资产并发送验证码。继续操作时,现金已显示确认完成,因此没有重复提交现金确认;随后单独确认零积分。
- 页面显示两项资产均已确认、当前条件满足;仍为未提交草稿。
- 15:23通过页面发送一次注销验证码,界面提示已发送至当前身份绑定手机号。未保存短信 POST 原始响应,不能把界面提示当作完整接口 JSON。
- 停留在验证码输入页,等待用户提供本次验证码;此时尚未提交新申请,也未进入新的冷静期。
## 2026-08-28 15:27—15:29:新申请已提交并保留
- 用户提供本次验证码,并在最终弹窗前明确确认:只注销北大科技园身份,保留申请至服务端7天后复核,正式完成不可恢复,不像上次立即撤销。
- 15:27:24在模拟器点击一次“确认提交申请”。原因为 `Test account deregistration`;验证码不写入文档或代码。
- 页面进入冷静期核验页,显示截止时间 `2026-09-04 15:27:24`(服务端时间)。
- 15:28:27只读 GET `/status`、`/eligibility` 均确认 `deregister.status: 1`,`cancel_time`、`completed_at` 均为 null。
- 15:29:22以同一现有 Token GET `/api/yf-handset-app/userinfo`,返回 HTTP 200、业务码150015、`data.status: "cooling"`、相同冷静期截止时间;响应没有 `time` 字段。前后 GET `/status` 均成功且仍是同一冷静期记录。
- 未重复发送短信、未重复申请、未执行撤销、登录或身份选择。未保留 Token 副本或修改模拟器偏好设置。
**此时申请仍待注销,与14:24已撤销的旧申请不同;后续已于15:55:36因重新登录自动撤销,见文末15:56记录。** 终审阻断/正式完成未发生;不能把冷静期截止当作已经注销。查询样例已加入[旧接口返回数据说明](门店身份注销旧接口返回数据说明.md)。短信和申请 POST 原始响应体仍未捕获,不能用 GET 代替。
本轮脱敏证据:
- `/private/tmp/suixinkan-contract-discovery-20260828/152826-simulator-status-redacted.json`
- `/private/tmp/suixinkan-contract-discovery-20260828/152826-simulator-eligibility-redacted.json`
- `/private/tmp/suixinkan-contract-discovery-20260828/152921-simulator-userinfo-cooling-redacted.json`
- `/private/tmp/suixinkan-contract-discovery-20260828/152921-simulator-status-before-auth-redacted.json`
- `/private/tmp/suixinkan-contract-discovery-20260828/152921-simulator-status-after-auth-redacted.json`
## 2026-08-28 15:45:真实响应回归与源码补查
- 将本轮150015及对应status响应整理为Swift测试夹具;逐字段对比采样JSON,仅注销记录ID替换为测试值301。
- 在iPhone 11上完成全量792项测试:782通过、10项既有失败,无跳过;失败用例集合与原始基线一致。注销相关73项全部通过,包括真实userinfo限制响应经API、通知和路由进入冷静期页面的新增Mock集成测试。
- 本轮仅更新测试与文档,未操作模拟器中的真实申请;未变更工程配置、版本号或依赖。
- 补查已保存项目列表,未发现相关后端工程;Android工程现有Git主机的HTTP网页根返回空响应,未取得源码。未猜测后端仓库路径、修改服务器或扩展访问权限。
- 结果:`/private/tmp/suixinkan-deregister-real-contract-20260828.xcresult`。终态样例仍缺,不能将测试通过作为正式注销完成的证据。
## 2026-08-28 15:48—15:50:最终只读复核与冷启动尝试
- 15:48:04查询仍为申请时间15:27:24的冷静期1。
- 仅终止并重启iPhone Air中的App进程;未卸载、清空数据、登录或选择身份。工具未能切换至正确模拟器窗口,故冷启动UI没有验证完成。
- 15:50:51用原会话再次GET `/status`、`/eligibility`,均返回同一申请的冷静期1,截止时间不变,`remaining_seconds: 603392`,撤销时间与完成时间均为null。
- 证据:`/private/tmp/suixinkan-contract-discovery-20260828/155050-simulator-status-redacted.json`及同前缀的`eligibility-redacted.json`。
- 未产生新的终态依据,不继续重复轮询或制造业务变化;待服务端到期状态或已有源码可用后再推进。
## 2026-08-28 15:56:重新登录自动撤销
用户告知已进入后,只读查询发现15:27:24申请已于15:55:36自动撤销。两次GET均返回status9、CANCELLED_BY_LOGIN、用户重新登录自动撤销的说明,completed_at仍为null。没有调用cancel或重新申请;未采集触发撤销的具体登录请求,因此不能断言具体接口时机。
证据:/private/tmp/suixinkan-contract-discovery-20260828/155633-simulator-status-redacted.json及同前缀eligibility文件。此前待注销记录的截止时间已失效,不再等待该申请于9月4日完成。用户随后要求UI简化,本轮仅使用Mock测试,不再操作真实注销。
## 2026-08-28 17:01:验证资产确认接口的false参数
用户单独授权尝试现金、积分接口传`accepted: false`。使用iPhone 11当前已登录门店身份和测试环境,先只读确认两项标记均为true、金额均为0、状态为未提交草稿0,再分别调用一次两个确认接口。
- 现金:17:01:14返回HTTP 200、业务码100099、`msg: 请明确确认自愿放弃对应资产`、`data: []`。
- 积分:17:01:32返回同样结果。
- 每次调用后重新GET查询:`wallet_waived`和`points_waived`仍为true,草稿仍为0,金额未变化,没有进入冷静期。
- 结论:当前接口不接受false作为撤回确认;本轮没有调用true、短信、申请、撤销或登录接口。未重试POST。
完整返回及前后状态见[旧接口返回数据说明4.4节](门店身份注销旧接口返回数据说明.md)。脱敏证据目录:`/private/tmp/suixinkan-waiver-false-20260828/`。