diff --git a/docs/mvp/进度总账.md b/docs/mvp/进度总账.md index 7b5b3dc0..87fb8020 100644 --- a/docs/mvp/进度总账.md +++ b/docs/mvp/进度总账.md @@ -120,12 +120,12 @@ ### C. 前端 muse-studio(B3,体量最大,最长的长板) -- ✅ **2026-06-15:整套 studio e2e 单次全绿 8/8**(chromium,MSW off 直连单体 48080)——accept-suggestion 正+负 / knowledge-confirm / live-read ×4(content·market·account·ai) / workspace 壳;MSW-off 已固化进 `playwright.config.ts` webServer `env`(CI 自起 vite 生效)+ `.env.local`(本地);`workspace.spec.ts` 原 mock 假绿(断言「星海迷途」,MSW 关后必红)**已改写为不依赖 mock 的活体壳冒烟**,消除最后一处 MSW 互斥红。下列各 ✅ 切片即构成此 8/8。**(套件此后随切片增长:live-read ×4→×6 增「购买/授权」「安全事件」深页 + market-publish ×3→×7 增上架生命周期徽标 + 申诉提交·撤回·补充材料写路 + account-security-ack ×1 写路,`playwright test --list` 当前共 18 例;每轮按改动旅程实跑验证,非每次重跑全 18——accept-suggestion/knowledge-confirm/security-ack/market 上架徽标·申诉 依赖手工重种子。)** +- ✅ **2026-06-15:整套 studio e2e 单次全绿 8/8**(chromium,MSW off 直连单体 48080)——accept-suggestion 正+负 / knowledge-confirm / live-read ×4(content·market·account·ai) / workspace 壳;MSW-off 已固化进 `playwright.config.ts` webServer `env`(CI 自起 vite 生效)+ `.env.local`(本地);`workspace.spec.ts` 原 mock 假绿(断言「星海迷途」,MSW 关后必红)**已改写为不依赖 mock 的活体壳冒烟**,消除最后一处 MSW 互斥红。下列各 ✅ 切片即构成此 8/8。**(套件此后随切片增长:live-read ×4→×6 增「购买/授权」「安全事件」深页 + market-publish ×3→×8 增上架生命周期徽标 + 申诉提交·撤回·补充材料写路 + appealStatus 回显 + account-security-ack ×1 写路,`playwright test --list` 当前共 19 例;每轮按改动旅程实跑验证,非每次重跑全 19——accept-suggestion/knowledge-confirm/security-ack/market 上架徽标·申诉 依赖手工重种子。)** - 现状:其余旅程仍 ~14% 真连、dev 默认 MSW;但 MVP#1 已打通"关 MSW→直连活体→playwright e2e"模式(见下),可复用到后续旅程。 - ✅ **AI 候选采纳(rendered UI 活体证,2026-06-14)**:`main.tsx` 加 `VITE_API_MOCK=false` 开关关 MSW、前端直连活体单体;`accept-suggestion.spec.ts` 在 chromium 实跑——**happy-path**(生成经 page.route stub 发真实 suggestionId→点"采纳替换"→真合并)隔离跑绿 + DB 证(block rev1→2、正文=候选);**negative-path**(陈旧 revision)绿(乐观锁 1041000002)。连带**真实修复 client.ts 注入 tenant-id**(活体集成缺口,dev MSW 不校验故没暴露)。**遗留**:happy-path 受种子状态依赖(合并后 block rev 变 + RQ 缓存,需 fresh 种子复跑);`workspace.spec.ts` 已改活体壳冒烟(2026-06-15,MSW 互斥红消除、整套 8/8 全绿);suggestion 合并后留 pending=merge 不在同事务回写 AI 域状态(靠 outbox→AI 异步翻,本机 dispatcher 节流故 DB 仍 pending,读 `ContentSourceServiceImpl` 确认,非产品 bug)。 - ✅ **content/market/account/ai-agent 读旅程(rendered UI 活体证,2026-06-14)**:`live-read.spec.ts` 4/4 绿(MSW off,chromium:作品列表/市场浏览/个人中心/智能体列表均渲染真实后端数据)——证 tenant-id 修复后 studio 直连活体单体跨 BC 取数、真实数据形态与页面渲染契约对齐。 - 🔧 知识库工作台:**✅ 确认草稿(先审后入)rendered-UI 活体证(2026-06-14)**——`KnowledgeDraftPanel`(/knowledge/:workId)列待确认草稿+「确认入库」→ 真 confirm 物化 Canonical 实体(`knowledge-confirm.spec.ts` chromium MSW-off 绿,DB 证 draft confirmed+entity_id);后端补 `SummaryRespVO` 暴露 confirm 并发/源令牌。**仍 ⬜**:bindings/publish/graph UI;**遗留 bug**:confirm 对已存在同名实体抛 500(应干净 conflict,已 flag task_497da70f)。 -- 🔧 市场生产侧:**✅ 发布草稿 + 提交审核全链(创作者飞轮)rendered-UI 活体证(2026-06-15)**——`MarketPublish`(/market/publish):①「保存发布草稿」(`POST /marketplace/publish-drafts`)→`my-publish-records` 真实回读;②「提交审核」做 save→运行检查(`POST .../checks`)→检查通过提交申请(`POST /publish-requests`)全链(licenseType+权利声明为检查硬门槛)。`market-publish.spec.ts` chromium MSW-off **7/7 绿**(草稿正 / 缺标题负 / 提交全链 / 上架生命周期徽标 / 申诉提交·撤回·补充材料写路),DB 证:`muse_market_publish_draft`=draft、`muse_market_publish_request`=submitted、`muse_market_review_event`=submitted 落库;草稿测幂等自包含、全链测唯一名可重复跑。MarketBrowse 加「我要发布资产」入口。**附带修真实潜伏后端 bug**:publish draft/check/request/review_event 的 (tenant_id,command_id) 为**部分**唯一索引(V15 `WHERE command_id IS NOT NULL`),mapper insertIgnore 却发无谓词 `ON CONFLICT (tenant_id,command_id)`→PG 无法用部分索引作仲裁器→save/check/submit 500(链路从未活体跑过故潜伏);修=部分→完整唯一索引对齐 command/purchase 约定(`V22__fix_market_publish_command_unique_index.sql` 4 表 + muse_slice_live 已应用,零 Java 改动/零重启)。**✅ 上架状态可视化(读,2026-06-15)**:「上架=审核通过自动 `markListed`」(`AdminMarketReviewServiceImpl`,**非生产者动作**),生产者侧 `MarketPublish`「我的发布记录」新增**发布生命周期中文徽标**(草稿/已提交/审核中/已通过/**已上架**/**已驳回**/需补充…)+ nextAction/appealStatus 副文本,使创作者看到 publish→review→list 进度反馈(`market-publish.spec.ts` 第 4 例断言 listed→「已上架」、rejected→「已驳回」渲染;种子 /tmp/SetReqStatus.java 置 req#2→listed、#3→rejected)。**申诉(appeal):后端已验证 real+fail-closed(2026-06-15)**——curl:对自有已驳回资产 `POST /marketplace/appeals`(review_rejection)→`appealId/pending`;负路 他人资产→`1044000024 无权访问`、不存在资产→`1044000003 资产不存在`(fixture=asset(pub=1)+rejected request,见 /tmp/SeedAppeal.java)。**✅ 申诉(appeal)写路 UI——gap 已解(2026-06-15)**:根因=my-publish-records 的 assetId 是 publish-record id,**非** `submitAppeal.requireAsset` 所需的物化 `muse_market_asset.id`(资产仅 admin 审核通过 `markListed` 物化)。**后端最小契约改动**:`PublishRecordItem`/`MarketPublishRecordItemRespVO` 加 `marketAssetId`(=request.asset_id 命中真实 muse_market_asset 且归属当前发布者才给,否则 null=fail-closed 不放开入口),`MarketPublishServiceImpl.resolveMarketAssetId` 解析(已重建 jar + 重启单体 26483)。**前端**:仅 marketAssetId 非空 + 状态∈{rejected/compliance_blocked→review_rejection、delisted→delist、recalled→recall} 的记录放开「发起申诉」→ 面板填理由 → `useSubmitAppeal`(新 commandId 幂等)真打 `POST /marketplace/appeals`→`申诉已提交(pending)`(`market-publish.spec.ts` 第 5 例 chromium MSW-off 绿)。**DB 证(反假绿)**:`muse_market_appeal` 追加行 status=pending、commandId=前端 UUID(e2e 点击产 appealId=3);curl 正负路(自有已驳回→appealId/pending;他人→`无权访问`;不存在→`资产不存在`);后端 `MarketPublishServiceTest` 回归通过。种子 /tmp/SeedAppeal.java(asset(pub=1)+rejected request,asset_id=物化资产)。**✅ 申诉补充/撤回 UI(2026-06-15)**:同类 gap——生产者本无自己申诉的列表端点,故新增 app-api `GET /marketplace/appeals`(`appListMyAppeals`+`MuseMarketAppealMapper.selectListByUser`+`MarketMyAppealItemRespVO`,带 `canSupplement`/`canWithdraw` 派生)。`MarketPublish`「我的申诉」区列申诉 + 中文状态徽标(待处理/审核中/待补充材料/维持原判/已恢复/已关闭…);`canWithdraw`(非终态)放开「撤回」→`useWithdrawAppeal`→`POST .../withdraw`(expectedStatus 乐观锁)→closed;`canSupplement`(supplementing 态)放开「补充材料」→`useSupplementAppeal`→`POST .../supplements`(privacyConfirmed)→reviewing。`market-publish.spec.ts` 第 6/7 例 chromium MSW-off 绿(撤回→已撤回、补充→材料已补充),**DB 证(反假绿)**:withdraw→`muse_market_appeal` status=closed、supplement→`muse_market_appeal_material` 追加行(e2e 点击产 material#2);后端 `MarketAppealServiceTest` 15/15 + `AppMuseMarketAppealControllerTest` 6/6 回归通过。种子 /tmp/SetAppealSup.java(置 supplementing;提交后→reviewing 故每轮重置)。**仍 ⬜**:发布记录上 appealStatus 回显(my-publish-records 未 join 申诉表);**遗留已收口(2026-06-15)**:handoff_event/appeal_material/authorization_summary/appeal_event/appeal 5 表(其 mapper 确发无谓词 ON CONFLICT)经 **V23 系统性修**(部分→完整唯一索引)+ 新增 `P1rMarketCommandIndexFlywayMigrationIT` **真实 PG 验证**(clean→迁移 V1→V23→断言 V22/V23 的 9 个命令索引均完整、可作 ON CONFLICT 仲裁器,Tests run 1/0F;与 V15-pinned `P1rMarketFlywayMigrationIT` 互补)。其余 market 部分 command 索引(favorite/auth_snapshot/installation/asset/governance_*/handoff/source_status_event/account_projection)未被当 command-arbiter,保持不变。 +- 🔧 市场生产侧:**✅ 发布草稿 + 提交审核全链(创作者飞轮)rendered-UI 活体证(2026-06-15)**——`MarketPublish`(/market/publish):①「保存发布草稿」(`POST /marketplace/publish-drafts`)→`my-publish-records` 真实回读;②「提交审核」做 save→运行检查(`POST .../checks`)→检查通过提交申请(`POST /publish-requests`)全链(licenseType+权利声明为检查硬门槛)。`market-publish.spec.ts` chromium MSW-off **8/8 绿**(草稿正 / 缺标题负 / 提交全链 / 上架生命周期徽标 / 申诉提交·撤回·补充材料写路 / appealStatus 回显),DB 证:`muse_market_publish_draft`=draft、`muse_market_publish_request`=submitted、`muse_market_review_event`=submitted 落库;草稿测幂等自包含、全链测唯一名可重复跑。MarketBrowse 加「我要发布资产」入口。**附带修真实潜伏后端 bug**:publish draft/check/request/review_event 的 (tenant_id,command_id) 为**部分**唯一索引(V15 `WHERE command_id IS NOT NULL`),mapper insertIgnore 却发无谓词 `ON CONFLICT (tenant_id,command_id)`→PG 无法用部分索引作仲裁器→save/check/submit 500(链路从未活体跑过故潜伏);修=部分→完整唯一索引对齐 command/purchase 约定(`V22__fix_market_publish_command_unique_index.sql` 4 表 + muse_slice_live 已应用,零 Java 改动/零重启)。**✅ 上架状态可视化(读,2026-06-15)**:「上架=审核通过自动 `markListed`」(`AdminMarketReviewServiceImpl`,**非生产者动作**),生产者侧 `MarketPublish`「我的发布记录」新增**发布生命周期中文徽标**(草稿/已提交/审核中/已通过/**已上架**/**已驳回**/需补充…)+ nextAction/appealStatus 副文本,使创作者看到 publish→review→list 进度反馈(`market-publish.spec.ts` 第 4 例断言 listed→「已上架」、rejected→「已驳回」渲染;种子 /tmp/SetReqStatus.java 置 req#2→listed、#3→rejected)。**申诉(appeal):后端已验证 real+fail-closed(2026-06-15)**——curl:对自有已驳回资产 `POST /marketplace/appeals`(review_rejection)→`appealId/pending`;负路 他人资产→`1044000024 无权访问`、不存在资产→`1044000003 资产不存在`(fixture=asset(pub=1)+rejected request,见 /tmp/SeedAppeal.java)。**✅ 申诉(appeal)写路 UI——gap 已解(2026-06-15)**:根因=my-publish-records 的 assetId 是 publish-record id,**非** `submitAppeal.requireAsset` 所需的物化 `muse_market_asset.id`(资产仅 admin 审核通过 `markListed` 物化)。**后端最小契约改动**:`PublishRecordItem`/`MarketPublishRecordItemRespVO` 加 `marketAssetId`(=request.asset_id 命中真实 muse_market_asset 且归属当前发布者才给,否则 null=fail-closed 不放开入口),`MarketPublishServiceImpl.resolveMarketAssetId` 解析(已重建 jar + 重启单体 26483)。**前端**:仅 marketAssetId 非空 + 状态∈{rejected/compliance_blocked→review_rejection、delisted→delist、recalled→recall} 的记录放开「发起申诉」→ 面板填理由 → `useSubmitAppeal`(新 commandId 幂等)真打 `POST /marketplace/appeals`→`申诉已提交(pending)`(`market-publish.spec.ts` 第 5 例 chromium MSW-off 绿)。**DB 证(反假绿)**:`muse_market_appeal` 追加行 status=pending、commandId=前端 UUID(e2e 点击产 appealId=3);curl 正负路(自有已驳回→appealId/pending;他人→`无权访问`;不存在→`资产不存在`);后端 `MarketPublishServiceTest` 回归通过。种子 /tmp/SeedAppeal.java(asset(pub=1)+rejected request,asset_id=物化资产)。**✅ 申诉补充/撤回 UI(2026-06-15)**:同类 gap——生产者本无自己申诉的列表端点,故新增 app-api `GET /marketplace/appeals`(`appListMyAppeals`+`MuseMarketAppealMapper.selectListByUser`+`MarketMyAppealItemRespVO`,带 `canSupplement`/`canWithdraw` 派生)。`MarketPublish`「我的申诉」区列申诉 + 中文状态徽标(待处理/审核中/待补充材料/维持原判/已恢复/已关闭…);`canWithdraw`(非终态)放开「撤回」→`useWithdrawAppeal`→`POST .../withdraw`(expectedStatus 乐观锁)→closed;`canSupplement`(supplementing 态)放开「补充材料」→`useSupplementAppeal`→`POST .../supplements`(privacyConfirmed)→reviewing。`market-publish.spec.ts` 第 6/7 例 chromium MSW-off 绿(撤回→已撤回、补充→材料已补充),**DB 证(反假绿)**:withdraw→`muse_market_appeal` status=closed、supplement→`muse_market_appeal_material` 追加行(e2e 点击产 material#2);后端 `MarketAppealServiceTest` 15/15 + `AppMuseMarketAppealControllerTest` 6/6 回归通过。种子 /tmp/SetAppealSup.java(置 supplementing;提交后→reviewing 故每轮重置)。**✅ appealStatus 记录回显(2026-06-15)**:`my-publish-records` 经 `MuseMarketAppealMapper.selectLatestByAssetIdAndUser` + `resolveAppealStatus` 回填该物化资产被当前发布者发起的最新申诉态到记录 `appealStatus`(无物化资产/无申诉则 null,marketAssetId 与 appealStatus 两路共用各只查一次);`MarketPublish` 发布记录副文本以中文徽标渲染「申诉:<待处理/审核中/已关闭…>」。`market-publish.spec.ts` 第 8 例 chromium 绿(recId=5/marketAssetId=2 回显 appealStatus=closed→「申诉:已关闭」);后端 `MarketPublishServiceTest` 14/14 回归通过。**至此市场生产者飞轮端到端完整**:发布(草稿→检查→提交)→上架状态可视化→申诉(提交/补充材料/撤回)全生命周期 + 申诉态回显。**遗留已收口(2026-06-15)**:handoff_event/appeal_material/authorization_summary/appeal_event/appeal 5 表(其 mapper 确发无谓词 ON CONFLICT)经 **V23 系统性修**(部分→完整唯一索引)+ 新增 `P1rMarketCommandIndexFlywayMigrationIT` **真实 PG 验证**(clean→迁移 V1→V23→断言 V22/V23 的 9 个命令索引均完整、可作 ON CONFLICT 仲裁器,Tests run 1/0F;与 V15-pinned `P1rMarketFlywayMigrationIT` 互补)。其余 market 部分 command 索引(favorite/auth_snapshot/installation/asset/governance_*/handoff/source_status_event/account_projection)未被当 command-arbiter,保持不变。 - 🔧 个人中心:**✅ profile/用量/权益 + 我的购买/我的授权 + 安全事件(读+确认写路)(rendered-UI 活体证,2026-06-15)**——PersonalCenter 新增「我的购买/我的授权」渲染 market→account 投影真实记录(`useAccountPurchases`/`useAccountLicenses`→`/account/{purchases,licenses}`;`live-read.spec.ts` 第 5 例 chromium MSW-off 绿,断言真实资产「活体市场资产·测试」渲染——证后端 account 读端在单体可用即被 UI 消费,见 §五A 活体订正);**新增「安全事件」区**渲染 `GET /account/security-events` 真实摘要(`useAccountSecurityEvents`→`AccountPageResult`;eventType/severity 徽标 + 已确认/待确认状态;`live-read.spec.ts` 第 6 例 chromium MSW-off 绿,断言种子事件「活体安全事件·异地登录提醒」渲染)。**确认写路**:未确认事件展示「确认」按钮→`useAcknowledgeSecurityEvent`(每次发新 commandId,后端按 commandId 幂等)真打 `POST /account/security-events/{id}/acknowledge`(action=acknowledged)→列表失活重取翻「已确认」、按钮消失(构成 acked→无动作 UI 不变量;`account-security-ack.spec.ts` chromium MSW-off 绿,种子事件 B「待确认演练」)。后端**正负路 curl 实证**(反假绿):正路 acknowledge 真实事件→`code:0`+列表 acknowledged 翻 true;负路 acknowledge 不存在 eventId→`404 安全事件不存在`、非法 action→`400 参数校验`,均 **fail-closed 非许可桩**。写入 **DB 双证**:`muse_member_security_event.acknowledged` 翻 true + `muse_account_security_event_ack` 追加 1 行(action=acknowledged、commandId 为前端 UUID=权威处理历史,非覆盖)。种子幂等可重置(/tmp/SeedSec.java:事件A读锚点幂等插入 + 事件B写锚点每轮重置为未确认)。本轮实跑:account-security-ack **1/1** + live-read **6/6** + vitest 47/47 + tsc 干净。**仍 ⬜**:exports / downloads / new-api-binding 深页(后端均为**正确 fail-closed 外部依赖**:对象存储 / New-API 未配置,非接线缺口,见 §五A)。 - 收口口径:每切片"关 MSW→真连活体单体→playwright e2e 绿"才算完成(反假绿)。 diff --git a/muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/application/muse/MarketPublishServiceImpl.java b/muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/application/muse/MarketPublishServiceImpl.java index 7c0ff3ee..a526f234 100644 --- a/muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/application/muse/MarketPublishServiceImpl.java +++ b/muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/application/muse/MarketPublishServiceImpl.java @@ -5,12 +5,14 @@ import cn.iocoder.muse.framework.common.pojo.PageResult; import cn.iocoder.muse.framework.common.util.json.JsonUtils; import cn.iocoder.muse.framework.tenant.core.context.TenantContextHolder; import cn.iocoder.muse.module.market.dal.dataobject.muse.MuseMarketAccountProjectionDO; +import cn.iocoder.muse.module.market.dal.dataobject.muse.MuseMarketAppealDO; import cn.iocoder.muse.module.market.dal.dataobject.muse.MuseMarketAssetDO; import cn.iocoder.muse.module.market.dal.dataobject.muse.MuseMarketCommandDO; import cn.iocoder.muse.module.market.dal.dataobject.muse.MuseMarketPublishCheckDO; import cn.iocoder.muse.module.market.dal.dataobject.muse.MuseMarketPublishDraftDO; import cn.iocoder.muse.module.market.dal.dataobject.muse.MuseMarketPublishRequestDO; import cn.iocoder.muse.module.market.dal.dataobject.muse.MuseMarketReviewEventDO; +import cn.iocoder.muse.module.market.dal.mysql.muse.MuseMarketAppealMapper; import cn.iocoder.muse.module.market.dal.mysql.muse.MuseMarketAssetMapper; import cn.iocoder.muse.module.market.dal.mysql.muse.MuseMarketPublishCheckMapper; import cn.iocoder.muse.module.market.dal.mysql.muse.MuseMarketPublishDraftMapper; @@ -70,6 +72,7 @@ public class MarketPublishServiceImpl implements MarketPublishService { private final MuseMarketReviewEventMapper reviewEventMapper; @SuppressWarnings("unused") private final MuseMarketAssetMapper assetMapper; + private final MuseMarketAppealMapper appealMapper; private final MarketCommandService commandService; private final MarketAccountProjectionProvider accountProjectionProvider; @@ -565,13 +568,16 @@ public class MarketPublishServiceImpl implements MarketPublishService { MuseMarketPublishRequestDO request) { MuseMarketPublishDraftDO draft = request.getAssetId() == null ? null : publishDraftMapper.selectOwnedById(tenantId, request.getAssetId(), loginUserId); + // marketAssetId(物化资产 id)与 appealStatus(该资产最新申诉态)只依赖 request,两路共用、各只查一次。 + String marketAssetId = resolveMarketAssetId(tenantId, loginUserId, request.getAssetId()); + String appealStatus = resolveAppealStatus(tenantId, loginUserId, marketAssetId); if (draft == null) { log.warn("[listMyPublishRecords][发布申请缺少可回读草稿,tenantId={}, userId={}, requestId={}, assetId={}]", tenantId, loginUserId, request.getId(), request.getAssetId()); return assetType == null ? new PublishRecordItem(String.valueOf(request.getId()), String.valueOf(request.getAssetId()), null, String.valueOf(request.getDraftVersion()), - request.getStatus(), nextAction(request.getStatus()), null, null, "publisher_only", - resolveMarketAssetId(tenantId, loginUserId, request.getAssetId())) : null; + request.getStatus(), nextAction(request.getStatus()), null, appealStatus, "publisher_only", + marketAssetId) : null; } if (StringUtils.hasText(assetType) && !assetType.equals(draft.getAssetType())) { return null; @@ -585,9 +591,9 @@ public class MarketPublishServiceImpl implements MarketPublishService { request.getStatus(), nextAction(request.getStatus()), null, - null, + appealStatus, "publisher_only", - resolveMarketAssetId(tenantId, loginUserId, request.getAssetId())); + marketAssetId); } /** @@ -607,6 +613,19 @@ public class MarketPublishServiceImpl implements MarketPublishService { return String.valueOf(asset.getId()); } + /** + * 回显该物化资产被当前发布者发起的最新申诉状态(pending/reviewing/supplementing/maintained/restored/closed…), + * 无物化资产或无申诉则 null。WHY:让「我的发布记录」直接呈现申诉进展,无需另开「我的申诉」对照。 + */ + private String resolveAppealStatus(Long tenantId, Long loginUserId, String marketAssetId) { + if (marketAssetId == null) { + return null; + } + MuseMarketAppealDO appeal = appealMapper.selectLatestByAssetIdAndUser(tenantId, + Long.valueOf(marketAssetId), loginUserId); + return appeal == null ? null : appeal.getStatus(); + } + private String nextAction(String status) { if (STATUS_DRAFT.equals(status)) { return "run_check"; diff --git a/muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/dal/mysql/muse/MuseMarketAppealMapper.java b/muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/dal/mysql/muse/MuseMarketAppealMapper.java index 2710cb67..e526f14e 100644 --- a/muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/dal/mysql/muse/MuseMarketAppealMapper.java +++ b/muse-cloud/muse-module-market/muse-module-market-server/src/main/java/cn/iocoder/muse/module/market/dal/mysql/muse/MuseMarketAppealMapper.java @@ -45,6 +45,16 @@ public interface MuseMarketAppealMapper extends BaseMapperX .orderByDesc(MuseMarketAppealDO::getId)); } + /** 查某资产被当前发布者发起的最新一条申诉,供「我的发布记录」回显 appealStatus。 */ + default MuseMarketAppealDO selectLatestByAssetIdAndUser(Long tenantId, Long assetId, Long userId) { + return selectOne(new LambdaQueryWrapperX() + .eq(MuseMarketAppealDO::getTenantId, tenantId) + .eq(MuseMarketAppealDO::getAssetId, assetId) + .eq(MuseMarketAppealDO::getUserId, userId) + .orderByDesc(MuseMarketAppealDO::getId) + .last("LIMIT 1")); + } + default PageResult selectAdminPage(Long tenantId, String appealType, String status, Long appellantId, Long assetId, Integer pageNo, Integer pageSize) { diff --git a/muse-studio/e2e/market-publish.spec.ts b/muse-studio/e2e/market-publish.spec.ts index f29560bb..8b567e96 100644 --- a/muse-studio/e2e/market-publish.spec.ts +++ b/muse-studio/e2e/market-publish.spec.ts @@ -168,4 +168,14 @@ test.describe('市场生产侧发布草稿(真实后端, MSW off)', () => { expect((await resp.json()).code).toBe(0); await expect(page.getByText(/材料已补充/)).toBeVisible({ timeout: 15_000 }); }); + + // appealStatus 记录回显:有申诉的物化资产记录在「我的发布记录」副文本显示「申诉:<中文状态>」(后端 my-publish-records join 申诉表)。 + // 前置:asset_id=2 的记录(marketAssetId 非空)已有申诉(种子 + 申诉提交测产生)。 + test('我的发布记录回显 appealStatus(申诉:中文状态)', async ({ page }) => { + await seedToken(page); + await page.goto('/market/publish'); + await expect( + page.getByText(/申诉:(待处理|审核中|待补充材料|已关闭|维持原判|已恢复|部分恢复)/).first() + ).toBeVisible({ timeout: 15_000 }); + }); }); diff --git a/muse-studio/src/features/market/components/MarketPublish.tsx b/muse-studio/src/features/market/components/MarketPublish.tsx index fa803829..7b3927eb 100644 --- a/muse-studio/src/features/market/components/MarketPublish.tsx +++ b/muse-studio/src/features/market/components/MarketPublish.tsx @@ -314,7 +314,7 @@ export default function MarketPublish() {
v{rec.version} {rec.nextAction ? ` · 下一步:${NEXT_ACTION_LABEL[rec.nextAction] ?? rec.nextAction}` : ''} - {rec.appealStatus ? ` · 申诉:${rec.appealStatus}` : ''} + {rec.appealStatus ? ` · 申诉:${appealStatusMeta(rec.appealStatus).label}` : ''}