# 06 门锁授权与异步操作状态机设计 (Authorization State Machine) > **语义铁律**:**DMS HTTP 200 `success: true` 仅代表“请求已保存/已受理 (REQUEST_ACCEPTED)”,绝不代表“门锁设备执行成功 (DEVICE_SUCCESS)”!** > **门锁生效滞后性**:门锁作为电池供电的无线物联网终端,下发指令需要经过网关转发与微功耗空中唤醒,官方明确要求**调用成功 10 秒后再行查询操作状态**。 > **设计目标**:建立双层解耦状态机(业务授权状态 vs 设备操作状态),消除状态混淆。 --- ## 1. 双层状态体系解耦定义 必须区分两个不同生命周期的状态机: 1. **门禁授权业务状态 (`DoorAuthorizationStatus`)**:站在学员与住宿业务视角,表达该住宿事实当前是否具备开门权限。 2. **外部硬件操作状态 (`ExternalOperationStatus`)**:站在异步任务与设备网络视角,表达向 DMS 派发的具体单次指令的执行进展。 --- ## 2. 状态映射与状态定义字典 ### 2.1 门禁授权业务状态 (`DoorAuthorizationStatus`) | 本地业务状态枚举 | 业务含义 | 允许的操作 | | :--- | :--- | :--- | | **`UNISSUED`** (待下发) | 住宿已确认,尚未向门锁发起授权下发请求 | 下发授权 (`grant`) | | **`ISSUING`** (下发中) | 已向 DMS 发送创建钥匙请求,已获得 `operationId`,正在等待 10 秒后对账确认 | 刷新状态、等待轮询 | | **`ACTIVE`** (已生效/成功) | 轮询确认设备返回 `operationStatus = success`,学员可正常刷卡/输密码开门 | 查看凭据、撤销授权 (`revoke`) | | **`ISSUE_FAILED`** (下发失败) | DMS 或设备明确返回失败,或超时重试达到上限 | 重新下发 (`retry`)、人工改派房间 | | **`REVOKING`** (撤销中) | 学员退宿/调房,已向 DMS 提交删除钥匙请求,正在等待对账确认 | 刷新状态、等待轮询 | | **`REVOKED`** (已注销) | 轮询确认钥匙已被设备成功删除,权限已彻底关闭 | 归档留存、不可逆 | | **`EXPIRED`** (已到达有效期) | 超过 DMS 凭据设定的 `validUntil` 有效期边界(离线过期机制需供应商确认) | 自动归档、主动撤权兜底 | ### 2.2 外部硬件异步操作状态 (`ExternalOperationStatus`) 与 DMS 映射 | 本地操作状态枚举 | 对应 DMS API 原始字段 | 状态说明与处理策略 | | :--- | :--- | :--- | | **`CREATED`** | 内部构建指令,尚未发送 | 准备调用 HTTP 客户端 | | **`REQUESTED`** | HTTP 请求发出,等待响应 | 网络传输中 | | **`ACCEPTED`** | DMS 返回 `success: true`,携带 `operationId` | 记录 `operationId`,调度器挂起 10 秒轮询任务 | | **`PENDING_DEVICE`** | 轮询返回 `operationStatus: "undone"` | 设备尚未完成执行,计算下次退避轮询时间 | | **`SUCCESS`** | 轮询返回 `operationStatus: "success"` | 终态:设备已成功落锁/删除,触发推进业务状态 | | **`FAILED`** | 轮询返回 `operationStatus: "failed"` | 终态:设备返回明确失败,记录 `resultDetails` 与错误原因 | | **`TIMEOUT`** | 轮询返回 `operationStatus: "timeout"` | 终态:DMS 判定设备通信超时(10秒未响应),触发重试或报警 | | **`RETRY_WAIT`** | 遭遇可恢复异常(如网络抖动),等待下一次重试 | 依据退避算法重新放入调度队列 | --- ## 3. 门锁授权端到端生命周期状态流转图 (Mermaid) ```mermaid stateDiagram-v2 [*] --> UNISSUED : 住宿安排确认 (Accommodation Confirmed) state "授权下发阶段 (Grant Flow)" as GrantFlow { UNISSUED --> ISSUING : 触发下发 (报到或临近入住) state ISSUING { [*] --> Op_Requested : 调用 POST /online-password-key/create Op_Requested --> Op_Accepted : DMS 返回 200 (获得 operationId) Op_Accepted --> Op_Polling : 延迟等待 10 秒 Op_Polling --> Op_Undone : 查询状态返回 undone Op_Undone --> Op_Polling : 间隔 5 秒重试 (最大3次) } ISSUING --> ACTIVE : 查询状态返回 success ISSUING --> ISSUE_FAILED : 状态返回 failed 或多次 timeout ISSUE_FAILED --> ISSUING : 管理员手动重试 / 定时补偿重试 } state "正常使用阶段 (Active Use)" as ActiveUse { ACTIVE --> ACTIVE : 门锁正常开门 / 查询电量信号 ACTIVE --> EXPIRED : 超过 validUntil 有效期边界 } state "权限注销阶段 (Revoke Flow)" as RevokeFlow { ACTIVE --> REVOKING : 学员退宿 / 提前退训 / 换房 ISSUE_FAILED --> REVOKING : 换房或作废 state REVOKING { [*] --> Revoke_Requested : 调用 POST /online-password/delete Revoke_Requested --> Revoke_Accepted : DMS 返回 200 (获得 operationId) Revoke_Accepted --> Revoke_Polling : 延迟等待 10 秒 Revoke_Polling --> Revoke_Polling : undone (重试轮询) } REVOKING --> REVOKED : 查询状态返回 success REVOKING --> REVOKE_FAILED : 状态返回 failed 或 timeout REVOKE_FAILED --> REVOKING : 紧急人工重试撤销 } REVOKED --> [*] EXPIRED --> [*] ``` --- ## 4. 关键流转分支与语义判定细节 ### 4.1 成功下发分支 (Happy Path) 1. 触发下发:适配器调用 `/rest/doorlock/online-password-key/create`。 2. DMS 同步响应: ```json { "success": true, "data": { "id": 4945, "placeId": 1320, "lockId": 372, "operationId": "0872887e-464b-4f44-a0cc-057871ba0f65" } } ``` **本地动作**:持久化 `external_key_id = 4945`,创建外部操作记录(`operationId = 0872887e...`, `status = ACCEPTED`),授权主表推进为 `ISSUING`。 3. 轮询调度器等待 10 秒:调用 `/rest/doorlock/query-operation-status`(入参 `requestType = 1`, `operationId = ...`)。 4. 轮询响应: ```json { "success": true, "data": { "operationStatus": "success" } } ``` **本地动作**:更新操作记录为 `SUCCESS`,授权主表推进为 `ACTIVE`(生效可用)。 ### 4.2 设备超时分支 (`Timeout Handling`) - 若 10 秒后轮询返回: ```json { "success": true, "data": { "operationStatus": "timeout", "message": "Device response timeout" } } ``` - **判定规则**:门锁可能休眠过深、网关信号不佳或网关离线。 - **系统动作**: 1. 不得无限循环调用。 2. 若配置了自动补偿,可追加 1 次重试查询;若依然超时,将操作置为 `TIMEOUT`,授权主表置为 `ISSUE_FAILED`。 3. 页面以警告色徽标展示“设备下发超时”,提供【重新下发】按钮供管理员现场排障。