From f02defd367b71cc4debce068eda4e38e2e092d8f Mon Sep 17 00:00:00 2001 From: zizi Date: Wed, 3 Jun 2026 15:56:53 +0800 Subject: [PATCH] =?UTF-8?q?docs(p1r):=20=E8=A1=A5=E9=BD=90=20Market=20?= =?UTF-8?q?=E7=9C=9F=E5=AE=9E=20API=20=E8=A7=84=E6=A0=BC=E8=AE=A1=E5=88=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/memorys/2026-06-03-P1R6Market规格计划.md | 221 ++++++++ .../plans/2026-06-03-P1R-6-market-real-api.md | 534 ++++++++++++++++++ ...2026-06-03-P1R-6-market-real-api-design.md | 365 ++++++++++++ 3 files changed, 1120 insertions(+) create mode 100644 docs/memorys/2026-06-03-P1R6Market规格计划.md create mode 100644 docs/superpowers/plans/2026-06-03-P1R-6-market-real-api.md create mode 100644 docs/superpowers/specs/2026-06-03-P1R-6-market-real-api-design.md diff --git a/docs/memorys/2026-06-03-P1R6Market规格计划.md b/docs/memorys/2026-06-03-P1R6Market规格计划.md new file mode 100644 index 00000000..820bb12b --- /dev/null +++ b/docs/memorys/2026-06-03-P1R6Market规格计划.md @@ -0,0 +1,221 @@ +# P1R6 Market 规格计划 + +日期:2026-06-03 + +## 背景 + +本次进入 P1R-6 Market Real API 的规格、计划和 review gate 阶段,只完成事实盘点、规格、执行计划、review 修订和阶段留痕,不进入代码实现。 + +真实工作面: + +```text +/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 +``` + +分支: + +```text +dev/1.0.0 +``` + +开始前已执行: + +```bash +git status --short --branch +git log --oneline -5 +git pull --ff-only origin dev/1.0.0 +``` + +结果: + +```text +## dev/1.0.0...origin/dev/1.0.0 +48c750b test(p1r): 补齐 AI 与 Knowledge 外部端到端验收 +Already up to date. +``` + +## 已验证事实 + +当前 coverage summary: + +```text +completedOperations 100 +needsVerificationOperations 100 +incompleteOperations 33 +genericPersistenceOperations 32 +ssePlaceholderOperations 1 +``` + +当前按域状态: + +```text +account 33 dedicated / needs_verification +ai 41 dedicated / completed +content 51 dedicated / needs_verification +events 1 sse_placeholder / incomplete +knowledge 59 dedicated / completed +market 32 generic_persistence / incomplete +meta 16 dedicated / needs_verification +``` + +Market 32 个 operation 当前全部为: + +```text +generic_persistence / incomplete +``` + +当前 Market 实现事实: + +- `docs/api-contracts/market/openapi.yaml` 声明 32 个 Market operation。 +- `muse-module-market-server` 当前合同入口仍是 `AdminMuseMarketContractController` 和 `AppMuseMarketContractController`。 +- 当前 Market 合同入口统一委托 `MuseContractPersistenceService`,不能作为真实 API 完成证据。 +- `muse-module-market-server` 当前只依赖 `muse-module-market-api`、security、mybatis。 +- `muse-server` 当前同时装配 `muse-module-market-server` 和 `muse-module-member-server`。 +- `muse-module-member-server` 已有 `MarketAccountProjectionFacade`、默认 fail-closed `UnavailableMarketAccountProjectionFacade`、`muse_account_record_projection` DO/Mapper 和 V11 DDL。 + +当前 DDL 事实: + +- V6 已有 Market 初始资产、版本、安装、发布申请、申诉和 handoff 表。 +- V8 已有 workflow task、favorite、purchase 表。 +- V11 已有 Account 查询读模型 `muse_account_record_projection`。 +- P1R-5 已使用 V14;P1R-6 如新增迁移,推荐使用 `V15__extend_market_real_api_schema.sql`。 + +## 本阶段产物 + +- 规格:`docs/superpowers/specs/2026-06-03-P1R-6-market-real-api-design.md` +- 计划:`docs/superpowers/plans/2026-06-03-P1R-6-market-real-api.md` +- 留痕:`docs/memorys/2026-06-03-P1R6Market规格计划.md` + +## Review 结果 + +本阶段按要求执行 fresh review gate。 + +Spec compliance review: + +```text +PASS_WITH_WARNINGS +``` + +已修复首轮阻塞点: + +- 计划开头错误写成只允许执行 `Task 1-12`,但正文实际包含 Task 13。已修为 `Task 1-13`。 + +Spec review 非阻塞提醒: + +- handoff token 业务状态里的 `completed` 不能和 coverage `completionStatus=completed` 混淆。计划已补充消歧说明。 + +Quality / feasibility review: + +```text +PASS_WITH_WARNINGS +``` + +已修复阻塞点: + +- Task 5 不再要求 Market 模块另建同名 `MarketAccountProjectionFacade` / `UnavailableMarketAccountProjectionFacade`。 +- 已明确 P1R-6 Account 投影采用最小可执行接线方案:`muse-module-market-server` 显式依赖 `muse-module-member-server`,复用 member 侧既有 `MarketAccountProjectionFacade`、`AccountRecordProjectionDO`、`AccountRecordProjectionMapper`。 +- 已明确 `muse_market_account_projection` 只表达 Market 侧 projection sync / outbox 状态,不是 Account 查询读模型。 +- 已明确 Account 查询读模型只能是 V11 已有 `muse_account_record_projection`。 +- 已要求实现阶段用 Maven reactor 或 dependency-tree 验证不形成 Maven 循环,并用 Spring context 测试证明真实 provider 替换默认 unavailable bean。 + +Quality review 非阻塞提醒: + +- 当前 worktree 仍有 P1R-4 / P1R-5 coverage/test/script 未提交 diff,P1R-6 spec/plan/memory 也是未跟踪文件。进入 P1R-6 实现前必须先提交或明确保留这些 diff,避免阶段混账。 + +## 关键设计结论 + +- P1R-6 目标状态只能是 `dedicated / needs_verification`,不能写成 `completed`。 +- Market owner 是 `muse-module-market`,负责市场来源侧事实、授权、安装、handoff、发布、治理和申诉。 +- Market 不直接写 AI / Knowledge / Content canonical facts。 +- 购买只产生授权,不等于安装;安装只进入账户可用列表,不等于绑定目标 owner。 +- Account 只消费 Market 写入的投影;Account 查询读模型为 `muse_account_record_projection`。 +- `needs_recheck` 不能作为 Market `SourceStatus` 值,只能表达为 action policy / recheck reasons。 +- P1R-7 统一 Events SSE 不属于 P1R-6。 +- 不修改 `docs/api-contracts/market/openapi.yaml`,不修改 scanner 掩盖缺口。 + +## 验证命令与结果 + +coverage audit: + +```bash +python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check +``` + +结果: + +```text +Generated docs/superpowers/reports/p1r-api-coverage.json +Generated docs/superpowers/reports/p1r-api-coverage.md +``` + +coverage 对账: + +```bash +jq -r '.summary | [.completedOperations,.needsVerificationOperations,.incompleteOperations,.genericPersistenceOperations,.ssePlaceholderOperations] | @tsv' docs/superpowers/reports/p1r-api-coverage.json +jq -r '.operations[] | select(.domain == "market") | [.implementationStatus,.completionStatus] | @tsv' docs/superpowers/reports/p1r-api-coverage.json | sort | uniq -c +``` + +结果: + +```text +100 100 33 32 1 +32 generic_persistence incomplete +``` + +文档结构检查: + +```bash +python3 - <<'PY' +import re, pathlib +plan=pathlib.Path('docs/superpowers/plans/2026-06-03-P1R-6-market-real-api.md').read_text() +spec=pathlib.Path('docs/superpowers/specs/2026-06-03-P1R-6-market-real-api-design.md').read_text() +print('plan_tasks', re.findall(r'^## Task (\d+):', plan, re.M)) +print('spec_operation_rows', len(re.findall(r'^\| `[^`]+` \| (GET|POST|DELETE|PUT|PATCH) \| `/', spec, re.M))) +print('plan_operation_lines', len(re.findall(r'^[A-Za-z].+ (GET|POST|DELETE|PUT|PATCH) /', plan, re.M))) +PY +``` + +结果: + +```text +plan_tasks ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13'] +spec_operation_rows 32 +plan_operation_lines 32 +``` + +diff hygiene: + +```bash +git diff --check +``` + +结果:无输出。 + +OpenAPI diff 检查: + +```bash +git status --short -- docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml +``` + +结果:无输出。 + +## 当前阶段边界 + +本阶段只完成 P1R-6 规格、计划、review 修订和 memory 留痕,没有实现代码。 + +当前不应把任何 Market operation 标为 `completed`。 + +## 下一步 + +进入 P1R-6 实现前必须先处理工作面: + +1. 提交或明确保留 P1R-4 / P1R-5 coverage completed 推进 diff。 +2. 提交或明确保留 P1R-6 spec / plan / memory 文档 diff。 +3. 再按 `docs/superpowers/plans/2026-06-03-P1R-6-market-real-api.md` Task 1-13 执行。 + +实现阶段执行规则: + +- 同一实现 Task 内复用最近 implementer。 +- 新 Task 才 fresh implementer。 +- 每个 Task 完成后仍必须 fresh spec review 和 fresh quality / feasibility review。 +- review 结论不能替代真实运行态验收或 coverage `completed` 批准。 diff --git a/docs/superpowers/plans/2026-06-03-P1R-6-market-real-api.md b/docs/superpowers/plans/2026-06-03-P1R-6-market-real-api.md new file mode 100644 index 00000000..56c3a810 --- /dev/null +++ b/docs/superpowers/plans/2026-06-03-P1R-6-market-real-api.md @@ -0,0 +1,534 @@ +# P1R-6 Market 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:** 将 Market 32 个 operation 从 `generic_persistence / incomplete` 推进到专用 Market Controller / Service / DTO / DAL / DDL / 测试可验收的 `dedicated / needs_verification`。 + +**Architecture:** 以 `muse-module-market` 为 owner,新增 Market 合同专用 app/admin 入口和应用服务。Market 只持有来源侧市场事实、授权摘要、安装、handoff、发布审核和治理状态;AI / Knowledge / Content 目标 owner 事实必须通过各自 owner 完成,Market 不直接写入。Account 只消费 Market 写入 `muse_account_record_projection` 的投影,并通过 member 模块既有 `MarketAccountProjectionFacade` 判断投影是否可用,不成为 Market 的运行时代码 owner。 + +**Tech Stack:** Java 21、Spring Boot、Yudao `CommonResult`、Spring Security `@PreAuthorize`、MyBatis Plus、PostgreSQL/Flyway、JUnit 5、Mockito/MockMvc、P1R coverage JSON。 + +**Implementation Status:** 本文件是 P1R-6 规格计划阶段产物;review gate 待执行;当前不进入实现代码。收到“进入 P1R-6 实现阶段”后才允许按 Task 1-13 执行。 + +--- + +## 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` 的其他 checkout 上写阶段文档或实现代码。 +- 不修改 `docs/api-contracts/market/openapi.yaml`。 +- 不修改 `docs/api-contracts/ai/openapi.yaml`、`docs/api-contracts/knowledge/openapi.yaml`。 +- 不修改 `muse-cloud/scripts/**` 把 Market 伪装成 `completed`。 +- 不破坏 AI 41、Knowledge 59 当前 `dedicated / completed`。 +- 不破坏 Content 51、Meta 16、Account 33 当前 `dedicated / needs_verification`。 +- 不把 Market operation 标为 `completed`。 +- 不跨入 P1R-7 统一 `/app-api/muse/events`。 +- 如果新增或修改 DDL,阶段收口前必须在同一 PostgreSQL 实例的 `_test` 数据库跑真实 Flyway。 + +## 1. 当前事实盘点 + +Market operation 清单: + +```text +listMarketplaceAssets GET /app-api/muse/marketplace/assets +listMarketplaceCategories GET /app-api/muse/marketplace/categories +listMarketplaceRecommendations GET /app-api/muse/marketplace/recommendations +getMarketplaceAsset GET /app-api/muse/marketplace/assets/{assetId} +favoriteAsset POST /app-api/muse/marketplace/assets/{assetId}/favorite +unfavoriteAsset DELETE /app-api/muse/marketplace/assets/{assetId}/favorite +purchaseAsset POST /app-api/muse/marketplace/assets/{assetId}/purchase +installMarketplaceAsset POST /app-api/muse/marketplace/assets/{assetId}/install +createBindPrecheck POST /app-api/muse/marketplace/assets/{assetId}/bind-precheck +createMarketplaceHandoff POST /app-api/muse/marketplace/handoffs +getHandoffStatus GET /app-api/muse/marketplace/handoffs/{handoffToken} +cancelHandoff POST /app-api/muse/marketplace/handoffs/{handoffToken}/cancel +savePublishDraft POST /app-api/muse/marketplace/publish-drafts +runPublishCheck POST /app-api/muse/marketplace/publish-drafts/{draftId}/checks +submitPublishRequest POST /app-api/muse/marketplace/publish-requests +withdrawPublishRequest POST /app-api/muse/marketplace/publish-requests/{requestId}/withdraw +listMyPublishRecords GET /app-api/muse/marketplace/my-publish-records +getGovernanceImpact GET /app-api/muse/marketplace/assets/{assetId}/governance-impact +submitAppeal POST /app-api/muse/marketplace/appeals +supplementAppeal POST /app-api/muse/marketplace/appeals/{appealId}/supplements +withdrawAppeal POST /app-api/muse/marketplace/appeals/{appealId}/withdraw +adminListMarketAssets GET /admin-api/muse/market/assets +adminGetMarketAsset GET /admin-api/muse/market/assets/{assetId} +adminPreviewGovernanceImpact POST /admin-api/muse/market/assets/{assetId}/governance-impact +adminDelistAsset POST /admin-api/muse/market/assets/{assetId}/delist +adminRecallAsset POST /admin-api/muse/market/assets/{assetId}/recall +adminListPublishRequests GET /admin-api/muse/market/publish-requests +adminApprovePublishRequest POST /admin-api/muse/market/publish-requests/{requestId}/approve +adminRejectPublishRequest POST /admin-api/muse/market/publish-requests/{requestId}/reject +adminListAppeals GET /admin-api/muse/market/appeals +adminGetAppeal GET /admin-api/muse/market/appeals/{appealId} +adminResolveAppeal POST /admin-api/muse/market/appeals/{appealId}/resolve +``` + +全部 32 个当前状态均为 `generic_persistence / incomplete`,当前实现 owner 是 `AdminMuseMarketContractController` / `AppMuseMarketContractController` + `MuseContractPersistenceService`。 + +## 2. 子代理执行规则 + +同一实现 Task 内,复用最近一次负责该任务的 implementer;只有进入新的实现 Task 时,才启用 fresh implementer。 + +每个实现 Task 完成后必须两轮 fresh review: + +1. spec compliance review:检查是否符合 `docs/superpowers/specs/2026-06-03-P1R-6-market-real-api-design.md` 和本计划。 +2. quality / feasibility review:检查最小改动、可维护性、测试质量、外部 owner 失败路径、无脚本掩盖。 + +review 未通过不能进入下一 Task。review 修复仍属于同一实现 Task,复用最近 implementer;重新 review 必须 fresh reviewer。 + +## Task 1: Preflight 与覆盖基线冻结 + +**Files:** + +- Read: `docs/superpowers/reports/p1r-api-coverage.json` +- Read: `docs/superpowers/reports/p1r-api-coverage.md` +- Read: `docs/api-contracts/market/openapi.yaml` +- Read: `muse-cloud/muse-module-market/**` +- Read: `muse-cloud/sql/muse/**` +- Read: `docs/memorys/2026-06-03-P1R4P1R5完成状态推进.md` + +- [ ] **Step 1: 核实工作面** + +Run: + +```bash +git status --short --branch +``` + +Expected: + +```text +## dev/1.0.0...origin/dev/1.0.0 +``` + +进入实现前,P1R-4 / P1R-5 coverage 状态推进和 P1R-6 spec/plan 必须已提交,或明确作为唯一待提交文档/coverage diff 存在。不得混入其它代码 diff。 + +- [ ] **Step 2: 统计 Market operation 状态** + +Run: + +```bash +jq -r '.operations[] | select(.domain == "market") | [.implementationStatus,.completionStatus] | @tsv' docs/superpowers/reports/p1r-api-coverage.json | sort | uniq -c +``` + +Expected: + +```text +32 generic_persistence incomplete +``` + +- [ ] **Step 3: 冻结 operation 清单** + +Run: + +```bash +jq -r '.operations[] | select(.domain == "market") | [.operationId,.method,.path,.implementationStatus,.completionStatus] | @tsv' docs/superpowers/reports/p1r-api-coverage.json +``` + +Expected: 输出 32 行,且 operationId 与本计划第 1 节一致。 + +- [ ] **Step 4: 检查禁止修改面** + +Run: + +```bash +git status --short -- docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml muse-cloud/scripts +``` + +Expected: 无输出;如果存在 P1R-4 / P1R-5 coverage 脚本已批准改动,必须在进入 P1R-6 实现前先提交。 + +## Task 2: Market 基础设施与 V15 DDL + +**Files:** + +- Modify: `muse-cloud/muse-module-market/muse-module-market-server/pom.xml` +- Create/Modify: `muse-cloud/muse-module-market/muse-module-market-api/src/main/java/cn/iocoder/muse/module/market/enums/ErrorCodeConstants.java` +- Create: `muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/domain/muse/*.java` +- Create: `muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/application/muse/*.java` +- Create: `muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/dal/dataobject/muse/*.java` +- Create: `muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/dal/mysql/muse/*.java` +- Create: `muse-cloud/sql/muse/V15__extend_market_real_api_schema.sql` +- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMarketMigrationSqlTest.java` + +- [ ] **Step 1: 确认 Market server 模块依赖** + +确认或补齐 validation、web、security、mybatis、test 依赖。P1R-6 Account 投影采用最小可执行接线方案:`muse-module-market-server` 显式依赖 `muse-module-member-server`,复用 member 侧既有 `MarketAccountProjectionFacade`、`AccountRecordProjectionDO` 和 `AccountRecordProjectionMapper`。当前 `member-server` 不依赖 `market-server`,该依赖方向不得形成 Maven 循环;实现时必须用 Maven reactor 或 dependency-tree 验证。不要引入 AI / Knowledge / Content 作为强 owner 依赖;跨 owner 能力通过本模块 facade 明确隔离。 + +- [ ] **Step 2: 增加 Market 错误码** + +至少覆盖: + +- `MARKET_API_VERSION_UNSUPPORTED` +- `MARKET_COMMAND_ID_REQUIRED` +- `MARKET_COMMAND_ID_CONFLICT` +- `MARKET_ASSET_NOT_EXISTS` +- `MARKET_ASSET_NOT_LISTED` +- `MARKET_ASSET_GOVERNANCE_BLOCKED` +- `MARKET_LICENSE_NOT_EXISTS` +- `MARKET_LICENSE_NOT_PURCHASABLE` +- `MARKET_INSTALL_NOT_EXISTS` +- `MARKET_INSTALL_NOT_BINDABLE` +- `MARKET_HANDOFF_NOT_EXISTS` +- `MARKET_HANDOFF_EXPIRED` +- `MARKET_HANDOFF_NOT_CANCELLABLE` +- `MARKET_PUBLISH_DRAFT_NOT_EXISTS` +- `MARKET_PUBLISH_CHECK_NOT_EXISTS` +- `MARKET_PUBLISH_CHECK_EXPIRED` +- `MARKET_PUBLISH_REQUEST_NOT_EXISTS` +- `MARKET_PUBLISH_REQUEST_STATUS_CONFLICT` +- `MARKET_GOVERNANCE_PREVIEW_REQUIRED` +- `MARKET_APPEAL_NOT_EXISTS` +- `MARKET_APPEAL_STATUS_CONFLICT` +- `MARKET_TARGET_OWNER_UNAVAILABLE` +- `MARKET_ACCOUNT_PROJECTION_UNAVAILABLE` + +- [ ] **Step 3: 编写 V15 迁移** + +至少新增或扩展: + +- `muse_market_command` +- `muse_market_authorization_snapshot` +- `muse_market_authorization_summary` +- `muse_market_handoff_event` +- `muse_market_publish_draft` +- `muse_market_publish_check` +- `muse_market_review_event` +- `muse_market_governance_preview` +- `muse_market_governance_action` +- `muse_market_governance_impact` +- `muse_market_appeal_material` +- `muse_market_appeal_event` +- `muse_market_account_projection` +- `muse_market_source_status_event` + +DDL 必须有 tenant、command、status、revision、audit 时间、必要唯一约束和查询索引。其中 `muse_market_account_projection` 只表达 Market 侧投影同步/outbox 状态,不是 Account 查询读模型;Account 查询读模型只能是 V11 已有的 `muse_account_record_projection`。 + +- [ ] **Step 4: DDL 静态测试** + +`P1rMarketMigrationSqlTest` 至少验证: + +- V15 文件存在。 +- 新表名存在。 +- `muse_market_command` 有 `command_id`、`request_hash` 和唯一约束。 +- handoff token 不以明文作为唯一安全依据,必须存在 token hash 或等价字段。 +- governance action / preview / impact 三类事实不被压成一个字段。 + +## Task 3: Market command、version、owner guard + +**Files:** + +- Create: `.../domain/muse/MarketApiVersionGuard.java` +- Create: `.../domain/muse/MarketCommandGuard.java` +- Create: `.../domain/muse/MarketOwnerGuard.java` +- Create: `.../application/muse/MarketCommandService.java` +- Create: `.../application/muse/MarketCommandServiceImpl.java` +- Test: `.../application/muse/MarketCommandServiceTest.java` + +- [ ] **Step 1: 实现 API version guard** + +所有 dedicated Controller 入口必须校验 `X-API-Version`,不支持版本返回业务错误,不进入应用服务写事实。 + +- [ ] **Step 2: 实现 command 幂等 guard** + +相同 command + 相同 request hash 返回已保存结果;相同 command + 不同 request hash 返回冲突。 + +- [ ] **Step 3: 实现 owner/tenant guard** + +App 查询只能看当前用户可见资产、授权、安装、发布记录和申诉。Admin 查询必须保留权限注解,不能用 app user 可见性替代。 + +## Task 4: 资产发现、分类、推荐、详情、收藏 + +**Files:** + +- Create: `.../controller/app/muse/AppMuseMarketplaceAssetController.java` +- Create: `.../controller/app/muse/vo/*.java` +- Create: `.../application/muse/MarketAssetQueryService.java` +- Create: `.../application/muse/MarketFavoriteService.java` +- Test: `.../controller/app/muse/AppMuseMarketplaceAssetControllerTest.java` +- Test: `.../application/muse/MarketFavoriteServiceTest.java` + +- [ ] **Step 1: 实现资产列表、分类、推荐、详情** + +读取 Market 资产、版本和展示摘要。无独立推荐数据时可以使用可解释 fallback 排序,但必须在响应摘要中体现来源,不允许 fake 个性化。 + +- [ ] **Step 2: 实现收藏/取消收藏** + +写 `muse_market_favorite`,保持幂等。取消收藏不能删除审计事实,只能更新状态。 + +- [ ] **Step 3: 测试合同字段和可见性** + +测试覆盖 listed/delisted/recall、当前用户收藏状态、分页结构、未登录和不可见资产。 + +## Task 5: 授权购买、安装和 Account 投影 + +**Files:** + +- Create: `.../controller/app/muse/AppMuseMarketLicenseController.java` +- Create: `.../application/muse/MarketLicenseService.java` +- Create: `.../application/muse/MarketInstallService.java` +- Create: `.../application/muse/MarketAccountProjectionProvider.java` +- Implement: `cn.iocoder.muse.module.member.application.account.facade.MarketAccountProjectionFacade` provider bean +- Use: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/dataobject/account/AccountRecordProjectionDO.java` +- Use: `muse-cloud/muse-module-member/muse-module-member-server/src/main/java/cn/iocoder/muse/module/member/dal/mysql/account/AccountRecordProjectionMapper.java` +- Test: `.../application/muse/MarketLicenseServiceTest.java` +- Test: `.../application/muse/MarketInstallServiceTest.java` +- Test: `.../application/muse/MarketAccountProjectionProviderTest.java` +- Test: `muse-cloud/muse-module-member/muse-module-member-server/src/test/java/cn/iocoder/muse/module/member/application/account/AccountMarketRecordServiceTest.java` + +- [ ] **Step 1: 实现 purchaseAsset** + +校验资产可获取、许可可用、治理策略允许新获取。写购买/授权快照,并向既有 `muse_account_record_projection` 写入 `purchase` / `license` 投影。重复 command 返回同一授权结果。 + +- [ ] **Step 2: 实现 installMarketplaceAsset** + +安装必须消费有效授权。安装智能体和知识库只进入账户可用列表,并更新 Account `license` 投影状态;不写 AI / Knowledge 目标事实。作品资产不走安装流程。 + +- [ ] **Step 3: Account projection fail-closed** + +不得在 Market 模块另建同名 `MarketAccountProjectionFacade` 或 `UnavailableMarketAccountProjectionFacade` 作为运行态契约。Market 必须通过 `market-server -> member-server` 依赖对接 member 模块既有 `cn.iocoder.muse.module.member.application.account.facade.MarketAccountProjectionFacade`,并提供真实 provider bean 替换 member 默认 auto-configuration 注册的 unavailable bean:当 Market projection provider 未接入时,Account 查询继续 fail-closed;当 provider 接入且投影写入成功后,测试必须证明 Account `purchase` / `license` / `publish` 查询不再命中默认 unavailable 实现。投影写入失败时,Market 业务事实只能记录为 projection pending / blocked,不能伪造成 Account 已同步。 + +## Task 6: 来源授权摘要与 Handoff + +**Files:** + +- Create: `.../controller/app/muse/AppMuseMarketHandoffController.java` +- Create: `.../application/muse/MarketHandoffService.java` +- Create: `.../application/muse/facade/MarketTargetOwnerFacade.java` +- Create: `.../application/muse/facade/UnavailableMarketTargetOwnerFacade.java` +- Test: `.../application/muse/MarketHandoffServiceTest.java` +- Test: `.../controller/app/muse/AppMuseMarketHandoffControllerTest.java` + +- [ ] **Step 1: 实现 createBindPrecheck** + +生成来源侧 authorization summary 和 authorization snapshot。结果不能包含目标 owner precheck ID。 + +- [ ] **Step 2: 实现 createMarketplaceHandoff** + +生成一次性 handoff token、token hash、过期时间和目标页。Market 不消费 token,不写目标 owner。 + +- [ ] **Step 3: 实现 getHandoffStatus / cancelHandoff** + +查询不改变状态。取消只能取消 pending / owner_precheck_created 前的 token;已 completed、expired、cancelled 不可取消。这里的 `completed` 只表示 handoff token 业务生命周期状态,不等于 coverage `completionStatus=completed`,不得据此推进 Market operation completed。 + +## Task 7: 发布草稿、发布检查、提交与撤回 + +**Files:** + +- Create: `.../controller/app/muse/AppMuseMarketPublishController.java` +- Create: `.../application/muse/MarketPublishService.java` +- Test: `.../application/muse/MarketPublishServiceTest.java` +- Test: `.../controller/app/muse/AppMuseMarketPublishControllerTest.java` + +- [ ] **Step 1: 实现 savePublishDraft** + +保存发布材料草稿,不提交审核、不上架。作品资产高阶模式只允许保存为 `read_only_favorite_license`。 + +- [ ] **Step 2: 实现 runPublishCheck** + +检查权利、隐私、许可、治理和材料 hash。检查通过只允许 submit,不上架。 + +- [ ] **Step 3: 实现 submitPublishRequest / withdrawPublishRequest / listMyPublishRecords** + +提交必须原子消费未过期、匹配草稿版本和材料 hash 的检查结果。撤回只允许非终态申请。 + +## Task 8: 管理端审核、上架、拒绝 + +**Files:** + +- Create: `.../controller/admin/muse/AdminMuseMarketReviewController.java` +- Create: `.../application/muse/AdminMarketReviewService.java` +- Test: `.../application/muse/AdminMarketReviewServiceTest.java` +- Test: `.../controller/admin/muse/AdminMuseMarketReviewControllerTest.java` + +- [ ] **Step 1: 实现 adminListPublishRequests** + +支持审核状态、资产类型、风险标签、发布者筛选。 + +- [ ] **Step 2: 实现 adminApprovePublishRequest** + +通过必须消费审核校验结果,写审核事件,更新资产/版本为 listed,可见状态必须可查询。 + +- [ ] **Step 3: 实现 adminRejectPublishRequest** + +拒绝必须写理由和证据标签,发布者可后续申诉。终态申请不可重复审核。 + +## Task 9: 管理端资产治理、下架、召回和影响预览 + +**Files:** + +- Create: `.../controller/admin/muse/AdminMuseMarketAssetController.java` +- Create: `.../application/muse/AdminMarketGovernanceService.java` +- Test: `.../application/muse/AdminMarketGovernanceServiceTest.java` +- Test: `.../controller/admin/muse/AdminMuseMarketAssetControllerTest.java` + +- [ ] **Step 1: 实现 adminListMarketAssets / adminGetMarketAsset** + +管理端详情必须包含版本、授权、安装、治理和来源摘要。 + +- [ ] **Step 2: 实现 adminPreviewGovernanceImpact** + +生成影响预览,覆盖授权、安装、handoff、绑定摘要、运行中任务、Shadow 候选、Knowledge Draft 和导出限制。 + +- [ ] **Step 3: 实现 adminDelistAsset / adminRecallAsset** + +必须消费有效 `impactPreviewId` 和 expectedStatus。召回要写 source status propagation 事实,owner facade 不可用时记录 blocked,并把需要重验的信息写入 action policy / recheck reasons;`needs_recheck` 不能作为 Market `SourceStatus` 值。 + +## Task 10: 申诉提交、补充、撤回与管理端处理 + +**Files:** + +- Create: `.../controller/app/muse/AppMuseMarketAppealController.java` +- Create: `.../controller/admin/muse/AdminMuseMarketAppealController.java` +- Create: `.../application/muse/MarketAppealService.java` +- Test: `.../application/muse/MarketAppealServiceTest.java` +- Test: `.../controller/app/muse/AppMuseMarketAppealControllerTest.java` +- Test: `.../controller/admin/muse/AdminMuseMarketAppealControllerTest.java` + +- [ ] **Step 1: 实现用户端申诉** + +提交、补充、撤回必须校验申诉期限、关联关系和 expectedStatus。补充材料保存脱敏摘要,不记录密钥或无关正文全文。 + +- [ ] **Step 2: 实现管理端申诉查询与处理** + +只读取申诉材料和发布资产,不读取用户私有副本。恢复上架必须消费治理影响预览。 + +## Task 11: Retire Market 合同兜底与 coverage gate + +**Files:** + +- Modify: `muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/controller/admin/AdminMuseMarketContractController.java` +- Modify: `muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/controller/app/AppMuseMarketContractController.java` +- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMarketRealApiGateTest.java` +- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMarketRouteOwnershipTest.java` + +- [ ] **Step 1: 精确退役 32 个 Market 路由兜底** + +合同兜底 Controller 不能继续捕获 P1R-6 32 个路由。可以删除或精确避让,但必须由 route ownership test 证明。 + +- [ ] **Step 2: coverage gate** + +`P1rMarketRealApiGateTest` 必须证明: + +- Market operation 数量为 32。 +- 32 个 operation 全部 `dedicated / needs_verification`。 +- Market 不包含 `generic_persistence`、`catch_all`、`sse_placeholder`、`missing`。 +- `completedOperations` 仍为 100。 +- AI / Knowledge 仍是 `dedicated / completed`。 +- Content / Meta / Account 仍是 `dedicated / needs_verification`。 +- Events P1R-7 `streamEvents` 仍是唯一 SSE placeholder。 + +- [ ] **Step 3: route ownership gate** + +`P1rMarketRouteOwnershipTest` 必须证明 32 个 Market 路由 handler 不属于 `*MarketContractController`,且不调用 `MuseContractPersistenceService`。 + +## Task 12: PostgreSQL / Flyway `_test` 迁移验收 + +**Files:** + +- Create: `muse-cloud/muse-server/src/test/java/cn/iocoder/muse/server/framework/api/P1rMarketFlywayMigrationIT.java` + +- [ ] **Step 1: 构建 reactor** + +Run: + +```bash +cd muse-cloud +JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ + mvn -o clean install -DskipTests -Dspring-boot.repackage.skip=true -pl muse-server -am +``` + +- [ ] **Step 2: 跑真实 PostgreSQL / Flyway** + +Run: + +```bash +cd muse-cloud +set -a +. ~/.config/muse-repo/infra.env +set +a +export P1R_FLYWAY_PASSWORD="$MUSE_POSTGRES_PASSWORD" +P1R6_TEST_DB="${P1R6_TEST_DB:-muse_local_p1r6_test}" +JAVA_HOME=$(/usr/libexec/java_home -v 21) PATH="$JAVA_HOME/bin:$PATH" \ + mvn -o test -pl muse-server \ + -Dtest=P1rMarketFlywayMigrationIT \ + -Dp1r.flyway.url="jdbc:postgresql://${MUSE_POSTGRES_HOST}:${MUSE_POSTGRES_PORT}/${P1R6_TEST_DB}" \ + -Dp1r.flyway.user="$MUSE_POSTGRES_USERNAME" \ + -Dp1r.flyway.locations="filesystem:sql/muse" +``` + +Expected: + +```text +migrations_executed >= 15 +target_schema_version = 15 +flyway_latest = 15:extend market real api schema +``` + +## Task 13: 最终验证、报告对账与 memory + +**Files:** + +- Modify: `docs/superpowers/reports/p1r-api-coverage.json` +- Modify: `docs/superpowers/reports/p1r-api-coverage.md` +- Create: `docs/memorys/YYYY-MM-DD-P1R6Market真实API收口.md` + +- [ ] **Step 1: 重新生成 coverage** + +Run: + +```bash +python3 muse-cloud/scripts/p1r-audit-api-coverage.py --check +``` + +- [ ] **Step 2: 对账 coverage** + +Run: + +```bash +jq -r '.operations | group_by(.domain)[] | [.[0].domain, (length|tostring), ([group_by(.implementationStatus + "/" + .completionStatus)[] | (.[0].implementationStatus + "/" + .[0].completionStatus + ":" + (length|tostring))] | join(", "))] | @tsv' docs/superpowers/reports/p1r-api-coverage.json +jq -r '.summary | [.completedOperations,.needsVerificationOperations,.incompleteOperations,.genericPersistenceOperations,.ssePlaceholderOperations] | @tsv' docs/superpowers/reports/p1r-api-coverage.json +``` + +Expected: + +```text +market dedicated/needs_verification:32 +completedOperations = 100 +genericPersistenceOperations = 0 +ssePlaceholderOperations = 1 +``` + +- [ ] **Step 3: Maven gate** + +Run: + +```bash +cd muse-cloud +JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -pl muse-server -Dtest=P1rApiCoverageReportTest,P1rMarketRealApiGateTest,P1rMarketRouteOwnershipTest,P1rMarketMigrationSqlTest test +``` + +- [ ] **Step 4: focused tests** + +Run the focused Market service/controller tests created in Tasks 3-11. + +- [ ] **Step 5: diff hygiene** + +Run: + +```bash +git diff --check +git status --short -- docs/api-contracts/market/openapi.yaml docs/api-contracts/ai/openapi.yaml docs/api-contracts/knowledge/openapi.yaml +``` + +Expected: no OpenAPI diff and no whitespace errors. + +## 3. Review handoff + +完成本计划文档后必须执行两轮 fresh review: + +1. spec compliance review:确认本计划覆盖 `docs/superpowers/specs/2026-06-03-P1R-6-market-real-api-design.md`,没有跨入目标 owner 事实写入。 +2. quality / feasibility review:确认任务拆分、DDL、测试门禁、失败路径和验证命令足够可执行。 + +review 通过前不得进入 P1R-6 代码实现。 diff --git a/docs/superpowers/specs/2026-06-03-P1R-6-market-real-api-design.md b/docs/superpowers/specs/2026-06-03-P1R-6-market-real-api-design.md new file mode 100644 index 00000000..e10d6113 --- /dev/null +++ b/docs/superpowers/specs/2026-06-03-P1R-6-market-real-api-design.md @@ -0,0 +1,365 @@ +# P1R-6:Market Real API 规格 + +- 版本:v1 +- 日期:2026-06-03 +- 状态:review gate 待执行 +- 上游规格:`docs/superpowers/specs/2026-05-25-P1R-muse-cloud-real-api-design.md` +- 基线输入:`docs/superpowers/reports/p1r-api-coverage.json` +- 范围:`muse-cloud/muse-module-market/**` 中 Market 专用实现、Market 相关追加迁移、授权/安装/handoff/发布/治理/申诉状态机、P1R-6 覆盖门禁 +- 非范围:P1R-7 统一 Events SSE、目标 owner 绑定确认、AI / Knowledge / Content canonical 写入、修改 `docs/api-contracts/market/openapi.yaml`、把 Market operation 标为 `completed` + +## 1. 结论 + +P1R-6 的目标不是继续用 `MuseContractPersistenceService` 记录市场合同请求,也不是把 Market 设计成能直接替目标 owner 写入作品、智能体槽位或知识库绑定事实的“中间缝合层”。 + +本阶段要把 Market 域从“通用持久化可响应”推进到“专用 Market API 可验证”: + +1. Market 32 个 operation 当前全部是 `generic_persistence / incomplete`,必须退出通用持久化完成口径。 +2. Market owner 落在 `muse-module-market`,负责市场资产、版本、分类/推荐曝光、收藏、购买授权、安装、来源授权摘要、handoff、发布草稿、发布检查、发布申请、管理端审核、下架、召回和申诉。 +3. Market 只生成来源侧授权摘要和 handoff token。目标 owner 的预检、确认和事实写入仍由 AI、Knowledge 或 Content 自己完成。 +4. 购买只产生授权记录,不等于安装;安装只把资产放入账户可用列表,不等于绑定作品或写目标事实。 +5. 作品资产高阶模式默认关闭。未完成 ADR、Schema/API、lineage 和授权快照前,只允许 `read_only_favorite_license`。 +6. 下架/召回必须形成治理影响预览、状态传播和审计事实,不能只改 `listing_status`。 +7. P1R-6 实现通过后的覆盖目标只能是 `dedicated / needs_verification`。没有 P1R-7 真实跨域端到端验收前,Market 不能写成 `completed`。 + +## 2. 已验证事实 + +### 2.1 执行工作面 + +真实工作面: + +```text +/Users/qingse/.config/superpowers/worktrees/oh-my-muse/dev-1.0.0 +``` + +当前分支: + +```text +dev/1.0.0 +``` + +当前 worktree 已存在 P1R-4 / P1R-5 coverage 状态推进相关未提交 diff。进入 P1R-6 实现前,必须先提交或明确保留这些已验证改动,避免新阶段实现和上一阶段 coverage 收口混在不可追踪状态里。 + +### 2.2 覆盖矩阵 + +当前全局 summary: + +```text +completedOperations 100 +needsVerificationOperations 100 +incompleteOperations 33 +genericPersistenceOperations 32 +ssePlaceholderOperations 1 +``` + +按域状态: + +```text +account 33 dedicated / needs_verification +ai 41 dedicated / completed +content 51 dedicated / needs_verification +events 1 sse_placeholder / incomplete +knowledge 59 dedicated / completed +market 32 generic_persistence / incomplete +meta 16 dedicated / needs_verification +``` + +P1R-6 preflight 命令: + +```bash +jq -r '.operations[] | select(.domain == "market") | [.operationId,.method,.path,.targetStage,.implementationStatus,.completionStatus] | @tsv' docs/superpowers/reports/p1r-api-coverage.json +``` + +输出事实:32 行,全部为 `P1R-6 Market Real API / generic_persistence / incomplete`。 + +### 2.3 OpenAPI 事实 + +`docs/api-contracts/market/openapi.yaml` 声明 32 个 Market operation: + +| 分组 | 数量 | operation | +|---|---:|---| +| 用户端发现 | 4 | `listMarketplaceAssets`、`listMarketplaceCategories`、`listMarketplaceRecommendations`、`getMarketplaceAsset` | +| 收藏 | 2 | `favoriteAsset`、`unfavoriteAsset` | +| 授权与安装 | 2 | `purchaseAsset`、`installMarketplaceAsset` | +| Handoff | 4 | `createBindPrecheck`、`createMarketplaceHandoff`、`getHandoffStatus`、`cancelHandoff` | +| 发布 | 5 | `savePublishDraft`、`runPublishCheck`、`submitPublishRequest`、`withdrawPublishRequest`、`listMyPublishRecords` | +| 用户端治理与申诉 | 4 | `getGovernanceImpact`、`submitAppeal`、`supplementAppeal`、`withdrawAppeal` | +| 管理端资产治理 | 5 | `adminListMarketAssets`、`adminGetMarketAsset`、`adminPreviewGovernanceImpact`、`adminDelistAsset`、`adminRecallAsset` | +| 管理端发布审核 | 3 | `adminListPublishRequests`、`adminApprovePublishRequest`、`adminRejectPublishRequest` | +| 管理端申诉 | 3 | `adminListAppeals`、`adminGetAppeal`、`adminResolveAppeal` | + +关键合同约束: + +1. 所有接口使用 `X-API-Version`。 +2. 写命令使用 `commandId` 并结合 `expectedStatus` 或检查快照防止盲写。 +3. `purchaseAsset` 只获取授权,不安装、不绑定、不写目标 owner。 +4. `installMarketplaceAsset` 只写安装事实,不写作品事实。 +5. `createBindPrecheck` 只生成来源侧授权摘要和 handoff 准备信息,不生成目标 owner 预检 ID。 +6. `createMarketplaceHandoff` 只生成一次性 token 和目标页,不替换绑定、不解绑、不降权、不改作品事实。 +7. `runPublishCheck` 只证明草稿可提交审核,不直接上架。 +8. `submitPublishRequest` 必须原子消费未过期且匹配草稿 hash 的发布检查结果。 +9. `adminDelistAsset` / `adminRecallAsset` 必须绑定治理影响预览,不能盲下架或盲召回。 +10. 申诉处理只能读取申诉材料和发布资产,不读取用户私有副本。 + +### 2.4 当前代码事实 + +当前 Market 合同入口: + +- `muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/controller/admin/AdminMuseMarketContractController.java` +- `muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/controller/app/AppMuseMarketContractController.java` + +两个 Controller 都统一委托: + +```text +MuseContractPersistenceService.handle(...) +``` + +当前 `muse-module-market-server` 只有合同 Controller 和 `package-info.java`,没有 Market Application Service、专用 VO/DTO、DO、Mapper、状态机、owner facade 或 P1R-6 gate test。 + +`muse-server/pom.xml` 已装配: + +```text +muse-module-market-server +``` + +因此 P1R-6 实现应继续在 `muse-module-market-server` 内落 owner,不需要新增合同模块或把 Market 逻辑写入 Account、AI、Knowledge 或 Content。 + +### 2.5 当前 DDL + +`muse-cloud/sql/muse/V6__init_market_schema.sql` 已有: + +- `muse_market_asset` +- `muse_market_asset_version` +- `muse_market_installation` +- `muse_market_publish_request` +- `muse_market_appeal` +- `muse_market_handoff` + +`muse-cloud/sql/muse/V8__add_contract_workflow_and_market_interaction.sql` 已有: + +- `muse_domain_workflow_task` +- `muse_market_favorite` +- `muse_market_purchase` + +这些表不足以完成 P1R-6: + +1. 没有 Market 专用 command/requestHash/replay 表。 +2. 没有来源侧授权摘要、授权快照、兼容性结果和 handoff 消费状态的独立事实。 +3. 没有发布草稿、发布检查、发布检查快照、发布材料 hash、审核校验结果和审核证据记录。 +4. 没有治理影响预览、治理动作、治理传播、受影响授权/安装/绑定/任务/候选的可查询结果。 +5. 没有申诉补充材料、申诉处理历史、申诉证据脱敏摘要。 +6. 没有分类/推荐/曝光读模型或可降级策略。 +7. 没有 Account 投影同步状态,无法证明购买、安装、授权能被 Account 查询消费。 + +P1R-5 已使用 V14;P1R-6 如新增迁移,推荐使用: + +```text +muse-cloud/sql/muse/V15__extend_market_real_api_schema.sql +``` + +## 3. 目标 + +1. 将 Market 32 个 operation 从 `generic_persistence / incomplete` 推进到 `dedicated / needs_verification`。 +2. 建立 `muse-module-market` 专用 Admin/App Controller、Application Service、VO/DTO、DO、Mapper、Assembler 和 domain guard。 +3. 建立 Market command 幂等、API version guard、tenant/user/owner guard、expectedStatus guard。 +4. 建立资产、版本、分类、推荐、详情和收藏真实读写模型。 +5. 建立购买授权、安装、Account projection 同步状态和授权快照。 +6. 建立来源侧 bind precheck、authorization summary、authorization snapshot 和 handoff token 生命周期。 +7. 建立发布草稿、发布检查、发布申请、撤回和我的发布记录状态机。 +8. 建立管理端审核、下架、召回、治理影响预览和申诉处理状态机。 +9. 建立 owner facades,明确 Account、AI、Knowledge、Content 不可用时的 fail-closed 边界。 +10. 建立 P1R-6 coverage gate,防止 Market operation 继续被合同兜底、通用持久化或 `completed` 混淆。 + +## 4. 非目标 + +1. 不修改 Market OpenAPI 来适配当前代码。 +2. 不修改 scanner 掩盖 Market 仍未 dedicated 的事实。 +3. 不把 Market operation 标为 `completed`。 +4. 不实现 P1R-7 `/app-api/muse/events`。 +5. 不让 Market 直接写 AI agent slot、Knowledge binding、Content work/block/canonical 事实。 +6. 不在 P1R-6 开放作品资产 `full`、`reference`、`context_use` 高阶模式。 +7. 不把 `MuseContractPersistenceService`、空列表、固定样例或 `muse_domain_workflow_task` 原始行作为完成证据。 +8. 不把 Account、AI、Knowledge、Content owner unavailable 伪造成 success。 + +## 5. Operation 清单与目标 owner + +| operationId | Method | Path | 当前状态 | P1R-6 owner | P1R-6 目标 | +|---|---|---|---|---|---| +| `listMarketplaceAssets` | GET | `/app-api/muse/marketplace/assets` | `generic_persistence / incomplete` | Market Asset Query | `dedicated / needs_verification` | +| `listMarketplaceCategories` | GET | `/app-api/muse/marketplace/categories` | `generic_persistence / incomplete` | Market Discovery Query | `dedicated / needs_verification` | +| `listMarketplaceRecommendations` | GET | `/app-api/muse/marketplace/recommendations` | `generic_persistence / incomplete` | Market Discovery Query | `dedicated / needs_verification` | +| `getMarketplaceAsset` | GET | `/app-api/muse/marketplace/assets/{assetId}` | `generic_persistence / incomplete` | Market Asset Query | `dedicated / needs_verification` | +| `favoriteAsset` | POST | `/app-api/muse/marketplace/assets/{assetId}/favorite` | `generic_persistence / incomplete` | Market Favorite Command | `dedicated / needs_verification` | +| `unfavoriteAsset` | DELETE | `/app-api/muse/marketplace/assets/{assetId}/favorite` | `generic_persistence / incomplete` | Market Favorite Command | `dedicated / needs_verification` | +| `purchaseAsset` | POST | `/app-api/muse/marketplace/assets/{assetId}/purchase` | `generic_persistence / incomplete` | Market License Command | `dedicated / needs_verification` | +| `installMarketplaceAsset` | POST | `/app-api/muse/marketplace/assets/{assetId}/install` | `generic_persistence / incomplete` | Market Install Command | `dedicated / needs_verification` | +| `createBindPrecheck` | POST | `/app-api/muse/marketplace/assets/{assetId}/bind-precheck` | `generic_persistence / incomplete` | Market Handoff Source Command | `dedicated / needs_verification` | +| `createMarketplaceHandoff` | POST | `/app-api/muse/marketplace/handoffs` | `generic_persistence / incomplete` | Market Handoff Command | `dedicated / needs_verification` | +| `getHandoffStatus` | GET | `/app-api/muse/marketplace/handoffs/{handoffToken}` | `generic_persistence / incomplete` | Market Handoff Query | `dedicated / needs_verification` | +| `cancelHandoff` | POST | `/app-api/muse/marketplace/handoffs/{handoffToken}/cancel` | `generic_persistence / incomplete` | Market Handoff Command | `dedicated / needs_verification` | +| `savePublishDraft` | POST | `/app-api/muse/marketplace/publish-drafts` | `generic_persistence / incomplete` | Market Publish Draft Command | `dedicated / needs_verification` | +| `runPublishCheck` | POST | `/app-api/muse/marketplace/publish-drafts/{draftId}/checks` | `generic_persistence / incomplete` | Market Publish Check Command | `dedicated / needs_verification` | +| `submitPublishRequest` | POST | `/app-api/muse/marketplace/publish-requests` | `generic_persistence / incomplete` | Market Publish Request Command | `dedicated / needs_verification` | +| `withdrawPublishRequest` | POST | `/app-api/muse/marketplace/publish-requests/{requestId}/withdraw` | `generic_persistence / incomplete` | Market Publish Request Command | `dedicated / needs_verification` | +| `listMyPublishRecords` | GET | `/app-api/muse/marketplace/my-publish-records` | `generic_persistence / incomplete` | Market Publish Query | `dedicated / needs_verification` | +| `getGovernanceImpact` | GET | `/app-api/muse/marketplace/assets/{assetId}/governance-impact` | `generic_persistence / incomplete` | Market Governance Query | `dedicated / needs_verification` | +| `submitAppeal` | POST | `/app-api/muse/marketplace/appeals` | `generic_persistence / incomplete` | Market Appeal Command | `dedicated / needs_verification` | +| `supplementAppeal` | POST | `/app-api/muse/marketplace/appeals/{appealId}/supplements` | `generic_persistence / incomplete` | Market Appeal Command | `dedicated / needs_verification` | +| `withdrawAppeal` | POST | `/app-api/muse/marketplace/appeals/{appealId}/withdraw` | `generic_persistence / incomplete` | Market Appeal Command | `dedicated / needs_verification` | +| `adminListMarketAssets` | GET | `/admin-api/muse/market/assets` | `generic_persistence / incomplete` | Admin Market Asset Query | `dedicated / needs_verification` | +| `adminGetMarketAsset` | GET | `/admin-api/muse/market/assets/{assetId}` | `generic_persistence / incomplete` | Admin Market Asset Query | `dedicated / needs_verification` | +| `adminPreviewGovernanceImpact` | POST | `/admin-api/muse/market/assets/{assetId}/governance-impact` | `generic_persistence / incomplete` | Admin Governance Command | `dedicated / needs_verification` | +| `adminDelistAsset` | POST | `/admin-api/muse/market/assets/{assetId}/delist` | `generic_persistence / incomplete` | Admin Governance Command | `dedicated / needs_verification` | +| `adminRecallAsset` | POST | `/admin-api/muse/market/assets/{assetId}/recall` | `generic_persistence / incomplete` | Admin Governance Command | `dedicated / needs_verification` | +| `adminListPublishRequests` | GET | `/admin-api/muse/market/publish-requests` | `generic_persistence / incomplete` | Admin Publish Review Query | `dedicated / needs_verification` | +| `adminApprovePublishRequest` | POST | `/admin-api/muse/market/publish-requests/{requestId}/approve` | `generic_persistence / incomplete` | Admin Publish Review Command | `dedicated / needs_verification` | +| `adminRejectPublishRequest` | POST | `/admin-api/muse/market/publish-requests/{requestId}/reject` | `generic_persistence / incomplete` | Admin Publish Review Command | `dedicated / needs_verification` | +| `adminListAppeals` | GET | `/admin-api/muse/market/appeals` | `generic_persistence / incomplete` | Admin Appeal Query | `dedicated / needs_verification` | +| `adminGetAppeal` | GET | `/admin-api/muse/market/appeals/{appealId}` | `generic_persistence / incomplete` | Admin Appeal Query | `dedicated / needs_verification` | +| `adminResolveAppeal` | POST | `/admin-api/muse/market/appeals/{appealId}/resolve` | `generic_persistence / incomplete` | Admin Appeal Command | `dedicated / needs_verification` | + +## 6. 目标架构 + +```mermaid +flowchart TD + APP[App Market Controllers] --> SVC[Market Application Services] + ADM[Admin Market Controllers] --> SVC + SVC --> CMD[Market Command Service] + SVC --> ASSET[Asset and Discovery Service] + SVC --> LIC[License and Install Service] + SVC --> HND[Handoff Service] + SVC --> PUB[Publish Service] + SVC --> GOV[Governance Service] + SVC --> APL[Appeal Service] + CMD --> DB[(Market Tables)] + ASSET --> DB + LIC --> DB + HND --> DB + PUB --> DB + GOV --> DB + APL --> DB + LIC --> ACC[Account Projection Facade] + HND --> AI[AI Owner Facade] + HND --> KNO[Knowledge Owner Facade] + HND --> CON[Content Owner Facade] + GOV --> SRC[Source Status Propagation Facades] +``` + +## 7. 关键设计 + +### 7.1 Market 是来源授权 owner,不是目标事实 owner + +Market 只能输出: + +- 授权记录。 +- 安装记录。 +- 来源授权摘要。 +- 授权快照。 +- handoff token。 +- 治理影响和来源状态。 + +Market 不能直接输出: + +- AI agent slot binding。 +- Knowledge binding。 +- Content work/block/canonical 写入。 +- 目标 owner precheck ID。 + +### 7.2 写命令必须幂等且可审计 + +所有写命令至少包含: + +- `commandId` +- `operationId` +- `requestHash` +- `actorUserId` +- `ownerUserId` +- `targetType` +- `targetId` 或 `targetKey` +- `correlationId` +- `status` +- `resultSnapshot` + +相同 `commandId` + 相同 `requestHash` 返回同一业务结果;相同 `commandId` + 不同 `requestHash` 必须冲突。 + +### 7.3 授权、安装和 handoff 不可互相替代 + +`purchaseAsset`: + +- 校验资产可获取、许可可用、治理策略允许新获取。 +- 写 `muse_market_purchase` 和授权快照。 +- 投影给 Account。 +- 不安装、不绑定。 + +`installMarketplaceAsset`: + +- 消费有效授权。 +- 写 `muse_market_installation`。 +- 不写目标 owner。 +- Account 投影采用既有 member owner 契约:Market 实现只写 V11 已有 `muse_account_record_projection` 读模型,并提供 member 侧 `MarketAccountProjectionFacade` 的真实 provider;Market 侧追加表只允许表达投影同步/outbox 状态,不能另建第二套 Account 查询读模型。 + +`createBindPrecheck` / `createMarketplaceHandoff`: + +- 消费有效安装和来源状态。 +- 生成来源侧授权摘要、授权快照和 token。 +- 不创建目标 owner 的预检或绑定事实。 + +### 7.4 发布审核是硬门槛 + +`savePublishDraft` 只保存草稿。 + +`runPublishCheck` 只产出有有效期的检查结果。 + +`submitPublishRequest` 必须消费未过期且匹配草稿版本/材料 hash 的检查结果。 + +`adminApprovePublishRequest` 才能让资产版本进入上架可见状态。 + +### 7.5 治理动作必须有影响预览和传播边界 + +下架和召回前必须生成 `impactPreviewId`,并记录受影响: + +- 授权。 +- 安装。 +- handoff。 +- 目标 owner 绑定摘要。 +- 运行中任务。 +- Shadow 候选。 +- Knowledge Draft。 +- 导出限制。 + +传播给 AI、Knowledge、Content、Account 时只能通过 facade 或 projection;目标 owner 不可用时必须记录 `blocked`,不能伪造成已传播。需要重验时只能写入 action policy / recheck reasons,不能把 `needs_recheck` 写成 Market `SourceStatus`,因为合同内 `SourceStatus` 只表达来源可用性。 + +### 7.6 申诉只能处理治理事实 + +申诉处理读取申诉材料、资产、治理动作和发布申请,不读取用户私有副本正文。补充材料必须脱敏,禁止记录密钥或无关正文全文。 + +## 8. 验收标准 + +P1R-6 实现阶段完成后,必须满足: + +1. Market 32 个 operation 全部为 `dedicated / needs_verification`。 +2. AI 41 和 Knowledge 59 继续保持 `dedicated / completed`。 +3. Content 51、Meta 16、Account 33 继续保持 `dedicated / needs_verification`。 +4. Events 仍只有 P1R-7 `streamEvents` 为 `sse_placeholder / incomplete`。 +5. `completedOperations` 仍为 100,除非后续有真实端到端验收证据并获得单独批准。 +6. `docs/api-contracts/market/openapi.yaml` 无 diff。 +7. `muse-cloud/scripts/p1r-audit-api-coverage.py` 不为 Market 特判 `completed`。 +8. 若新增 V15,必须通过真实 PostgreSQL / Flyway `_test` 迁移验收。 + +## 9. Review gate 待检查项 + +Spec review 应重点检查: + +1. Market 是否没有越权写 AI / Knowledge / Content 目标事实。 +2. 购买、安装、handoff 是否分层清楚。 +3. 发布检查、审核和上架是否没有被压扁成一个开关。 +4. 下架/召回是否包含影响预览、传播和审计。 +5. 申诉是否没有读取私有副本。 + +Quality review 应重点检查: + +1. 是否能按小任务拆实现。 +2. 是否有足够的 gate test 和 focused tests。 +3. 是否避免大而全抽象。 +4. 是否保持 OpenAPI、coverage 和实现三者一致。 +5. 是否明确失败、重试、幂等和回滚边界。