docs(engineering): I8/I9 iteration artifacts and frontend UI specification

Add I8/I9 design and implementation review markdown, update parallel iteration
index and I7 review cross-links, and add FRONTEND_UI_SPECIFICATION for design
handoff (Figma Make).

Made-with: Cursor
This commit is contained in:
2026-04-07 21:26:51 +08:00
parent d53ddf32c8
commit 650c1caffa
7 changed files with 589 additions and 28 deletions
@@ -20,7 +20,7 @@
| 项 | 说明 |
|----|------|
| **DEAD 行运维** | 仅 DB 字段 `last_error`/`status`;无 UI 重放按钮(I7.1 可选)。 |
| **DEAD 行运维** | **I8** 已提供平台代理重放 + 详情页按钮;**I9** 补充出库 **只读状态**`PENDING`/`SENT`/`DEAD` 等)见 [I9_WEBHOOK_DELIVERY_VISIBILITY_DESIGN.md](./I9_WEBHOOK_DELIVERY_VISIBILITY_DESIGN.md)。 |
| **`v-permission` 指令** | 设计可选组件级指令;当前以 **路由 + 菜单 `hasAnyRole`** 为主,足够覆盖 I7 DoD。 |
| **Playwright** | 仍未进 CI;与 [I6_CLOSEOUT](./I6_CLOSEOUT.md) 一致列为后续。 |
| **内部 mTLS** | 未在本次范围;仍共享 `X-Platform-Internal-Token`。 |
@@ -0,0 +1,40 @@
# I8 实现审核 — 对照 [I8_WEBHOOK_DELIVERY_REPLAY_DESIGN.md](./I8_WEBHOOK_DELIVERY_REPLAY_DESIGN.md)
> **日期**2026-04-06。
---
## 1. 总评
| 设计条款 | 结论 |
|----------|------|
| 浏览器只调平台、平台代调 Webhook | **已落地**`POST /api/v1/callback-inbox/{id}/replay-webhook-delivery` + `WebhookDeliveryReplayClient`。 |
| Webhook `/internal/**` 独立 Ops Token | **已落地**`WebhookOpsTokenFilter``X-Webhook-Ops-Token`;空配置 **503**。 |
| 仅 `DEAD` 可重放 | **已落地**`PlatformDeliveryService.replayDeadDeliveryByReceiptId`。 |
| 关联 `webhookReceiptId` | **已落地**:平台从 `PlatformCallbackInbox` 解析 `Long` 调 Webhook。 |
| OpenAPI / Runbook | **已更新**`CallbackWebhookReplayResponse`、RUNBOOK §10.5 I8 段。 |
---
## 2. 偏差与后续
| 项 | 说明 |
|----|------|
| **UI 不展示出库状态** | 仍为「成功调用平台接口」提示;是否在详情旁拉取 Webhook 库属 **Mid**(需只读 API 或观测面)。 |
| **Playwright** | 未进 CI;与 I6/I7 一致可后续补一条 OPS 点击重放(需 mock Webhook 或 test 栈)。 |
---
## 3. 验证命令(提交前)
- `mvn -f services/pom.xml verify`
- `npm run build``web/delivery-platform-ui`
- `UPDATE_OPENAPI=0 mvn -f services/pom.xml test -Dtest=OpenApiContractSnapshotTest`(契约已与 `contracts/openapi/delivery-platform-api.json` 手工对齐时亦应通过)
---
## 4. 修订记录
| 日期 | 说明 |
|------|------|
| 2026-04-06 | 初版:I8 实现对照设计审核。 |
@@ -0,0 +1,88 @@
# I8 设计 — Webhook 平台投递 DEAD 重放(MVP
> **角色**:解决方案架构;指导前后端实现与 Runbook。
> **前置**I7 已落地 `webhook_platform_delivery``PENDING`/`SENT`/`DEAD`)与调度器;平台 `platform_callback_inbox.webhook_receipt_id` 与 Webhook 收据主键对齐(`PlatformCallbackRequestPlanner` 写入 `webhookReceiptId`)。
---
## 1. 问题与目标
| 维度 | 说明 |
|------|------|
| **问题** | 出库 `DEAD` 后仅能通过查库/SQL 手工改状态,无受控运维入口,易误操作。 |
| **目标** | Ops 在 **Callback 详情** 一键将对应 **`DEAD`** 投递重新入队(`PENDING`),由现有调度器按 `max-attempts` 再次尝试 `POST /internal/v1/callback-events`。 |
| **非目标** | 修改比特 Callback 契约;不实现全量 DLQ 控制台;不在浏览器直连 Webhook 服务。 |
---
## 2. 信任边界与 API 分层
```mermaid
flowchart LR
UI[delivery-platform-ui OPS JWT]
API[delivery-platform-api]
WH[license-webhook-ingress]
UI -->|Bearer JWT| API
API -->|X-Webhook-Ops-Token + receiptId| WH
WH -->|调度| API
```
1. **浏览器只调平台** `POST /api/v1/callback-inbox/{id}/replay-webhook-delivery`,沿用 `CallbackInboxController``@PreAuthorize("hasAnyRole('OPS','SYS_ADMIN')")`
2. **平台服务器到 Webhook** 使用 **独立共享密钥** `X-Webhook-Ops-Token`(与 `X-Platform-Internal-Token` 分离:前者保护 Webhook 运维面,后者保护平台内部 ingest)。
3. **Webhook** 暴露 `POST /internal/v1/platform-deliveries/by-receipt/{receiptId}/replay`,由 **Servlet Filter** 校验 Ops Token(模块无 Spring Security 依赖,与现有 `/webhook/bitanswer/callback` 并存)。
---
## 3. 业务规则
| 规则 | 说明 |
|------|------|
| **关联键** | 使用平台收件箱的 `webhook_receipt_id`(字符串数字)对应 `webhook_platform_delivery.receipt_id`(唯一)。缺失则 **400**,提示未关联 Webhook 出库记录。 |
| **仅 DEAD 可重放** | Webhook 侧若行不存在 → **404**;若状态为 `PENDING`/`SENT`**409**,避免误重置进行中或已成功任务。 |
| **重放语义** | `status=PENDING``attempts=0``last_error=NULL``next_retry_at=NULL``updated_at=now`(重新给满 `max-attempts` 次数)。 |
| **平台幂等** | 重放仅重新 HTTP 投递;若平台 Inbox 已存在同 `(sourceSystem, externalMessageId)`,内部 ingest 返回 duplicate**不新建 Inbox**(与现有幂等一致)。 |
---
## 4. 配置项
| 组件 | 属性 / 环境变量 | 说明 |
|------|-----------------|------|
| Webhook | `craftlabs.webhook.ops-token` / `LICENSE_WEBHOOK_OPS_TOKEN` | 非空才启用 `/internal/**` 鉴权;空则 **503** 所有内部运维路径(避免误暴露)。 |
| Platform | `craftlabs.webhook.base-url` / `LICENSE_WEBHOOK_BASE_URL` | Webhook 根协议+主机+端口。 |
| Platform | `craftlabs.webhook.ops-token` | 与 Webhook 相同密钥,仅驻内存。 |
**Runbook**:在 [RUNBOOK.md](../../../services/RUNBOOK.md) §10.5 旁补充:重放前确认平台已恢复;同 receipt **不要并发多次重放**(单机调度即可)。
---
## 5. 前端
- **Callback 详情**:展示 `webhookReceiptId`;若存在则显示 **「重新入队出库(DEAD→待投递)」**,调平台 POST;成功/失败用现有 `apiErrorMessage`
- **可见性**:路由已为 OPS/SYS_ADMIN,与 I7 一致。
---
## 6. 契约
- 更新 `contracts/openapi/delivery-platform-api.json``POST /api/v1/callback-inbox/{id}/replay-webhook-delivery` 与响应 DTO(如 `status``receiptId`)。
- Webhook 内部路由 **不入** 对外 OpenAPI。
---
## 7. 测试与验收
| 层级 | 验收 |
|------|------|
| Webhook | 单测:`replayDeadByReceiptId` 对 DEAD/非 DEAD/缺行行为;集成或 MockMvc401/503 无 token。 |
| Platform | 单测或 Mock`CallbackInboxService` 在缺 receipt、Webhook 409/404 时映射 HTTP。 |
| 手工 | DEAD 一行 → 详情点重放 → 行变 `PENDING` → 调度成功后 `SENT`。 |
---
## 8. 修订记录
| 日期 | 说明 |
|------|------|
| 2026-04-06 | 初版:I8 DEAD 重放架构(平台代理 + Webhook 内部 API)。 |
@@ -0,0 +1,44 @@
# I9 实现审核 — 对照 [I9_WEBHOOK_DELIVERY_VISIBILITY_DESIGN.md](./I9_WEBHOOK_DELIVERY_VISIBILITY_DESIGN.md)
> **日期**2026-04-07。
---
## 1. 总评
| 设计条款 | 结论 |
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| Webhook 只读 `GET by-receipt` | **已落地**`PlatformDeliveryService#getStatusByReceiptId` + `WebhookPlatformDeliveryOpsController`。 |
| 平台代理 + JWT | **已落地**`GET /api/v1/callback-inbox/{id}/webhook-delivery``WebhookDeliveryReplayClient#fetchDeliveryStatus`。 |
| 前端不直连 Webhook | **已落地**`getCallbackWebhookDelivery` + 详情 `el-descriptions`;失败仅提示文案不遮断主详情。 |
| OpenAPI | **已导出**`UPDATE_OPENAPI=1` 更新 `contracts/openapi/delivery-platform-api.json`。 |
| 文档索引 / I7 复盘 | **已修订**[PARALLEL_ITERATION_INDEX](../PARALLEL_ITERATION_INDEX.md)、[I7_IMPLEMENTATION_REVIEW](./I7_IMPLEMENTATION_REVIEW.md) §2。 |
---
## 2. 后续(未在本迭代范围)
| 项 | 说明 |
| -------------- | ----------------------------------------------- |
| **Playwright** | 可对「有 webhookReceiptId 的详情」断言状态区块或 503 提示。 |
| **指标** | M5-F08:积压 `PENDING`/`DEAD` 仍以观测平台为准,本迭代仅 UI 只读。 |
---
## 3. 验证
- `JAVA_HOME`=JDK **17**`mvn -f services/pom.xml verify`
- `npm run build``web/delivery-platform-ui`
---
## 4. 修订记录
| 日期 | 说明 |
| ---------- | --- |
| 2026-04-07 | 初版。 |
@@ -0,0 +1,68 @@
# I9 设计 — Callback 详情可观测 Webhook 平台投递状态
> **角色**:架构审查与实现规划;承接 [I8](I8_WEBHOOK_DELIVERY_REPLAY_DESIGN.md)(重放入队)之后的 **只读观测** 缺口。
> **日期**2026-04-07。
---
## 1. 走查结论(相对 I6/I7/I8
| 主题 | 状态 | 说明 |
|------|------|------|
| I7 异步出库 | 已落地 | `PENDING` / `SENT` / `DEAD` 在 Webhook 库表。 |
| I8 重放 | 已落地 | 平台 POST 代理 + Webhook `replay`;**详情页仍看不到实时出库状态**。 |
| I7 复盘 §2 DEAD | **文档过时** | 已具备 UI 重放(I8);本迭代补 **状态只读** 并修订 I7 复盘表述。 |
| OpenAPI / JDK | 门禁 | 变更契约后须 **JDK 17** + `OpenApiContractSnapshotTest`;见 [contracts/README](../../../contracts/README.md)。 |
---
## 2. 目标与非目标
| **目标** | Ops 在 **Callback 详情** 查看与该收件箱 `webhookReceiptId` 对应的 **`webhook_platform_delivery` 行摘要**`status``attempts``lastError``nextRetryAt``updatedAt`),无需直连 Webhook 库。 |
| **非目标** | 浏览器调用 Webhook;改动比特契约;全量 M5-F08 仪表盘;Playwright 进 CI(列为 I9 后可选)。 |
---
## 3. API 与信任边界
与 I8 相同:**仅** `delivery-platform-api`**`LICENSE_WEBHOOK_BASE_URL` + `LICENSE_WEBHOOK_OPS_TOKEN`** 访问 Webhook **`/internal/**`**;前端只打平台 JWT。
| 组件 | 方法 | 路径 | 说明 |
|------|------|------|------|
| Webhook | `GET` | `/internal/v1/platform-deliveries/by-receipt/{receiptId}` | 同 `WebhookOpsTokenFilter`;无行则 **404**。 |
| Platform | `GET` | `/api/v1/callback-inbox/{id}/webhook-delivery` | `@PreAuthorize` 与 Inbox 一致(`OPS`/`SYS_ADMIN`);无 `webhookReceiptId`**400**;未配 Webhook 则 **503**。 |
**响应体(双方 JSON 字段一致,便于直连 RestClient 反序列化)**
- `receiptId` (long)
- `status` (string)
- `attempts` (int)
- `lastError` (string, 可 null)
- `nextRetryAt` (date-time, 可 null)
- `updatedAt` (date-time)
---
## 4. 实现要点
1. **Webhook**`PlatformDeliveryService#getStatusByReceiptId` 查表组装 Map/DTOController 增加 `GET`
2. **平台**`WebhookDeliveryReplayClient#fetchDeliveryStatus`(保留 Bean 名以减重构面);`CallbackInboxService#getWebhookDeliveryStatus``CallbackInboxController`
3. **前端**:详情页在存在 `webhookReceiptId` 时请求上述 GET,展示 `el-descriptions`;失败时 **不阻断** 主详情(提示或短文案)。
4. **契约**`UPDATE_OPENAPI=1` 导出快照并提交。
5. **文档**:更新 [PARALLEL_ITERATION_INDEX](../PARALLEL_ITERATION_INDEX.md) I9 行;修正 [I7_IMPLEMENTATION_REVIEW](./I7_IMPLEMENTATION_REVIEW.md) §2 DEAD 表述。
---
## 5. 验证
- `mvn -f services/pom.xml verify`Java 17
- `npm run build``web/delivery-platform-ui`
- Webhook / 平台单测各 ≥1 条覆盖 GET 与平台代理
---
## 6. 修订记录
| 日期 | 说明 |
|------|------|
| 2026-04-07 | 初版:I9 出库可见性架构与 API。 |