From 32ada4c2bf20f3585dca0e15ee46874f8b4921ab Mon Sep 17 00:00:00 2001 From: jianghaifeng Date: Thu, 5 Nov 2026 10:55:52 +0800 Subject: [PATCH] =?UTF-8?q?feat(content):=20=E7=9F=AD=E5=89=A7=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=AE=A1=E6=A0=B8=E9=80=9A=E8=BF=87=E5=B9=B6=E4=B8=8A?= =?UTF-8?q?=E7=BA=BF=E4=B8=80=E6=AD=A5=E6=B5=81=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 APPROVE_PUBLISH 动作与 approve-publish 接口,草稿/审核中/已下线 可直接流转为已上线,事务内同步上线草稿分集并记录一条审核记录, 后台审核操作由三步减为一步。 --- .../admin/controller/DramaController.java | 9 ++++ .../content/service/DramaService.java | 6 +++ .../content/service/DramaServiceTest.java | 42 +++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/fruitshorts-admin-api/src/main/java/com/fruitshorts/admin/controller/DramaController.java b/fruitshorts-admin-api/src/main/java/com/fruitshorts/admin/controller/DramaController.java index ce24fd3..2d6e216 100644 --- a/fruitshorts-admin-api/src/main/java/com/fruitshorts/admin/controller/DramaController.java +++ b/fruitshorts-admin-api/src/main/java/com/fruitshorts/admin/controller/DramaController.java @@ -113,6 +113,15 @@ public class DramaController { return Result.ok(); } + @Operation(summary = "审核通过并上线") + @PostMapping("/{id}/approve-publish") + @PreAuthorize("@ss.hasPermission('drama:review')") + @OperationLog(module = "内容管理", action = "审核通过并上线") + public Result approvePublish(@PathVariable Long id, @RequestBody(required = false) DramaStatusReqVO req) { + changeStatus(id, "APPROVE_PUBLISH", req); + return Result.ok(); + } + @Operation(summary = "上线") @PostMapping("/{id}/publish") @PreAuthorize("@ss.hasPermission('drama:review')") diff --git a/fruitshorts-module-content/src/main/java/com/fruitshorts/content/service/DramaService.java b/fruitshorts-module-content/src/main/java/com/fruitshorts/content/service/DramaService.java index f7f3c6b..a43aaf4 100644 --- a/fruitshorts-module-content/src/main/java/com/fruitshorts/content/service/DramaService.java +++ b/fruitshorts-module-content/src/main/java/com/fruitshorts/content/service/DramaService.java @@ -162,6 +162,12 @@ public class DramaService { requireState(current, "APPROVE", Set.of(ContentStatus.REVIEWING.getCode())); yield ContentStatus.APPROVED.getCode(); } + case "APPROVE_PUBLISH" -> { + // 一步审核通过并上线,减少后台操作步骤 + requireState(current, "APPROVE_PUBLISH", Set.of(ContentStatus.DRAFT.getCode(), + ContentStatus.REVIEWING.getCode(), ContentStatus.OFFLINE.getCode())); + yield ContentStatus.PUBLISHED.getCode(); + } case "REJECT" -> { requireState(current, "REJECT", Set.of(ContentStatus.REVIEWING.getCode())); yield ContentStatus.DRAFT.getCode(); diff --git a/fruitshorts-module-content/src/test/java/com/fruitshorts/content/service/DramaServiceTest.java b/fruitshorts-module-content/src/test/java/com/fruitshorts/content/service/DramaServiceTest.java index 5188df6..96382f3 100644 --- a/fruitshorts-module-content/src/test/java/com/fruitshorts/content/service/DramaServiceTest.java +++ b/fruitshorts-module-content/src/test/java/com/fruitshorts/content/service/DramaServiceTest.java @@ -4,11 +4,13 @@ package com.fruitshorts.content.service; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; +import com.fruitshorts.common.exception.ServiceException; import com.fruitshorts.content.dal.entity.Drama; import com.fruitshorts.content.dal.entity.DramaEpisode; import com.fruitshorts.content.dal.mapper.DramaEpisodeMapper; import com.fruitshorts.content.dal.mapper.DramaMapper; import com.fruitshorts.content.dal.mapper.DramaReviewRecordMapper; +import com.fruitshorts.content.enums.ContentStatus; import com.fruitshorts.content.vo.DramaSaveReqVO; import org.apache.ibatis.builder.MapperBuilderAssistant; import org.junit.jupiter.api.BeforeAll; @@ -25,6 +27,7 @@ import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @@ -159,6 +162,45 @@ class DramaServiceTest { verify(dramaEpisodeMapper, never()).updateById(any(DramaEpisode.class)); } + @Test + void should_publish_directly_from_draft_when_approve_publish() { + Drama drama = givenDramaWithStatus(ContentStatus.DRAFT.getCode()); + + dramaService.changeStatus(DRAMA_ID, "APPROVE_PUBLISH", "", 1L, "admin"); + + assertThat(drama.getStatus()).isEqualTo(ContentStatus.PUBLISHED.getCode()); + assertThat(drama.getPublishedAt()).isNotNull(); + verify(dramaMapper).updateById(drama); + // 上线时同步把草稿分集发布 + verify(dramaEpisodeMapper).update(any(), any()); + } + + @Test + void should_publish_directly_from_reviewing_when_approve_publish() { + Drama drama = givenDramaWithStatus(ContentStatus.REVIEWING.getCode()); + + dramaService.changeStatus(DRAMA_ID, "APPROVE_PUBLISH", "", 1L, "admin"); + + assertThat(drama.getStatus()).isEqualTo(ContentStatus.PUBLISHED.getCode()); + } + + @Test + void should_reject_approve_publish_when_already_published() { + givenDramaWithStatus(ContentStatus.PUBLISHED.getCode()); + + assertThatThrownBy(() -> dramaService.changeStatus(DRAMA_ID, "APPROVE_PUBLISH", "", 1L, "admin")) + .isInstanceOf(ServiceException.class); + } + + private Drama givenDramaWithStatus(String status) { + Drama drama = new Drama(); + drama.setId(DRAMA_ID); + drama.setStatus(status); + given(dramaMapper.selectById(DRAMA_ID)).willReturn(drama); + given(dramaMapper.updateById(any(Drama.class))).willReturn(1); + return drama; + } + private void givenExistingDrama(int freeEpisodes, int unlockCoin) { Drama drama = new Drama(); drama.setId(DRAMA_ID);