diff --git a/docs/memorys/2026-05-28-P1R3账户真实API规格计划.md b/docs/memorys/2026-05-28-P1R3账户真实API规格计划.md new file mode 100644 index 00000000..a439f1b4 --- /dev/null +++ b/docs/memorys/2026-05-28-P1R3账户真实API规格计划.md @@ -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 runtime,binding / recheck / quota request 仍需保持 `needs_verification`。 +- 未接真实 FileService runtime,export 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 tests,0 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 tests,0 failures。 + +```bash +git diff --check +``` + +结果:通过,无空白错误输出。 + +### 未验证 runtime 风险 + +- 未验证真实 PostgreSQL / Flyway 执行,V11 仍需在真实数据库迁移链路确认。 +- 未接真实 New-API runtime,binding / recheck / quota request / integration call 仍保持 `needs_verification`。 +- 未接真实 FileService,export 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`。 diff --git a/docs/superpowers/plans/2026-05-28-P1R-3-account-real-api.md b/docs/superpowers/plans/2026-05-28-P1R-3-account-real-api.md new file mode 100644 index 00000000..67800ee9 --- /dev/null +++ b/docs/superpowers/plans/2026-05-28-P1R-3-account-real-api.md @@ -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`、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`。 + +## 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` 风险。 diff --git a/docs/superpowers/specs/2026-05-28-P1R-3-account-real-api-design.md b/docs/superpowers/specs/2026-05-28-P1R-3-account-real-api-design.md new file mode 100644 index 00000000..a0d67e57 --- /dev/null +++ b/docs/superpowers/specs/2026-05-28-P1R-3-account-real-api-design.md @@ -0,0 +1,564 @@ +# P1R-3:Account 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` 返回 `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` 或 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` 与 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` 成功封装。所有错误响应应保持 `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-4;P1R-3 不创建 AI task、不调模型 | +| AI runtime / agent / prompt / quality / suggestions / jobs | P1R-4;Account 只读已归因用量 | +| AI task SSE stream | P1R-4;P1R-3 export/call attribution job 不实现 SSE | +| Knowledge / RAGFlow / projection | P1R-5;Account 只读授权或用量投影 | +| Market | P1R-6;Account 只读购买/授权/发布投影,不处理购买/安装/审核 | +| Events SSE / 端到端验收 | P1R-7;P1R-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 只能验证凭证与状态机,不能验证真实文件流交付。