docs(p1r): 记录 Account 真实 API 收口

This commit is contained in:
zizi 2026-05-30 03:01:14 +08:00
parent 0dcaa9f581
commit 7c92a493d8
3 changed files with 1720 additions and 0 deletions

View File

@ -0,0 +1,261 @@
# P1R3 账户真实 API 规格计划
日期2026-05-28
## 背景
本次进入 P1R-3 Account Real API spec + plan 阶段,只完成 Account 域真实 API 的事实盘点、规格和执行计划,不进入代码实现。
P1R-3 的核心不是继续增强 `MuseContractPersistenceService`,也不是把 `generic_persistence` 包装成业务结果,而是把 Account 域从“通用持久化可响应”推进到“真实账户业务 API 可验收”的规格和计划阶段。
## 执行位置
```text
/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0
```
分支:
```text
dev/1.0.0
```
本阶段开始和收口时,工作树只包含本阶段新增文档改动,没有实现代码改动。
## 已验证事实
P1R 覆盖矩阵中 Account 域当前状态:
```text
33 generic_persistence / incomplete
```
同时保持:
- completed operations = 0
- Content 51 个 operation 为 `dedicated / needs_verification`
- Meta 16 个 operation 为 `dedicated / needs_verification`
当前代码事实:
- 仓库没有 `muse-cloud/muse-module-account/**`
- Account 当前真实 owner 应落在 `muse-module-member` 的 Account 子域。
- 当前 Account 合同入口仍是 `AdminMuseAccountContractController``AppMuseAccountContractController`
- 两个 Controller 仍通过 `MuseContractPersistenceService` 通用持久化处理 Account 路由,不是 P1R 完成证据。
当前 DDL 事实:
- V2 已有 `muse_member_entitlement``muse_member_entitlement_audit_log``muse_member_quota``muse_member_usage_record``muse_member_security_event``muse_member_new_api_binding`
- P1R-3 计划新增 `muse_account_profile``muse_account_audit``muse_account_command``muse_account_balance_snapshot``muse_account_quota_request``muse_account_integration_call``muse_account_call_attribution_job``muse_account_call_attribution_item``muse_account_export_task``muse_account_download_credential``muse_account_security_event_ack``muse_account_record_projection`
- purchase / license / publish records 统一使用 `muse_account_record_projection(record_type=...)` 作为 Account 视角读模型,不在 P1R-3 实现 Market 主流程。
## 本阶段产物
- 规格:`docs/superpowers/specs/2026-05-28-P1R-3-account-real-api-design.md`
- 计划:`docs/superpowers/plans/2026-05-28-P1R-3-account-real-api.md`
## 关键设计结论
- P1R-3 目标状态只能是 `dedicated / needs_verification`,不能写成 `completed`
- Account owner 采用 `muse-module-member/account` 子域,不新增孤立 `muse-module-account`
- `muse_account_profile` 承载 profile 扩展事实和 `expectedVersion` 乐观锁,不污染 `member_user`
- `muse_account_audit``muse_account_command` 是 commandId 幂等、requestHash 冲突和审计的必需基础设施。
- New-API binding / recheck 必须写内部 integration call并通过 `bindingId` / `jobId` 关联 `correlationId`;现有 OpenAPI 响应不新增 `correlationId`
- New-API、call attribution、FileService unavailable 只能通过合同内业务错误、内部 job 状态或 OpenAPI 已声明状态表达,不能返回未声明的 `pending` 等状态,也不能 fake success。
- `appDownloadExport` 成功响应按 OpenAPI 返回 `application/octet-stream`,错误响应仍走统一 `code/data/msg`
- AI runtime / task / SSE、Knowledge / RAGFlow、Market 主流程、统一 Events SSE 均不属于 P1R-3。
## Review 结果
本阶段使用 fresh subagent 做了两轮文档 review并按 review 结果修正后复审通过。
Spec compliance review 最终结果:
```text
SPEC REVIEW PASS
```
Quality review 最终结果:
```text
QUALITY REVIEW PASS
```
过程中修正的关键问题:
- 补齐 New-API binding create / recheck inline requestBody 的 typed VO。
- 明确 unavailable / failed / pending 等内部状态不得进入 OpenAPI 未声明 response status。
- 将 app quota request DTO 改为 `AppQuotaRequestCreateReqVO`,避免复用 admin DTO。
- 增加 `UnavailableNewApiAccountFacade``UnavailableMarketAccountProjectionFacade``UnavailableAccountFileServiceFacade` 的默认边界和测试要求。
- 移除会诱导创建独立 AI / Knowledge facade 的过宽表述AI / Knowledge 只通过 attribution source unavailable 边界表达。
- 移除 `-Dsurefire.failIfNoSpecifiedTests=false`,并要求关键指定测试命令用 `test -f` 防止缺失测试被掩盖。
## 当前阶段边界
本阶段只完成规格、计划、review 和 memory 留痕,没有实现代码。
未修改:
- `docs/api-contracts/account/openapi.yaml`
- `docs/superpowers/reports/p1r-api-coverage.*`
- `muse-cloud/scripts/**`
- `muse-cloud/muse-module-member/**` 实现代码
- `muse-cloud/sql/muse/**`
当前不应把任何 Account operation 标为 `completed`
## 残余风险
- 未运行真实 PostgreSQL / Flyway 验证P1R-3 DDL 仍需实现阶段验证。
- 未接真实 New-API runtimebinding / recheck / quota request 仍需保持 `needs_verification`
- 未接真实 FileService runtimeexport task / download credential 仍需保持 `needs_verification`
- 外部 Market / Content / AI / Knowledge owner 未闭合时purchase / license / publish / attribution 只能走 Account 读模型和 unavailable 边界,不能标为 completed。
## 下一步(规格计划阶段记录)
进入 P1R-3 实现前,应从 `docs/superpowers/plans/2026-05-28-P1R-3-account-real-api.md` Task 1 开始,按 fresh subagent + spec compliance review + quality review 执行。
实现阶段完成口径:
- Account 33 个 operation 退出 `generic_persistence`
- Account 33 个 operation 全部为 `dedicated / needs_verification`
- Content 51 个 operation 保持 `dedicated / needs_verification`
- Meta 16 个 operation 保持 `dedicated / needs_verification`
- 不修改脚本掩盖缺口。
- 不把 Account 标为 `completed`
## 2026-05-29 实现阶段收口更新
### 实现任务范围
本轮实现阶段在以下真实 worktree 执行:
```text
/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0
```
`4d9a242..HEAD` 已覆盖 P1R-3 Task 1-12
1. Preflight 与覆盖基线冻结。
2. Account 基础设施与 V11 追加 DDL。
3. DTO / DAL / Mapper 骨架。
4. App `me` / `profile` 与 Admin 用户摘要。
5. 权益、配额、余额、用量。
6. New-API binding、recheck、quota request、integration call。
7. call attribution job。
8. purchase / license / publish records Account 投影。
9. security events。
10. export task / download credential。
11. Account 合同兜底退役。
12. P1R-3 覆盖门禁与报告对账。
Task 10 最新事实:
- 已新增 Account export/download Controller、Service、FileService facade、unavailable facade、凭证与导出任务相关 DAL 和测试。
- 下载凭证边界覆盖 credential hash、owner、expiresAt、revoked、consumed、source blocked。
- 高敏导出在缺少可信 step-up 证明时 fail closed。
- `appDownloadExport` 成功响应按 OpenAPI 返回 `application/octet-stream`;错误仍走统一 `code/data/msg`
- unavailable FileService 不返回伪造文件流。
Task 11 最新事实:
- 已删除旧 Account 合同兜底 Controller。
- 已新增 `P1rAccountRouteOwnershipTest`,证明 33 个 P1R-3 Account 路由进入 dedicated Controller不再回落到 `MuseContractPersistenceService`
Task 12 最新事实:
- 已新增 `P1rAccountRealApiGateTest`
- 已更新 `docs/superpowers/reports/p1r-api-coverage.json``docs/superpowers/reports/p1r-api-coverage.md`
- Account 33 个 operation 统一收口到 `dedicated / needs_verification`,仍保留真实运行态验收缺口。
### Coverage 前后状态
实现起点 `4d9a242`
```text
Account 33 generic_persistence / incomplete
Content 51 dedicated / needs_verification
Meta 16 dedicated / needs_verification
```
当前 `HEAD`
```text
Account 33 dedicated / needs_verification
Content 51 dedicated / needs_verification
Meta 16 dedicated / needs_verification
```
该状态只证明仓库内 dedicated 入口、覆盖报告和门禁已收口,不代表 PostgreSQL / Flyway、New-API、FileService 或端到端运行态已验收。
### Review 结果
本轮实现按计划要求在每个实现 Task 后执行:
1. spec compliance review。
2. code quality review。
已记录的收口事实:
- Task 10 在修正高敏导出 fail-closed、导出凭证消费边界、HMAC 凭证派生和 FileService 异常处理后spec compliance review 与 code quality review 均通过。
- Task 11 的 spec compliance review 与 code quality review 均通过。
- Task 12 的 spec compliance review 与 code quality review 均通过。
这些 review 结果只证明 P1R-3 代码、报告和门禁符合当前 spec / plan不代表外部 runtime owner 已闭合。
### 测试命令和结果
本阶段已记录的验证命令与结果:
```bash
python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check
```
结果:通过;覆盖报告保持 Account 33、Content 51、Meta 16 均为 `dedicated / needs_verification`
```bash
cd muse-cloud
test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountRealApiGateTest.java
test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountRouteOwnershipTest.java
test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rApiCoverageReportTest.java
test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentRealApiGateTest.java
test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMetaRealApiGateTest.java
JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \
mvn -o test -pl muse-server -am \
-Dtest=P1rApiCoverageReportTest,P1rContentRealApiGateTest,P1rMetaRealApiGateTest,P1rAccountRealApiGateTest,P1rAccountRouteOwnershipTest \
-Dsurefire.failIfNoSpecifiedTests=false
```
结果通过22 tests0 failures。
说明:原计划中的 `-pl muse-server -am -Dtest=...` 命令在上游 reactor 模块没有同名测试时会触发 `failIfNoSpecifiedTests` 问题。本轮通过 `test -f` 先校验目标测试存在,再局部追加 `-Dsurefire.failIfNoSpecifiedTests=false` 避免上游模块误失败;这不是全局放宽测试缺失门禁。
```bash
cd muse-cloud
JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \
mvn -o test -pl muse-module-member/muse-module-member-server -am
```
结果通过member-server 272 tests0 failures。
```bash
git diff --check
```
结果:通过,无空白错误输出。
### 未验证 runtime 风险
- 未验证真实 PostgreSQL / Flyway 执行V11 仍需在真实数据库迁移链路确认。
- 未接真实 New-API runtimebinding / recheck / quota request / integration call 仍保持 `needs_verification`
- 未接真实 FileServiceexport task / download credential 只能证明边界和状态机,不能证明真实文件交付。
- Market / AI / Knowledge owner 未闭合purchase / license / publish / attribution 只能走 Account 投影或 unavailable 边界。
- 端到端 auth、错误响应、SSE 与跨域 runtime 行为仍需后续阶段验收。
### 变更面记录
- OpenAPI未修改 `docs/api-contracts/account/openapi.yaml`
- Scanner未修改 `muse-cloud/scripts/**`
- SQL新增 `muse-cloud/sql/muse/V11__extend_account_real_api_schema.sql`
- Java新增 Account Controller / Service / DTO / VO / DAL / Mapper / Facade / Guard / AutoConfiguration 及 P1R gate tests删除旧 Account 合同兜底 Controller。
- Coverage更新 `docs/superpowers/reports/p1r-api-coverage.json``docs/superpowers/reports/p1r-api-coverage.md`

View File

@ -0,0 +1,895 @@
# P1R-3 Account Real API Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use `superpowers:subagent-driven-development` to implement this plan task-by-task. `superpowers:executing-plans` is acceptable only when subagents are unavailable. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 将 Account 33 个 operation 从 `generic_persistence / incomplete` 推进到专用 Account Controller / Service / DTO / DAL / DDL / 测试可验收的 `dedicated / needs_verification`
**Architecture:** 以 `muse-module-member` 内 Account 子域为 owner新增 Account 专用 app/admin 入口和应用服务。通用 `MuseContractPersistenceService` 只能作为迁移保护网,不能作为 P1R-3 完成路径New-API、Market、AI、Knowledge、FileService 等外部 owner 不可用时返回合同内 `CommonResult` 业务错误或 pending job不伪造完成。
**Tech Stack:** Java 21、Spring Boot、Yudao `CommonResult<T>`、Spring Security `@PreAuthorize`、MyBatis Plus、PostgreSQL/Flyway、JUnit 5、Mockito/MockMvc、P1R coverage JSON。
**Implementation Status:** 2026-05-29 已按 Task 1-12 执行并完成 review 收口Account 33 个 operation 当前为 `dedicated / needs_verification`,真实 runtime owner 未闭合前不得写成 `completed`。实现阶段留痕见 `docs/memorys/2026-05-28-P1R3账户真实API规格计划.md`
---
## 0. 执行边界
- 工作目录:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`
- 分支:`dev/1.0.0`
- 不在 `/Users/qingse/Sync/local-git/oh-my-muse``feature/p1-muse-cloud-backend-20260524` checkout 上落实现。
- 不修改 `muse-cloud/scripts/**` 来改变 P1R 判断口径。
- 不修改 `docs/api-contracts/account/openapi.yaml`,除非先单独说明合同纠偏原因、影响范围和迁移风险。
- 不破坏 Content 当前 51 个 `dedicated / needs_verification`
- 不破坏 Meta 当前 16 个 `dedicated / needs_verification`
- 不把 Account operation 标为 `completed`
- 本文件是后续实现阶段的执行计划;本次 P1R-3 规格计划会话只产出 spec + plan + review + memory收到实现指令后必须按 Task 1-12 执行。
## 1. 当前事实盘点
| operationId | Method | Path | 当前状态 | 当前实现 owner | 计划处理 |
|---|---|---|---|---|---|
| `adminListAccountUsers` | GET | `/admin-api/muse/account/users` | `generic_persistence / incomplete` | `AdminMuseAccountContractController` + `MuseContractPersistenceService` | Task 4 |
| `adminGetUserEntitlements` | GET | `/admin-api/muse/account/users/{userId}/entitlements` | `generic_persistence / incomplete` | 同上 | Task 5 |
| `adminCreateQuotaAdjustment` | POST | `/admin-api/muse/account/users/{userId}/quota-adjustments` | `generic_persistence / incomplete` | 同上 | Task 5 |
| `adminListQuotaAdjustments` | GET | `/admin-api/muse/account/users/{userId}/quota-adjustments` | `generic_persistence / incomplete` | 同上 | Task 5 |
| `adminListNewApiBindings` | GET | `/admin-api/muse/account/new-api-bindings` | `generic_persistence / incomplete` | 同上 | Task 6 |
| `adminCreateNewApiBinding` | POST | `/admin-api/muse/account/users/{userId}/new-api-binding` | `generic_persistence / incomplete` | 同上 | Task 6 |
| `adminCreateQuotaRequest` | POST | `/admin-api/muse/account/users/{userId}/quota-requests` | `generic_persistence / incomplete` | 同上 | Task 6 |
| `adminGetBalanceSnapshots` | GET | `/admin-api/muse/account/users/{userId}/balance-snapshots` | `generic_persistence / incomplete` | 同上 | Task 5 |
| `adminCreateCallAttributionJob` | POST | `/admin-api/muse/account/call-attribution-jobs` | `generic_persistence / incomplete` | 同上 | Task 7 |
| `adminGetCallAttributionJob` | GET | `/admin-api/muse/account/call-attribution-jobs/{jobId}` | `generic_persistence / incomplete` | 同上 | Task 7 |
| `adminGetIntegrationCallByCorrelation` | GET | `/admin-api/muse/account/integration-calls/by-correlation/{correlationId}` | `generic_persistence / incomplete` | 同上 | Task 6 |
| `adminListUsageRecords` | GET | `/admin-api/muse/account/usage-records` | `generic_persistence / incomplete` | 同上 | Task 5 |
| `adminListPurchaseRecords` | GET | `/admin-api/muse/account/purchase-records` | `generic_persistence / incomplete` | 同上 | Task 8 |
| `getCurrentUser` | GET | `/app-api/muse/me` | `generic_persistence / incomplete` | `AppMuseAccountContractController` + `MuseContractPersistenceService` | Task 4 |
| `getProfile` | GET | `/app-api/muse/profile` | `generic_persistence / incomplete` | 同上 | Task 4 |
| `updateProfile` | PATCH | `/app-api/muse/profile` | `generic_persistence / incomplete` | 同上 | Task 4 |
| `getAppEntitlements` | GET | `/app-api/muse/account/entitlements` | `generic_persistence / incomplete` | 同上 | Task 5 |
| `getAppUsage` | GET | `/app-api/muse/account/usage` | `generic_persistence / incomplete` | 同上 | Task 5 |
| `getAppNewApiBinding` | GET | `/app-api/muse/account/new-api-binding` | `generic_persistence / incomplete` | 同上 | Task 6 |
| `appRecheckNewApiBinding` | POST | `/app-api/muse/account/new-api-binding/recheck` | `generic_persistence / incomplete` | 同上 | Task 6 |
| `getAppBalanceSnapshots` | GET | `/app-api/muse/account/balance-snapshots` | `generic_persistence / incomplete` | 同上 | Task 5 |
| `appCreateQuotaRequest` | POST | `/app-api/muse/account/quota-requests` | `generic_persistence / incomplete` | 同上 | Task 6 |
| `appGetQuotaRequest` | GET | `/app-api/muse/account/quota-requests/{requestId}` | `generic_persistence / incomplete` | 同上 | Task 6 |
| `appGetIntegrationCallByCorrelation` | GET | `/app-api/muse/account/integration-calls/by-correlation/{correlationId}` | `generic_persistence / incomplete` | 同上 | Task 6 |
| `appListPurchases` | GET | `/app-api/muse/account/purchases` | `generic_persistence / incomplete` | 同上 | Task 8 |
| `appListLicenses` | GET | `/app-api/muse/account/licenses` | `generic_persistence / incomplete` | 同上 | Task 8 |
| `appListPublishRecords` | GET | `/app-api/muse/account/publish-records` | `generic_persistence / incomplete` | 同上 | Task 8 |
| `appListSecurityEvents` | GET | `/app-api/muse/account/security-events` | `generic_persistence / incomplete` | 同上 | Task 9 |
| `appGetSecurityEvent` | GET | `/app-api/muse/account/security-events/{eventId}` | `generic_persistence / incomplete` | 同上 | Task 9 |
| `appAcknowledgeSecurityEvent` | POST | `/app-api/muse/account/security-events/{eventId}/acknowledge` | `generic_persistence / incomplete` | 同上 | Task 9 |
| `appCreateExportTask` | POST | `/app-api/muse/account/export-tasks` | `generic_persistence / incomplete` | 同上 | Task 10 |
| `appGetExportTask` | GET | `/app-api/muse/account/export-tasks/{taskId}` | `generic_persistence / incomplete` | 同上 | Task 10 |
| `appDownloadExport` | GET | `/app-api/muse/account/downloads/{credentialId}` | `generic_persistence / incomplete` | 同上 | Task 10 |
## 2. 目标文件结构
| 操作 | 路径 | 责任 |
|---|---|---|
| Modify | `muse-cloud/muse-module-member/muse-module-member-server/pom.xml` | 确认已有 validation/security/mybatis/test 依赖,按需补测试依赖 |
| Create | `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/*.java` | Account 管理端 dedicated API |
| Create | `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/*.java` | Account 用户端 dedicated API |
| Modify | `AdminMuseAccountContractController.java` | 移除或避让 P1R-3 已 dedicated 路径 |
| Modify | `AppMuseAccountContractController.java` | 移除或避让 P1R-3 已 dedicated 路径 |
| Create | `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/*/account/vo/*.java` | Account OpenAPI DTO |
| Create | `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/*.java` | Account 应用服务 |
| Create | `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/facade/*.java` | New-API / Market projection / FileService / AttributionSource 外部边界AI / Knowledge 只能经 `AccountAttributionSourceFacade` 的 unavailable 默认实现表达,不新增独立 AI / Knowledge facade |
| Create | `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/domain/account/*.java` | version、owner、permission、quota、download guard |
| Create | `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/*.java` | Account DO |
| Create | `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/*.java` | Account Mapper |
| Create | `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/convert/account/AccountConvert.java` | DO/facade 到 VO 组装 |
| Modify | `muse-cloud/muse-module-member/muse-module-member-api/src/main/java/cn/iocoder/muse/module/member/enums/ErrorCodeConstants.java` | Account 错误码 |
| Create | `muse-cloud/sql/muse/V11__extend_account_real_api_schema.sql` | Account 真实 API 追加表和约束 |
| Create | `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountRealApiGateTest.java` | P1R-3 阶段门禁 |
## 3. 子代理执行规则
每个 Task 使用 fresh subagent模型 `gpt-5.5`reasoning `xhigh`,等待超时 30 分钟。
每个实现 Task 完成后必须两轮 review
1. spec compliance review检查是否符合 `docs/superpowers/specs/2026-05-28-P1R-3-account-real-api-design.md` 和本计划。
2. code quality review检查最小改动、可维护性、测试质量、无脚本掩盖。
review 未通过不能进入下一 Task。所有 review 发现的问题必须由新的修复子代理处理,再重新 review。
## Task 1: Preflight 与覆盖基线冻结
**Files:**
- Read: `docs/superpowers/reports/p1r-api-coverage.json`
- Read: `docs/superpowers/reports/p1r-api-coverage.md`
- Read: `docs/api-contracts/account/openapi.yaml`
- Read: `muse-cloud/muse-module-member/**`
- Read: `muse-cloud/sql/muse/**`
- [ ] **Step 1: 核实工作面**
Run:
```bash
git status --short --branch
```
Expected:
```text
## dev/1.0.0...origin/dev/1.0.0
```
除本规格、本计划和本阶段 memory 尚未提交时可能出现的文档外,无其他输出。
- [ ] **Step 2: 统计 Account operation 状态**
Run:
```bash
jq -r '[.operations[] | select(.targetStage == "P1R-3 Account Real API" or .domain == "account")] | group_by(.implementationStatus + "/" + .completionStatus)[] | [(.[0].implementationStatus + "/" + .[0].completionStatus), length] | @tsv' docs/superpowers/reports/p1r-api-coverage.json
```
Expected:
```text
generic_persistence/incomplete 33
```
- [ ] **Step 3: 冻结 operation 清单**
Run:
```bash
jq -r '.operations[] | select(.targetStage == "P1R-3 Account Real API" or .domain == "account") | [.operationId, .method, .path, .implementationStatus, .completionStatus] | @tsv' docs/superpowers/reports/p1r-api-coverage.json
```
Expected: 输出 33 行,且 operationId 与本计划第 1 节完全一致。
- [ ] **Step 4: 检查脚本目录无改动**
Run:
```bash
git status --short -- muse-cloud/scripts
```
Expected: 无输出。
## Task 2: Account 基础设施与 DDL
**Files:**
- Read/Modify: `muse-cloud/muse-module-member/muse-module-member-server/pom.xml`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/domain/account/AccountApiVersionGuard.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/domain/account/AccountOwnerGuard.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/domain/account/AccountQuotaGuard.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/domain/account/AccountDownloadGuard.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountCommandService.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountCommandServiceImpl.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountAuditService.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountAuditServiceImpl.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-api/src/main/java/cn/iocoder/muse/module/member/enums/ErrorCodeConstants.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountCommandDO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountAuditDO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountCommandMapper.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountAuditMapper.java`
- Create: `muse-cloud/sql/muse/V11__extend_account_real_api_schema.sql`
- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountMigrationSqlTest.java`
- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/domain/account/AccountApiVersionGuardTest.java`
- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/domain/account/AccountOwnerGuardTest.java`
- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/domain/account/AccountQuotaGuardTest.java`
- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/domain/account/AccountDownloadGuardTest.java`
- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountCommandServiceTest.java`
- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountAuditServiceTest.java`
- [ ] **Step 1: 确认模块依赖**
`muse-module-member-server/pom.xml` 已有:
- `muse-spring-boot-starter-security`
- `muse-spring-boot-starter-mybatis`
- `spring-boot-starter-validation`
- `muse-spring-boot-starter-test`
如需新增依赖,先说明原因。
- [ ] **Step 2: 增加 Account 错误码**
`ErrorCodeConstants.java` 增加本阶段使用的 Account 错误码,至少覆盖 `ACCOUNT_API_VERSION_UNSUPPORTED``ACCOUNT_USER_NOT_EXISTS``ACCOUNT_RESOURCE_FORBIDDEN``ACCOUNT_COMMAND_ID_REQUIRED``ACCOUNT_COMMAND_ID_CONFLICT``ACCOUNT_VERSION_CONFLICT``ACCOUNT_NEW_API_BINDING_NOT_EXISTS``ACCOUNT_NEW_API_UNAVAILABLE``ACCOUNT_INTEGRATION_CALL_NOT_EXISTS``ACCOUNT_QUOTA_EXHAUSTED``ACCOUNT_DOWNLOAD_CREDENTIAL_INVALID``ACCOUNT_EXPORT_SOURCE_BLOCKED`
- [ ] **Step 3: 实现 API version guard**
`AccountApiVersionGuard.requireVersion(String version)` 只接受 `"1"`,缺失或未知版本抛 `ACCOUNT_API_VERSION_UNSUPPORTED`
- [ ] **Step 4: 实现 command service**
规则:
- `buildRequestHash(Object payload)` 使用稳定 JSON + SHA-256。
- `reserveCommand(...)` 预占 commandId。
- `getReplayCommand(...)` 相同 hash 返回旧记录。
- 相同 commandId 不同 hash 抛 `ACCOUNT_COMMAND_ID_CONFLICT`
- envelope 必须校验 operationId、actor、owner、targetType、targetId。
- [ ] **Step 5: 实现 audit service**
记录字段必须覆盖 spec 第 10 节,不记录 New-API token、Prompt/Response 全文、外部订单原文、敏感 IP 明文。
- [ ] **Step 6: 编写 V11 迁移**
至少新增:
- `muse_account_command`
- `muse_account_audit`
- `muse_account_profile`
- `muse_account_balance_snapshot`
- `muse_account_quota_request`
- `muse_account_integration_call`
- `muse_account_call_attribution_job`
- `muse_account_call_attribution_item`
- `muse_account_export_task`
- `muse_account_download_credential`
- `muse_account_security_event_ack`
- `muse_account_record_projection`
关键约束:
- command 唯一键 `(tenant_id, command_id)`
- account audit 必须索引 `account_user_id``operation_id``correlation_id`
- account profile 必须以 `account_user_id` 唯一,并用 `profile_version` 支撑 `expectedVersion`
- record projection 必须用 `record_type` 区分 `purchase``license``publish`,并保存 `source_table` / `source_id` / `source_revision`
- app 可见事实必须索引 `account_user_id`
- correlation 查询必须索引 `correlation_id`
- download credential 必须 hash 存储,不落明文 token。
- [ ] **Step 7: 创建迁移 SQL 门禁测试**
`P1rAccountMigrationSqlTest` 至少校验:
- `V11__extend_account_real_api_schema.sql` 存在。
- V11 包含 `muse_account_command``muse_account_audit``muse_account_profile``muse_account_balance_snapshot``muse_account_quota_request``muse_account_integration_call``muse_account_call_attribution_job``muse_account_call_attribution_item``muse_account_export_task``muse_account_download_credential``muse_account_security_event_ack``muse_account_record_projection`
- command 唯一键、profile version、record projection `record_type`、download credential hash、account owner 索引和 correlation 索引均存在。
- 测试不得只检查文件名,必须检查关键表名和约束片段。
- [ ] **Step 8: 运行迁移 SQL 门禁**
Run:
```bash
set -e
cd muse-cloud
test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountMigrationSqlTest.java
JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \
mvn -o test -pl muse-server -am \
-Dtest=P1rAccountMigrationSqlTest
```
Expected: V11 迁移表、索引、唯一键、hash credential 字段、profile version、audit 字段和 record projection 约束均通过门禁;若 `P1rAccountMigrationSqlTest` 不存在,必须失败。
- [ ] **Step 9: 运行聚焦测试**
Run:
```bash
set -e
cd muse-cloud
test -f muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/domain/account/AccountApiVersionGuardTest.java
test -f muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/domain/account/AccountOwnerGuardTest.java
test -f muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/domain/account/AccountQuotaGuardTest.java
test -f muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/domain/account/AccountDownloadGuardTest.java
test -f muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountCommandServiceTest.java
test -f muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountAuditServiceTest.java
JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \
mvn -o test -pl muse-module-member/muse-module-member-server -am \
-Dtest=AccountApiVersionGuardTest,AccountOwnerGuardTest,AccountQuotaGuardTest,AccountDownloadGuardTest,AccountCommandServiceTest,AccountAuditServiceTest
```
Expected: 测试通过。
## Task 3: DTO / DAL / Mapper 骨架
**Files:**
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminAccountUserSummaryRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminUserEntitlementDetailRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/QuotaAdjustmentReqVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/QuotaAdjustmentResultRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/QuotaAdjustmentLedgerEntryRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/NewApiBindingSummaryRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/NewApiBindingCreateReqVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/NewApiBindingResultRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminQuotaRequestCreateReqVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminQuotaRequestResultRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminBalanceSnapshotEntryRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminIntegrationCallDetailRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/CallAttributionJobCreateReqVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/CallAttributionJobResultRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/CallAttributionJobDetailRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminUsageRecordRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminPurchaseRecordRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppCurrentUserSummaryRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppProfileRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppProfileUpdateReqVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppEntitlementRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppUsageSummaryRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppNewApiBindingSummaryRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppNewApiBindingRecheckReqVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppNewApiBindingRecheckRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppQuotaRequestCreateReqVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppQuotaRequestResultRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/QuotaRequestStatusRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppBalanceSnapshotEntryRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppIntegrationCallDetailRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppPurchaseRecordRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppLicenseRecordRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppPublishRecordRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/SecurityEventSummaryRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/SecurityEventDetailRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/SecurityEventAcknowledgeReqVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/SecurityEventAcknowledgeResultRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AccountExportTaskCreateReqVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AccountExportTaskResultRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AccountExportTaskDetailRespVO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountProfileDO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/MemberEntitlementDO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/MemberEntitlementAuditLogDO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/MemberQuotaDO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/MemberUsageRecordDO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/MemberSecurityEventDO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/MemberNewApiBindingDO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountBalanceSnapshotDO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountQuotaRequestDO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountIntegrationCallDO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountCallAttributionJobDO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountCallAttributionItemDO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountRecordProjectionDO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountSecurityEventAckDO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountExportTaskDO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountDownloadCredentialDO.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountProfileMapper.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/MemberEntitlementMapper.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/MemberEntitlementAuditLogMapper.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/MemberQuotaMapper.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/MemberUsageRecordMapper.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/MemberSecurityEventMapper.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/MemberNewApiBindingMapper.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountBalanceSnapshotMapper.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountQuotaRequestMapper.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountIntegrationCallMapper.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountCallAttributionJobMapper.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountCallAttributionItemMapper.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountRecordProjectionMapper.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountSecurityEventAckMapper.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountExportTaskMapper.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountDownloadCredentialMapper.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/convert/account/AccountConvert.java`
- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/convert/account/AccountConvertTest.java`
- [ ] **Step 1: 按 OpenAPI 创建 VO**
覆盖 spec 第 6.3 节列出的最小 DTO命名与已有项目 VO 风格一致。
- [ ] **Step 2: 创建 DO / Mapper**
覆盖 V2 表和 V11 新表V2 表包括 `muse_member_entitlement``muse_member_entitlement_audit_log``muse_member_quota``muse_member_usage_record``muse_member_security_event``muse_member_new_api_binding`。对 `member_user`、Market 上游来源只创建必要 Mapper 方法,不复制其他模块状态机。
- [ ] **Step 3: 创建 Account assembler**
统一处理 ID 字符串化、分页、脱敏、枚举映射、时间字段。
- [ ] **Step 4: DTO 对齐测试**
新增单元测试校验关键 response shape不返回 `Map<String,Object>`
## Task 4: App me / profile 与 Admin 用户摘要
**Files:**
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/AppAccountProfileController.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/AdminAccountUserController.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountProfileService.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountProfileServiceImpl.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountProfileDO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountProfileMapper.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppCurrentUserSummaryRespVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppProfileRespVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppProfileUpdateReqVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminAccountUserSummaryRespVO.java`
- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountProfileServiceTest.java`
- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/controller/app/account/AppAccountProfileControllerTest.java`
- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/controller/admin/account/AdminAccountUserControllerTest.java`
- [ ] **Step 1: 实现 `/app-api/muse/me`**
返回 `CurrentUserSummary` 的基础账户资料,来源为当前登录用户和 `muse_account_profile`。权益摘要、安全风险数、配额状态、New-API binding 等跨服务聚合字段在 Task 5、Task 6、Task 9 中回填Task 4 不得为了完整摘要提前实现后续 task 的 service。
- [ ] **Step 2: 实现 `/app-api/muse/profile` GET/PATCH**
PATCH 必须校验 `commandId``expectedVersion`,联系方式不在本接口直接改。
profile 扩展事实写入 `muse_account_profile`,不得通过扩字段污染 `member_user` 基础会员表。
- [ ] **Step 3: 实现 `/admin-api/muse/account/users`**
查询用户基础摘要,来源为 `member_user``muse_account_profile` 和已存在的账号状态字段。权益来源、配额状态、New-API binding、风险标记在 Task 5、Task 6、Task 9 中通过修改 `AdminAccountUserController``AdminAccountUserSummaryRespVO` 回填Task 4 只预留可空字段,不跨 task 实现聚合服务。
- [ ] **Step 4: 测试**
覆盖当前用户、profile 更新、owner 只能是自己、版本冲突、command replay/conflict、admin 权限。
## Task 5: 权益 / 配额 / 余额 / 用量
**Files:**
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountEntitlementService.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountEntitlementServiceImpl.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountQuotaService.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountQuotaServiceImpl.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountUsageService.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountUsageServiceImpl.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/AdminAccountUserController.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/AdminAccountUsageController.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/AppAccountEntitlementController.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountBalanceSnapshotDO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountBalanceSnapshotMapper.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminUserEntitlementDetailRespVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/QuotaAdjustmentReqVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/QuotaAdjustmentResultRespVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/QuotaAdjustmentLedgerEntryRespVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminUsageRecordRespVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminAccountUserSummaryRespVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminBalanceSnapshotEntryRespVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppEntitlementRespVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppUsageSummaryRespVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppBalanceSnapshotEntryRespVO.java`
- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountEntitlementServiceTest.java`
- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountQuotaServiceTest.java`
- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountUsageServiceTest.java`
- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/controller/admin/account/AdminAccountUsageControllerTest.java`
- [ ] **Step 1: 权益查询**
实现:
- `adminGetUserEntitlements`
- `getAppEntitlements`
必须从 `muse_member_entitlement` / `muse_member_quota` 装配 OpenAPI DTO。
同时回填 `AdminAccountUserSummaryRespVO` 的权益来源和配额状态字段,不改 Task 4 的基础 owner 逻辑。
- [ ] **Step 2: 配额调整**
实现:
- `adminCreateQuotaAdjustment`
- `adminListQuotaAdjustments`
必须记录 before/after snapshot、operator summary、reason、correlationId、auditStatus。
- [ ] **Step 3: 余额快照**
实现:
- `adminGetBalanceSnapshots`
- `getAppBalanceSnapshots`
必须从 `muse_account_balance_snapshot` 读取;没有 New-API runtime 时只返回已有快照,不即时伪造远端余额。
- [ ] **Step 4: 用量摘要**
实现:
- `adminListUsageRecords`
- `getAppUsage`
必须从 `muse_member_usage_record` 聚合app 只查本人。
- [ ] **Step 5: 测试**
覆盖分页、owner guard、quota delta、before/after snapshot、usage aggregation。
## Task 6: New-API binding / quota request / integration call
**Files:**
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/AdminAccountNewApiController.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/AppAccountNewApiController.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/AdminAccountUsageController.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountNewApiBindingService.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountNewApiBindingServiceImpl.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountQuotaService.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountQuotaServiceImpl.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountUsageService.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountUsageServiceImpl.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/facade/NewApiAccountFacade.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/facade/UnavailableNewApiAccountFacade.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountQuotaRequestDO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountIntegrationCallDO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountQuotaRequestMapper.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountIntegrationCallMapper.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/NewApiBindingSummaryRespVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/NewApiBindingCreateReqVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/NewApiBindingResultRespVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminQuotaRequestCreateReqVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminQuotaRequestResultRespVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminIntegrationCallDetailRespVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminAccountUserSummaryRespVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppNewApiBindingSummaryRespVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppNewApiBindingRecheckReqVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppNewApiBindingRecheckRespVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppQuotaRequestCreateReqVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppQuotaRequestResultRespVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/QuotaRequestStatusRespVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppIntegrationCallDetailRespVO.java`
- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountNewApiBindingServiceTest.java`
- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/controller/app/account/AppAccountNewApiControllerTest.java`
- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/controller/admin/account/AdminAccountNewApiControllerTest.java`
- [ ] **Step 1: New-API binding 查询与创建**
实现:
- `adminListNewApiBindings`
- `adminCreateNewApiBinding`
- `getAppNewApiBinding`
不得暴露 token/provider route。
`adminCreateNewApiBinding` 必须使用 `NewApiBindingCreateReqVO` 接收 inline requestBody校验 `commandId``forceRefresh``reason`,不得用 `Map` 或裸对象承接请求。
创建或刷新 binding 时必须写 `muse_account_integration_call`,内部 `correlation_id``binding_id` 关联;响应只能返回 OpenAPI 已定义的 `NewApiBindingResult` 字段,不新增 `correlationId`
同时回填 `AdminAccountUserSummaryRespVO` 的 New-API binding 摘要字段。
默认 `UnavailableNewApiAccountFacade` 不得伪造远端同步成功:创建/刷新无法满足 OpenAPI `created/refreshed/idempotent_hit/idempotency_conflict`HTTP 响应返回合同内业务错误 `ACCOUNT_NEW_API_UNAVAILABLE`;内部 integration call 可记录 `unavailable` / `failed` 状态,但这些状态不得进入 `NewApiBindingResult.status`
- [ ] **Step 2: recheck**
实现:
- `appRecheckNewApiBinding`
必须使用 `AppNewApiBindingRecheckReqVO` 接收 inline requestBody校验 `commandId`,不得用 `Map` 或裸对象承接请求。
若真实 New-API 不可用,按合同返回业务错误 `ACCOUNT_NEW_API_UNAVAILABLE`,或在能够创建异步任务时只返回 OpenAPI 已定义的 `jobId/status=queued|processing`。服务端必须写 `muse_account_integration_call`,内部 `correlation_id``job_id` 关联;不得向响应新增 `correlationId`,也不得把内部 `unavailable/failed` 写入响应 `status`
测试必须断言 unavailable facade 不会产生 fake success且不会返回 OpenAPI 未声明的状态值。
- [ ] **Step 3: quota request**
实现:
- `adminCreateQuotaRequest`
- `appCreateQuotaRequest`
- `appGetQuotaRequest`
`appCreateQuotaRequest` 必须使用 `AppQuotaRequestCreateReqVO` 接收 inline requestBody校验 app 侧允许的 `requestType`,不得复用 admin DTO。
必须生成 requestId、correlationId并写 integration call 初始记录。
- [ ] **Step 4: integration call 查询**
实现:
- `adminGetIntegrationCallByCorrelation`
- `appGetIntegrationCallByCorrelation`
app 必须按 owner 限制。
- [ ] **Step 5: 测试**
覆盖 command replay/conflict、quota request 响应 correlation、binding/recheck 内部 correlation 写入但不改响应合同、app 越权、New-API unavailable。
## Task 7: Call attribution job
**Files:**
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/AdminAccountUsageController.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountAttributionService.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountAttributionServiceImpl.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountIntegrationCallDO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountCallAttributionJobDO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountCallAttributionItemDO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountIntegrationCallMapper.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountCallAttributionJobMapper.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountCallAttributionItemMapper.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/facade/AccountAttributionSourceFacade.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/facade/UnavailableAccountAttributionSourceFacade.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/CallAttributionJobCreateReqVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/CallAttributionJobResultRespVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/CallAttributionJobDetailRespVO.java`
- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountAttributionServiceTest.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/controller/admin/account/AdminAccountUsageControllerTest.java`
- [ ] **Step 1: 创建 job**
实现 `adminCreateCallAttributionJob`。只能基于已存在 `correlationId` 和可选 callIds不能信任请求体指定归因事实。
- [ ] **Step 2: 查询 job**
实现 `adminGetCallAttributionJob`,返回 completed/failed/partially_completed 等状态和补偿建议。
- [ ] **Step 3: 归因边界**
服务端只能通过 `AccountAttributionSourceFacade` 读取 call、task、work、agent、asset、license 关系P1R-3 默认实现为 `UnavailableAccountAttributionSourceFacade`。外部 owner 未接入时,内部 job 可保持不可执行或 failed响应状态只能使用 OpenAPI 已声明的 `queued``processing``completed``failed``partially_completed``idempotent_hit`,不得返回 `pending`,不得伪造归因成功,也不跨入 AI/Content/Market/Knowledge 主流程。
- [ ] **Step 4: 测试**
覆盖 correlation 不存在、callIds 白名单、幂等、归因失败、状态查询、unavailable facade 不 fake success且响应 `status` 不出现 OpenAPI 未声明状态。
## Task 8: Purchase / License / Publish records
**Files:**
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/AdminAccountPurchaseController.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/AppAccountMarketRecordController.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountMarketRecordService.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountMarketRecordServiceImpl.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/facade/MarketAccountProjectionFacade.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/facade/UnavailableMarketAccountProjectionFacade.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountRecordProjectionDO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountRecordProjectionMapper.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminPurchaseRecordRespVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppPurchaseRecordRespVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppLicenseRecordRespVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AppPublishRecordRespVO.java`
- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountMarketRecordServiceTest.java`
- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/controller/app/account/AppAccountMarketRecordControllerTest.java`
- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/controller/admin/account/AdminAccountPurchaseControllerTest.java`
- [ ] **Step 1: purchase records**
实现:
- `adminListPurchaseRecords`
- `appListPurchases`
来源为 `muse_account_record_projection(record_type=purchase)`,上游同步自 `muse_market_purchase`;不实现购买/退款/发票。
默认 `UnavailableMarketAccountProjectionFacade` 必须返回明确 projection unavailable 边界,不能回填伪造购买记录。
- [ ] **Step 2: license records**
实现 `appListLicenses`。来源为 `muse_account_record_projection(record_type=license)`,上游同步自 `muse_market_installation`;不修改 licenseStatus。
- [ ] **Step 3: publish records**
实现 `appListPublishRecords`。来源为 `muse_account_record_projection(record_type=publish)`,上游同步自 `muse_market_publish_request`;不审核、不上架、不下架、不申诉。
- [ ] **Step 4: 测试**
覆盖 app owner、admin query、脱敏、record_type 过滤、Market projection unavailable且 unavailable facade 不产生 fake record。
## Task 9: Security events
**Files:**
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/AppAccountSecurityController.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountSecurityService.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountSecurityServiceImpl.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountSecurityEventAckDO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountSecurityEventAckMapper.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/SecurityEventSummaryRespVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/SecurityEventDetailRespVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/SecurityEventAcknowledgeReqVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/SecurityEventAcknowledgeResultRespVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/account/vo/AdminAccountUserSummaryRespVO.java`
- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountSecurityServiceTest.java`
- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/controller/app/account/AppAccountSecurityControllerTest.java`
- [ ] **Step 1: 列表与详情**
实现:
- `appListSecurityEvents`
- `appGetSecurityEvent`
只能查看本人安全事件,敏感值脱敏。
同时回填 `AdminAccountUserSummaryRespVO` 的风险标记或安全事件计数字段,不提前接入 session 管理。
- [ ] **Step 2: acknowledge**
实现 `appAcknowledgeSecurityEvent`。必须 append-only 写确认动作、note、riskSummary不得删除事件。
- [ ] **Step 3: session_revoked 边界**
若 session 管理服务未接入,返回明确业务错误或 pending action不伪造 session 已撤销。
- [ ] **Step 4: 测试**
覆盖 owner guard、ack 幂等、重复确认、session_revoked external unavailable。
## Task 10: Export task / download credential
**Files:**
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/AppAccountExportController.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountExportService.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/AccountExportServiceImpl.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/facade/AccountFileServiceFacade.java`
- Create: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/application/account/facade/UnavailableAccountFileServiceFacade.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountExportTaskDO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountDownloadCredentialDO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountExportTaskMapper.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountDownloadCredentialMapper.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AccountExportTaskCreateReqVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AccountExportTaskResultRespVO.java`
- Modify: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/account/vo/AccountExportTaskDetailRespVO.java`
- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountExportServiceTest.java`
- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/controller/app/account/AppAccountExportControllerTest.java`
- [ ] **Step 1: 创建导出任务**
实现 `appCreateExportTask`。必须校验 exportType、dateRange、step-up requirement并写 command/audit。
- [ ] **Step 2: 查询导出任务**
实现 `appGetExportTask`。只能查本人 task。
- [ ] **Step 3: 下载凭证**
实现 `appDownloadExport`。必须校验 credential hash、owner、expiresAt、revoked/consumed/source blocked。成功响应必须返回 OpenAPI 定义的 `application/octet-stream` 文件流;错误响应仍走项目统一 `code/data/msg`
- [ ] **Step 4: FileService 边界**
默认 `UnavailableAccountFileServiceFacade` 表示 FileService 未接入;内部 task 可保持不可执行或 failed不返回伪造文件流。响应状态只能使用 OpenAPI 已声明的 `queued``processing``completed``failed``idempotent_hit`,不得返回 `pending`
- [ ] **Step 5: 测试**
覆盖高敏导出、安全事件生成、过期凭证、跨用户凭证、source blocked、成功下载响应不是 `CommonResult`unavailable facade 不返回 fake stream且响应 `status` 不出现 OpenAPI 未声明状态。
## Task 11: Account 合同兜底退役
**Files:**
- Modify/Delete: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/AdminMuseAccountContractController.java`
- Modify/Delete: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/AppMuseAccountContractController.java`
- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountRouteOwnershipTest.java`
- [ ] **Step 1: 删除或收窄 P1R-3 路由兜底**
优先方案:删除两个 Account 合同兜底 Controller。若还有非 P1R-3 Account 合同路径必须保留,必须把兜底路径收窄到这些非 P1R-3 路径,并在测试里证明 P1R-3 路径不会进入 `MuseContractPersistenceService`
- [ ] **Step 2: route ownership 测试**
测试必须证明:
- 33 个 P1R-3 路由都命中 dedicated Controller。
- 33 个 P1R-3 路由不会调用 `MuseContractPersistenceService.handle(...)`
- 没有 wildcard `/**` 把 dedicated 路由重新兜住。
- [ ] **Step 3: 运行 route ownership 测试**
Run:
```bash
set -e
cd muse-cloud
test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountRouteOwnershipTest.java
JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \
mvn -o test -pl muse-server -am \
-Dtest=P1rAccountRouteOwnershipTest
```
Expected: `P1rAccountRouteOwnershipTest` 通过;若测试文件不存在,必须失败。
## Task 12: P1R-3 覆盖门禁与报告对账
**Files:**
- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountRealApiGateTest.java`
- Modify: `docs/superpowers/reports/p1r-api-coverage.json`
- Modify: `docs/superpowers/reports/p1r-api-coverage.md`
- Read: `muse-cloud/scripts/p1r-audit-api-coverage.py`
- [ ] **Step 1: 运行覆盖审计**
Run:
```bash
python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check
```
Expected:
- 脚本成功。
- Content 仍是 51 个 `dedicated / needs_verification`
- Meta 仍是 16 个 `dedicated / needs_verification`
- Account 33 个 operation 是 `dedicated / needs_verification`
- Account 不出现 `completed`
如果脚本不能识别 dedicated Account 实现,先分析脚本识别规则和代码结构差异;不能直接改脚本掩盖缺口。需要修改脚本时必须先说明这是扫描规则纠偏还是状态口径变更,并单独取得确认。
- [ ] **Step 2: 增加 Account 阶段门禁测试**
`P1rAccountRealApiGateTest` 至少校验:
- Account operation 数量为 33。
- Account operation 清单与本计划第 1 节一致。
- Account operation 不允许 `generic_persistence``catch_all``sse_placeholder``missing`
- Account operation 不允许 `completionStatus=completed`
- Account operation 必须全部保持 `dedicated / needs_verification`,不能使用 `blocked``completed` 作为 P1R-3 收口状态。
- [ ] **Step 3: 运行 P1R 门禁**
Run:
```bash
set -e
cd muse-cloud
test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountRealApiGateTest.java
test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rAccountRouteOwnershipTest.java
test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rApiCoverageReportTest.java
test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rContentRealApiGateTest.java
test -f muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMetaRealApiGateTest.java
JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \
mvn -o test -pl muse-server -am \
-Dtest=P1rApiCoverageReportTest,P1rContentRealApiGateTest,P1rMetaRealApiGateTest,P1rAccountRealApiGateTest,P1rAccountRouteOwnershipTest
```
Expected: 测试通过。
- [ ] **Step 4: 运行 Account/Member 模块聚焦测试**
Run:
```bash
set -e
cd muse-cloud
JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \
mvn -o test -pl muse-module-member/muse-module-member-server -am
```
Expected: 测试通过。
- [ ] **Step 5: 基础卫生检查**
Run:
```bash
git diff --check
git status --short -- muse-cloud/scripts
```
Expected:
- `git diff --check` 无输出。
- `muse-cloud/scripts/**` 无改动,除非用户已批准扫描规则纠偏。
## 4. Review 门禁
每个 Task 完成后交给两个 fresh subagent
### Spec compliance review
检查:
- 是否完全覆盖对应 operation。
- 是否符合 P1R-3 spec。
- 是否没有标 `completed`
- 是否没有用 generic persistence / operation record / workflow task / sample JSON 伪装完成。
- 是否没有越权写其他 owner。
- 是否没有跨入 P1R-4/P1R-5/P1R-6/P1R-7。
### Code quality review
检查:
- 改动是否最小。
- Controller / Service / Domain / DAL 是否职责清晰。
- DTO 是否对齐 OpenAPI。
- 测试是否覆盖成功、冲突、非法状态、权限、幂等、审计。
- 是否无无关重构。
- 是否无 `muse-cloud/scripts/**` 掩盖性修改。
## 5. 回滚策略
- 文档与测试回滚:回退本阶段新增 spec/plan/gate test。
- 代码回滚:回退 `muse-module-member` 新增 Account Controller/Service/DTO/DAL/Domain。
- DDL 回滚:开发阶段可删除未合入迁移;迁移合入后不得修改历史 V11只能新增 V12 修正。
- 覆盖报告回滚:重新运行 P1R audit确保 Content 和 Meta 保持当前状态。
## 6. 完成条件
P1R-3 实现阶段完成时必须同时满足:
1. Account 33 个 operation 有专用实现。
2. Account 不存在 `generic_persistence``catch_all``sse_placeholder``missing` 残留。
3. Account 全部保持 `dedicated / needs_verification`,不存在 `completed``blocked` 收口状态。
4. Account/Member 模块测试通过。
5. `P1rAccountMigrationSqlTest` 通过,证明 V11 迁移包含 `muse_account_audit``muse_account_profile``muse_account_record_projection` 等必要表和约束。
6. `P1rAccountRouteOwnershipTest` 通过,证明 33 个 P1R-3 路由不再进入 `MuseContractPersistenceService`
7. P1R 门禁测试通过。
8. `muse-cloud/scripts/**` 未被用来掩盖缺口。
9. Content 51 个 operation 仍为 `dedicated / needs_verification`
10. Meta 16 个 operation 仍为 `dedicated / needs_verification`
11. 若未运行真实 PostgreSQL / Flyway / New-API runtime / FileService 验证,必须在 `docs/memorys/YYYY-MM-DD-P1R3账户真实API规格计划.md` 或实现阶段留痕中记录残余 `needs_verification` 风险。

View File

@ -0,0 +1,564 @@
# P1R-3Account Real API 规格
- 版本v1
- 日期2026-05-28
- 状态review 已通过2026-05-29 实现阶段已收口到 `dedicated / needs_verification`
- 上游规格:`docs/superpowers/specs/2026-05-25-P1R-muse-cloud-real-api-design.md`
- 基线输入:`docs/superpowers/reports/p1r-api-coverage.json`
- 范围:`muse-cloud/muse-module-member/**` 中 Account 专用实现、Account 相关追加迁移、P1R-3 覆盖门禁
- 非范围AI runtime / SSE、Knowledge / RAGFlow、Market 主流程、Events SSE、修改 `muse-cloud/scripts/**` 掩盖缺口、静默修改 OpenAPI、把 Account operation 标为 `completed`
## 1. 结论
P1R-3 的目标不是继续扩展 `MuseContractPersistenceService`,也不是把 Account 的通用持久化响应包装成 OpenAPI DTO。
本阶段要把 Account 域从“通用持久化可响应”推进到“真实账户业务 API 可验收”:
1. Account 33 个 operation 当前全部是 `generic_persistence / incomplete`,必须退出通用持久化完成口径。
2. 仓库没有 `muse-module-account`,当前 Account 合同实际挂在 `muse-module-member`P1R-3 的 owner 应落在 `muse-module-member` 的 Account 子域,而不是新增孤立模块。
3. Account owner 只拥有用户资料、权益、配额、用量、New-API 绑定、调用归因读模型、安全事件、个人中心导出的账户事实。
4. 购买、授权、发布记录只能读取 Market/Content/Knowledge/AI 已产生的事实或投影,不能在 Account 阶段实现 Market 主流程。
5. New-API binding / recheck / quota request / integration call / call attribution 必须有 commandId 幂等、外部调用记录和失败补偿边界;其中 quota request 与 call attribution 按 OpenAPI 暴露 `correlationId`binding / recheck 只在服务端 integration call 中关联 `bindingId` / `jobId`,不向响应偷加 OpenAPI 未定义字段。未接真实 New-API runtime 前只能保持 `needs_verification`
6. P1R-3 通过后的覆盖状态目标统一为 `dedicated / needs_verification`,不能写成 `completed`。外部 owner 未闭合时返回合同内 `CommonResult` 业务错误或 pending job不使用 coverage 的 `blocked` 完成状态。
## 2. 已验证事实
### 2.1 执行工作面
用户给定主路径 `/Users/qingse/Sync/local-git/oh-my-muse` 当前不是本阶段真实工作面。只读核验结果显示:
- `/Users/qingse/Sync/local-git/oh-my-muse` 当前在 `feature/p1-muse-cloud-backend-20260524`,且有未跟踪文件。
- `dev/1.0.0` 已被 worktree 占用:`/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0`
- 写入本规格和执行计划前,真实工作面 `dev/1.0.0``git status --short --branch` 输出为 `## dev/1.0.0...origin/dev/1.0.0`,无未提交改动。
本规格和执行计划落在真实 worktree
```text
/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0
```
### 2.2 覆盖矩阵
来自 `docs/superpowers/reports/p1r-api-coverage.json`
| 指标 | 数量 |
|---|---:|
| 全量 operation | 233 |
| completed | 0 |
| needs_verification | 67 |
| incomplete | 166 |
| generic_persistence | 164 |
| sse_placeholder | 2 |
| missing | 0 |
| blocked | 0 |
| Content `dedicated / needs_verification` | 51 |
| Meta `dedicated / needs_verification` | 16 |
| Account operation | 33 |
| Account `generic_persistence / incomplete` | 33 |
| Account `dedicated / needs_verification` | 0 |
Account 当前没有 `catch_all``sse_placeholder``missing``dedicated`。所有 Account operation 的 `notes` 均为:由 `MuseContractPersistenceService` 通用持久化处理,不是 P1R 完成证据。
### 2.3 当前代码事实
当前没有 `muse-cloud/muse-module-account/**`。Account 合同入口在 `muse-cloud/muse-module-member/**`
- `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/admin/AdminMuseAccountContractController.java`
- `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/controller/app/AppMuseAccountContractController.java`
这两个 Controller 使用宽泛路由接管:
- Admin`/muse/account/**`
- App`/muse/me``/muse/profile``/muse/account/**`
并统一委托:
- `MuseContractPersistenceService.handle(...)`
当前缺失:
- Account 专用 Controller。
- Account Application Service。
- Account OpenAPI 对应 DTO / VO。
- Account DO / Mapper。
- Account commandId + requestHash 幂等服务。
- Account 领域审计与 security event 写入。
- App user owner guard 的统一后端强制。
- Admin RBAC 权限点。
- New-API binding / recheck 的真实外部调用边界。
- integration call / correlation / call attribution 的专用事实表或投影。
- balance snapshot、export task、download credential 的专用持久化。
- purchase / license / publish record 的 Account 视角投影。
已有 Yudao 会员能力可复用但不是 OpenAPI 完成路径:
- `AppMemberUserController` 只覆盖 `/member/user/**`
- `MemberUserController` 只覆盖 `/member/user/**` 管理端会员用户。
- `MemberUserService` / `MemberUserMapper` 可作为用户资料底层能力。
### 2.4 当前 DDL
`muse-cloud/sql/muse/V2__init_account_schema.sql` 已有 Account 基础表:
- `muse_member_entitlement`
- `muse_member_entitlement_audit_log`
- `muse_member_quota`
- `muse_member_usage_record`
- `muse_member_security_event`
- `muse_member_new_api_binding`
`muse-cloud/sql/muse/V7__add_contract_operation_audit.sql` 已有通用操作记录:
- `muse_domain_operation_record`
`muse-cloud/sql/muse/V8__add_contract_workflow_and_market_interaction.sql` 已有通用工作流和市场购买:
- `muse_domain_workflow_task`
- `muse_market_purchase`
`muse-cloud/sql/muse/V6__init_market_schema.sql` 已有 Market 侧可读来源:
- `muse_market_asset`
- `muse_market_asset_version`
- `muse_market_installation`
- `muse_market_publish_request`
- `muse_market_appeal`
- `muse_market_handoff`
这些表不足以完成 P1R-3
- 没有 Account 专用 command 表;`muse_domain_operation_record` 只有 domain 级 replay没有 request hash 冲突判断。
- 没有 balance snapshot 表。
- 没有 integration call 表。
- 没有 call attribution job 专用表和 per-call 归因结果。
- 没有 quota request 专用状态表;当前只落 `muse_domain_workflow_task`
- 没有 account export task 表和 download credential 表。
- `muse_member_security_event` 只有确认字段,缺 acknowledge action / note / riskSummary / append-only audit。
- `muse_member_entitlement_audit_log` 缺 OpenAPI 所需 before/after 快照完整填充和审计事件关系。
- purchase/license/publish records 没有 Account 视角读模型或明确 projection owner。
## 3. 目标
1. 为 33 个 Account operation 定义真实 API 落地边界。
2. 用专用 Controller / Service / DTO / DAL / DDL 替代通用持久化完成口径。
3. 建立当前用户、profile、权益、配额、余额、用量、安全事件、导出下载的账户事实模型。
4. 建立 New-API binding / recheck / quota request / integration call / call attribution 的 correlation 边界。
5. 建立 purchase / license / publish records 的 Account 视角投影,不接管 Market 主流程。
6. 建立 commandId 幂等、requestHash 冲突校验、owner guard、admin RBAC、审计和错误响应。
7. 建立 P1R-3 阶段门禁,防止 Account operation 继续被 `generic_persistence``completed` 混淆。
## 4. 非目标
1. 不修改 OpenAPI 来适配当前代码。若发现合同纠偏需求,必须单独列出原因、影响范围和是否属于合同纠偏。
2. 不实现 AI task 真实 New-API 调用、AI runtime、Agent、Prompt、Quality、Suggestion、Job。
3. 不实现 AI task SSE stream 或统一 Events SSE。
4. 不实现 Knowledge / RAGFlow / projection 主流程。
5. 不实现 Market 购买、安装、handoff、审核、召回、申诉主流程。
6. 不把 `MuseContractPersistenceService``muse_domain_operation_record``muse_domain_workflow_task` 原始行、空列表、`status: accepted` 作为完成证据。
7. 不修改 `muse-cloud/scripts/**` 来掩盖真实缺口。若扫描器无法识别 dedicated 实现,先说明扫描规则合同纠偏原因和影响范围。
8. 不把前端按钮隐藏或页面权限当作后端权限。
## 5. Operation 清单与当前状态
| operationId | Method | Path | 当前状态 | 真实 owner | P1R-3 目标 |
|---|---|---|---|---|---|
| `adminListAccountUsers` | GET | `/admin-api/muse/account/users` | `generic_persistence / incomplete` | Account User Query | `dedicated / needs_verification` |
| `adminGetUserEntitlements` | GET | `/admin-api/muse/account/users/{userId}/entitlements` | `generic_persistence / incomplete` | Account Entitlement Query | `dedicated / needs_verification` |
| `adminCreateQuotaAdjustment` | POST | `/admin-api/muse/account/users/{userId}/quota-adjustments` | `generic_persistence / incomplete` | Account Quota Command | `dedicated / needs_verification` |
| `adminListQuotaAdjustments` | GET | `/admin-api/muse/account/users/{userId}/quota-adjustments` | `generic_persistence / incomplete` | Account Quota Query | `dedicated / needs_verification` |
| `adminListNewApiBindings` | GET | `/admin-api/muse/account/new-api-bindings` | `generic_persistence / incomplete` | Account New-API Query | `dedicated / needs_verification` |
| `adminCreateNewApiBinding` | POST | `/admin-api/muse/account/users/{userId}/new-api-binding` | `generic_persistence / incomplete` | Account New-API Command | `dedicated / needs_verification` |
| `adminCreateQuotaRequest` | POST | `/admin-api/muse/account/users/{userId}/quota-requests` | `generic_persistence / incomplete` | Account New-API Quota Command | `dedicated / needs_verification` |
| `adminGetBalanceSnapshots` | GET | `/admin-api/muse/account/users/{userId}/balance-snapshots` | `generic_persistence / incomplete` | Account Balance Query | `dedicated / needs_verification` |
| `adminCreateCallAttributionJob` | POST | `/admin-api/muse/account/call-attribution-jobs` | `generic_persistence / incomplete` | Account Attribution Command | `dedicated / needs_verification` |
| `adminGetCallAttributionJob` | GET | `/admin-api/muse/account/call-attribution-jobs/{jobId}` | `generic_persistence / incomplete` | Account Attribution Query | `dedicated / needs_verification` |
| `adminGetIntegrationCallByCorrelation` | GET | `/admin-api/muse/account/integration-calls/by-correlation/{correlationId}` | `generic_persistence / incomplete` | Account Integration Query | `dedicated / needs_verification` |
| `adminListUsageRecords` | GET | `/admin-api/muse/account/usage-records` | `generic_persistence / incomplete` | Account Usage Query | `dedicated / needs_verification` |
| `adminListPurchaseRecords` | GET | `/admin-api/muse/account/purchase-records` | `generic_persistence / incomplete` | Account Purchase Projection Query | `dedicated / needs_verification` |
| `getCurrentUser` | GET | `/app-api/muse/me` | `generic_persistence / incomplete` | Account Current User Query | `dedicated / needs_verification` |
| `getProfile` | GET | `/app-api/muse/profile` | `generic_persistence / incomplete` | Account Profile Query | `dedicated / needs_verification` |
| `updateProfile` | PATCH | `/app-api/muse/profile` | `generic_persistence / incomplete` | Account Profile Command | `dedicated / needs_verification` |
| `getAppEntitlements` | GET | `/app-api/muse/account/entitlements` | `generic_persistence / incomplete` | Account Entitlement Query | `dedicated / needs_verification` |
| `getAppUsage` | GET | `/app-api/muse/account/usage` | `generic_persistence / incomplete` | Account Usage Query | `dedicated / needs_verification` |
| `getAppNewApiBinding` | GET | `/app-api/muse/account/new-api-binding` | `generic_persistence / incomplete` | Account New-API Query | `dedicated / needs_verification` |
| `appRecheckNewApiBinding` | POST | `/app-api/muse/account/new-api-binding/recheck` | `generic_persistence / incomplete` | Account New-API Command | `dedicated / needs_verification` |
| `getAppBalanceSnapshots` | GET | `/app-api/muse/account/balance-snapshots` | `generic_persistence / incomplete` | Account Balance Query | `dedicated / needs_verification` |
| `appCreateQuotaRequest` | POST | `/app-api/muse/account/quota-requests` | `generic_persistence / incomplete` | Account Quota Request Command | `dedicated / needs_verification` |
| `appGetQuotaRequest` | GET | `/app-api/muse/account/quota-requests/{requestId}` | `generic_persistence / incomplete` | Account Quota Request Query | `dedicated / needs_verification` |
| `appGetIntegrationCallByCorrelation` | GET | `/app-api/muse/account/integration-calls/by-correlation/{correlationId}` | `generic_persistence / incomplete` | Account Integration Query | `dedicated / needs_verification` |
| `appListPurchases` | GET | `/app-api/muse/account/purchases` | `generic_persistence / incomplete` | Account Purchase Projection Query | `dedicated / needs_verification` |
| `appListLicenses` | GET | `/app-api/muse/account/licenses` | `generic_persistence / incomplete` | Account License Projection Query | `dedicated / needs_verification` |
| `appListPublishRecords` | GET | `/app-api/muse/account/publish-records` | `generic_persistence / incomplete` | Account Publish Projection Query | `dedicated / needs_verification` |
| `appListSecurityEvents` | GET | `/app-api/muse/account/security-events` | `generic_persistence / incomplete` | Account Security Query | `dedicated / needs_verification` |
| `appGetSecurityEvent` | GET | `/app-api/muse/account/security-events/{eventId}` | `generic_persistence / incomplete` | Account Security Query | `dedicated / needs_verification` |
| `appAcknowledgeSecurityEvent` | POST | `/app-api/muse/account/security-events/{eventId}/acknowledge` | `generic_persistence / incomplete` | Account Security Command | `dedicated / needs_verification` |
| `appCreateExportTask` | POST | `/app-api/muse/account/export-tasks` | `generic_persistence / incomplete` | Account Export Command | `dedicated / needs_verification` |
| `appGetExportTask` | GET | `/app-api/muse/account/export-tasks/{taskId}` | `generic_persistence / incomplete` | Account Export Query | `dedicated / needs_verification` |
| `appDownloadExport` | GET | `/app-api/muse/account/downloads/{credentialId}` | `generic_persistence / incomplete` | Account Export Download | `dedicated / needs_verification` |
## 6. 目标架构
```mermaid
flowchart TD
A[Admin Account Controllers] --> B[Account Application Services]
C[App Account Controllers] --> B
B --> D[Account Command Service]
B --> E[Account Audit Service]
B --> F[Account Owner Guards]
B --> G[Account Query Assemblers]
B --> H[Account Persistence]
B --> I[External Facades]
H --> J[(member_user)]
H --> K[(muse_member_* tables)]
H --> L[(muse_account_* extension tables)]
I --> M[New-API Client Boundary]
I --> N[Market Projection Boundary]
I --> O[FileService Boundary]
I --> P[Attribution Source Boundary]
```
### 6.1 Controller
推荐拆分:
| Controller | 职责 |
|---|---|
| `AdminAccountUserController` | 用户摘要、用户权益、配额调整、余额快照 |
| `AdminAccountNewApiController` | New-API binding 列表、创建/刷新、quota request |
| `AdminAccountUsageController` | usage records、integration call、call attribution job |
| `AdminAccountPurchaseController` | purchase records 管理端查询 |
| `AppAccountProfileController` | `/app-api/muse/me``/app-api/muse/profile` |
| `AppAccountEntitlementController` | 权益、用量、余额、quota request |
| `AppAccountNewApiController` | binding 查询、recheck、integration call 查询 |
| `AppAccountMarketRecordController` | purchases、licenses、publish records |
| `AppAccountSecurityController` | security events、acknowledge |
| `AppAccountExportController` | export task、download credential |
Controller 只负责:
- `@PreAuthorize` 或 app authenticated owner 入口。
- `X-API-Version: 1` 校验。
- path/query/body 参数校验。
- DTO 进入 Application Service。
- 默认用 `CommonResult<T>` 返回 `code/data/msg`
- `appDownloadExport` 是 OpenAPI 明确的例外:成功响应必须返回 `application/octet-stream` 文件流;失败响应仍沿用项目统一错误响应。
禁止:
- Controller 访问 Mapper。
- Controller 拼接业务 JSON。
- Controller 调用 `MuseContractPersistenceService` 作为完成路径。
- Controller 静态返回 OpenAPI 样例。
### 6.2 Service
推荐服务:
| Service | 职责 |
|---|---|
| `AccountProfileService` | 当前用户、资料查询、资料更新、乐观锁 |
| `AccountEntitlementService` | 权益、配额、余额快照查询与装配 |
| `AccountQuotaService` | 配额调整、quota request 状态机 |
| `AccountNewApiBindingService` | New-API binding、recheck、外部同步边界 |
| `AccountUsageService` | usage records、usage summary、integration call 查询 |
| `AccountAttributionService` | call attribution job 创建、状态、归因校验边界 |
| `AccountMarketRecordService` | purchase/license/publish records Account 投影查询 |
| `AccountSecurityService` | security event 查询、确认、append-only 审计 |
| `AccountExportService` | export task、download credential、FileService 边界 |
| `AccountCommandService` | commandId、requestHash、resultSnapshot 幂等 |
| `AccountAuditService` | Account 领域审计和安全事件写入 |
### 6.3 DTO / Assembler
每个 OpenAPI schema 必须对应专用 VO/DTO不能返回 `Map<String, Object>` 或 DO。
最小 DTO 覆盖:
- `AdminAccountUserSummaryRespVO`
- `AdminUserEntitlementDetailRespVO`
- `QuotaAdjustmentReqVO`
- `QuotaAdjustmentResultRespVO`
- `QuotaAdjustmentLedgerEntryRespVO`
- `NewApiBindingSummaryRespVO`
- `NewApiBindingCreateReqVO`
- `NewApiBindingResultRespVO`
- `AdminQuotaRequestCreateReqVO`
- `AdminQuotaRequestResultRespVO`
- `AppQuotaRequestResultRespVO`
- `QuotaRequestStatusRespVO`
- `AdminBalanceSnapshotEntryRespVO`
- `AppBalanceSnapshotEntryRespVO`
- `CallAttributionJobCreateReqVO`
- `CallAttributionJobResultRespVO`
- `CallAttributionJobDetailRespVO`
- `AdminIntegrationCallDetailRespVO`
- `AppIntegrationCallDetailRespVO`
- `AdminUsageRecordRespVO`
- `AdminPurchaseRecordRespVO`
- `AppCurrentUserSummaryRespVO`
- `AppProfileRespVO`
- `AppProfileUpdateReqVO`
- `AppEntitlementRespVO`
- `AppUsageSummaryRespVO`
- `AppNewApiBindingSummaryRespVO`
- `AppNewApiBindingRecheckReqVO`
- `AppNewApiBindingRecheckRespVO`
- `AppQuotaRequestCreateReqVO`
- `AppPurchaseRecordRespVO`
- `AppLicenseRecordRespVO`
- `AppPublishRecordRespVO`
- `SecurityEventSummaryRespVO`
- `SecurityEventDetailRespVO`
- `SecurityEventAcknowledgeReqVO`
- `SecurityEventAcknowledgeResultRespVO`
- `AccountExportTaskCreateReqVO`
- `AccountExportTaskResultRespVO`
- `AccountExportTaskDetailRespVO`
Assembler 负责:
- ID 转字符串。
- 时间字段统一为 OpenAPI date-time。
- 手机、邮箱、外部订单、IP、设备信息脱敏。
- `PageResult<T>` 与 OpenAPI `PaginatedResult.list` 对齐。
- 不暴露 New-API token、provider authority、模型路由、Prompt/Response 原文、后台权限表。
## 7. 持久化设计
### 7.1 复用现有表
| 表 | P1R-3 用途 | 缺口 |
|---|---|---|
| `member_user` | 当前用户、用户摘要、账号基础状态 | 不直接承载 P1R-3 profile 乐观锁和公开笔名等扩展事实 |
| `muse_account_profile` | profile 扩展、expectedVersion、公开资料 | P1R-3 新增轻量扩展表 |
| `muse_member_entitlement` | plan、权益、额度摘要 | 需要 DTO 装配和来源语义 |
| `muse_member_quota` | quota 明细、剩余和重置周期 | 需要资源类型映射和并发调整 |
| `muse_member_entitlement_audit_log` | quota adjustment ledger | 需要 before/after 快照和 audit event 关联 |
| `muse_member_usage_record` | usage records、app usage summary | 需要 correlation、归因状态、统计聚合 |
| `muse_member_security_event` | security event 查询 | 需要 acknowledge action/note/audit |
| `muse_member_new_api_binding` | New-API binding | 需要 recheck status、失败原因、外部同步时间 |
| `muse_domain_workflow_task` | 可作为过渡读取旧任务 | 不能作为 Account dedicated 完成证据 |
| `muse_market_purchase` | purchase projection 上游来源 | Account 只通过 `muse_account_record_projection` 读取 |
| `muse_market_installation` | license projection 上游来源 | Account 只通过 `muse_account_record_projection` 读取 |
| `muse_market_publish_request` | publish records 上游来源 | Account 只通过 `muse_account_record_projection` 读取 |
### 7.2 追加迁移
P1R-3 实现阶段建议新增 `V11__extend_account_real_api_schema.sql`。最小新增:
- `muse_account_command`
- `muse_account_audit`
- `muse_account_profile`
- `muse_account_balance_snapshot`
- `muse_account_quota_request`
- `muse_account_integration_call`
- `muse_account_call_attribution_job`
- `muse_account_call_attribution_item`
- `muse_account_export_task`
- `muse_account_download_credential`
- `muse_account_security_event_ack`
- `muse_account_record_projection`
关键约束:
- `muse_account_command` 唯一键:`(tenant_id, command_id)`
- command 记录必须包含 `operation_id``actor_user_id``owner_user_id``target_type``target_id``request_hash``result_snapshot`
- 同一 `commandId` + 不同 `requestHash` 返回 409。
- `muse_account_audit` 必须覆盖写操作、外部调用、导出下载和高敏安全动作,索引 `account_user_id``operation_id``correlation_id`
- `muse_account_profile` 必须以 `account_user_id` 唯一,包含 `profile_version` 支撑 `expectedVersion`
- `muse_account_record_projection` 必须包含 `record_type`,限定为 `purchase``license``publish`,并保存上游 `source_table` / `source_id` / `source_revision`
- app 可见表必须具备 `account_user_id` / `owner_user_id` 索引。
- `correlation_id` 在 integration call 维度需要唯一或去重键。
- download credential 必须具备 `credential_hash``account_user_id``task_id``expires_at``consumed_at``revoked_at`
## 8. Owner Guard 与权限边界
### 8.1 App owner guard
App 入口所有读写必须以 `getLoginUserId()` 作为唯一账户 owner
- 不接受请求体中的 `userId` 作为 owner。
- `requestId``eventId``taskId``credentialId``correlationId` 都必须反查 owner。
- 查不到或 owner 不匹配返回 403 或 404不能返回跨用户空数据掩盖越权。
- `appDownloadExport` 必须同时校验 credential owner、过期、撤销、source blocked 和 task 完成状态。
### 8.2 Admin 权限边界
Admin 入口必须使用后端 RBAC
| 操作组 | 建议权限点 |
|---|---|
| 用户摘要/权益/余额/用量/购买记录查询 | `muse:account:query` |
| quota adjustment | `muse:account:quota-adjust` |
| quota request / New-API binding | `muse:account:new-api:manage` |
| call attribution job | `muse:account:attribution:manage` |
| security event 管理端查询若后续加入 | `muse:account:security:query` |
Admin 也必须校验目标用户存在。Admin 只能管理账户域事实,不能绕过 Market、AI、Knowledge 的 owner 状态机。
## 9. commandId 与幂等
所有写操作必须通过 `AccountCommandService`
| operationId | 幂等目标 |
|---|---|
| `adminCreateQuotaAdjustment` | `quotaAdjustment:userId` |
| `adminCreateNewApiBinding` | `newApiBinding:userId` |
| `adminCreateQuotaRequest` | `quotaRequest:userId` |
| `adminCreateCallAttributionJob` | `callAttribution:correlationId` |
| `updateProfile` | `profile:actorUserId` |
| `appRecheckNewApiBinding` | `newApiBindingRecheck:actorUserId` |
| `appCreateQuotaRequest` | `quotaRequest:actorUserId` |
| `appAcknowledgeSecurityEvent` | `securityEvent:eventId` |
| `appCreateExportTask` | `exportTask:actorUserId` |
规则:
1. `commandId` 必填的 operation 必须从请求体读取,不依赖 `X-Command-Id`
2. 稳定 JSON 计算 `requestHash`
3. 相同 `commandId` + 相同 `requestHash` 返回旧 `resultSnapshot`,并在 DTO 中按合同表达 `idempotent_hit`
4. 相同 `commandId` + 不同 `requestHash` 返回 409错误码为 Account 幂等冲突。
5. command 预占与业务写入必须在同一事务内。
6. 异步任务只保证“任务创建幂等”,不保证外部 runtime 已完成。
## 10. Audit 与 Security Event
Account 审计分两层:
1. `muse_account_command`:所有写命令的幂等和 result snapshot。
2. `muse_account_audit`业务可读审计覆盖操作者、目标用户、operation、前后状态、correlationId、requestHash、风险摘要。
最小审计字段:
- `operationId`
- `actorUserId`
- `accountUserId`
- `side`
- `targetType`
- `targetId`
- `commandId`
- `requestHash`
- `correlationId`
- `beforeSnapshot`
- `afterSnapshot`
- `status`
- `errorCode`
- `errorMessage`
- `createdAt`
Security event
- `appAcknowledgeSecurityEvent` 必须 append-only 记录 acknowledgement不应覆盖历史处理动作。
- `action=session_revoked` 不在 P1R-3 实现真实 session 失效;若 session owner 未接入,返回明确业务错误或 pending action不伪造成功。
- 账户导出高敏行为应生成 `sensitive_export` security event 或 audit event。
## 11. 错误响应
`appDownloadExport` 成功响应按 OpenAPI 返回 `application/octet-stream` 文件流外,其余接口使用项目现有 `CommonResult<T>` 成功封装。所有错误响应应保持 `code/data/msg` 的项目运行态一致性,不新增 OpenAPI 未定义字段。
Account 最小错误码:
| 场景 | HTTP/业务语义 | 建议业务错误 |
|---|---|---|
| X-API-Version 不支持 | 400 | `ACCOUNT_API_VERSION_UNSUPPORTED` |
| 用户不存在 | 404 | `ACCOUNT_USER_NOT_EXISTS` |
| app owner 不匹配 | 403 | `ACCOUNT_RESOURCE_FORBIDDEN` |
| commandId 缺失 | 400 | `ACCOUNT_COMMAND_ID_REQUIRED` |
| commandId 冲突 | 409 | `ACCOUNT_COMMAND_ID_CONFLICT` |
| expectedVersion 冲突 | 409 | `ACCOUNT_VERSION_CONFLICT` |
| New-API binding 不存在 | 404 | `ACCOUNT_NEW_API_BINDING_NOT_EXISTS` |
| New-API runtime 不可用 | 503 或业务错误 | `ACCOUNT_NEW_API_UNAVAILABLE` |
| correlationId 不存在 | 404 | `ACCOUNT_INTEGRATION_CALL_NOT_EXISTS` |
| 余额/权益不足 | 409 | `ACCOUNT_QUOTA_EXHAUSTED` |
| 下载凭证过期/撤销 | 403 | `ACCOUNT_DOWNLOAD_CREDENTIAL_INVALID` |
| 导出来源 blocked | 403 | `ACCOUNT_EXPORT_SOURCE_BLOCKED` |
## 12. New-API / Correlation 边界
P1R-3 只定义 Account 与 New-API 的账户侧边界:
- `adminCreateNewApiBinding` / `appRecheckNewApiBinding` 只能创建 binding 或 recheck job。
- binding / recheck 必须写入 `muse_account_integration_call`,并通过 `bindingId` / `jobId` 与内部 `correlationId` 关联;现有 OpenAPI 响应没有 `correlationId` 字段P1R-3 不修改响应合同。
- quota request 和 call attribution 已在 OpenAPI 中暴露 `correlationId`,必须用该值支持 `adminGetIntegrationCallByCorrelation` / `appGetIntegrationCallByCorrelation`
- 真实 New-API 用户创建、额度配置和余额查询若 runtime 未接入,必须以合同内业务错误或内部 pending/failed integration call 记录表达;不能为了“可追踪”向响应添加 OpenAPI 未定义字段,也不能返回 OpenAPI 未声明的状态值。
- `muse_account_integration_call` 是外部调用去重、重试、错误和 attribution 的账户侧事实。
- `adminGetIntegrationCallByCorrelation` 可看全局调用详情。
- `appGetIntegrationCallByCorrelation` 只能查看当前用户 owner 的调用。
Call attribution
- `adminCreateCallAttributionJob` 只能基于已存在的 integration call / New-API callIds。
- 请求体不能直接指定可信 user/work/agent/asset/license 归因事实。
- 服务端必须从调用审计记录、业务任务、作品、资产、授权关系校验后写 attribution item。
- AI runtime 真实调用不在 P1R-3 实现Account 只消费调用记录。
## 13. 数据来源
| 数据域 | P1R-3 数据来源 |
|---|---|
| user/profile/me | `member_user` + `muse_account_profile` + entitlement/security projection |
| entitlements | `muse_member_entitlement` |
| quota | `muse_member_quota` + `muse_member_entitlement_audit_log` |
| quota requests | `muse_account_quota_request` + integration call |
| balance snapshots | `muse_account_balance_snapshot` |
| usage records/app usage | `muse_member_usage_record` + attribution fields |
| purchase records | `muse_account_record_projection(record_type=purchase)`,上游来自 `muse_market_purchase` |
| license records | `muse_account_record_projection(record_type=license)`,上游来自 `muse_market_installation` |
| publish records | `muse_account_record_projection(record_type=publish)`,上游来自 `muse_market_publish_request` |
| New-API binding | `muse_member_new_api_binding` + recheck job |
| integration calls | `muse_account_integration_call` |
| call attribution jobs | `muse_account_call_attribution_job` / item |
| security events | `muse_member_security_event` + ack extension |
| export tasks/downloads | `muse_account_export_task` + `muse_account_download_credential` + FileService boundary下载成功响应为文件流 |
## 14. 外部阶段边界
| 边界 | P1R-3 处理方式 |
|---|---|
| AI task 真实 New-API 调用 | P1R-4P1R-3 不创建 AI task、不调模型 |
| AI runtime / agent / prompt / quality / suggestions / jobs | P1R-4Account 只读已归因用量 |
| AI task SSE stream | P1R-4P1R-3 export/call attribution job 不实现 SSE |
| Knowledge / RAGFlow / projection | P1R-5Account 只读授权或用量投影 |
| Market | P1R-6Account 只读购买/授权/发布投影,不处理购买/安装/审核 |
| Events SSE / 端到端验收 | P1R-7P1R-3 不标 completed |
| FileService | P1R-3 定义导出下载边界;真实存储未接入时保持 needs_verification |
## 15. 实现单元
P1R-3 实现阶段建议按以下单元推进:
1. Preflight 与覆盖基线冻结。
2. Account 基础设施与追加 DDL。
3. DTO / DAL / Mapper 骨架。
4. App `me` / `profile`
5. 权益、配额、余额、用量。
6. New-API binding、recheck、quota request、integration call。
7. call attribution job。
8. purchase / license / publish records 投影。
9. security events。
10. export task / download credential。
11. Account catch-all 路由退役。
12. P1R-3 覆盖门禁与报告对账。
## 16. 验收标准
P1R-3 spec + plan review 通过后,进入实现阶段。实现阶段完成必须满足:
1. Account 33 个 operation 有专用 Controller / Service / DTO。
2. Account 33 个 operation 不再是 `generic_persistence``catch_all``sse_placeholder``missing`
3. Account operation 未经 P1R-7 真实端到端验收前,不允许标 `completed`
4. Account 全部保持 `dedicated / needs_verification`
5. Content 51 个 operation 保持 `dedicated / needs_verification`
6. Meta 16 个 operation 保持 `dedicated / needs_verification`
7. `completedOperations=0`
8. `muse-cloud/scripts/**` 未被用来掩盖缺口。
9. Account 模块聚焦测试通过。
10. `P1rAccountMigrationSqlTest` 通过,证明 V11 迁移包含 Account 审计、profile、projection、integration、export/download 等必要约束。
11. P1R 门禁测试通过。
12. 若未运行真实 PostgreSQL / Flyway / New-API runtime / FileService必须在留痕中保留 `needs_verification` 风险。
## 17. 待确认项
1. P1R-3 是否允许在 `muse-module-member` 内新增 `account` 包作为长期 owner。默认推荐此方案因为仓库没有 `muse-module-account`,且用户资料属于 member 模块。
2. New-API runtime 在 P1R-3 是否有可用测试环境。若没有binding/recheck/quota request 只能做到合同内业务错误或内部 pending/failed 可追踪,保持 `needs_verification`
3. FileService 是否有可用测试环境。若没有export task / download credential 只能验证凭证与状态机,不能验证真实文件流交付。