feat(media): refine clerk album review and tests
This commit is contained in:
@@ -6,12 +6,16 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.starry.admin.common.aspect.ClerkUserLogin;
|
||||
import com.starry.admin.common.conf.ThreadLocalRequestDetail;
|
||||
import com.starry.admin.common.exception.CustomException;
|
||||
import com.starry.admin.modules.clerk.enums.ClerkMediaReviewState;
|
||||
import com.starry.admin.modules.clerk.enums.ClerkMediaUsage;
|
||||
import com.starry.admin.modules.clerk.module.entity.*;
|
||||
import com.starry.admin.modules.clerk.module.vo.PlayClerkCommodityEditVo;
|
||||
import com.starry.admin.modules.clerk.module.vo.PlayClerkCommodityQueryVo;
|
||||
import com.starry.admin.modules.clerk.service.*;
|
||||
import com.starry.admin.modules.clerk.service.impl.PlayClerkUserInfoServiceImpl;
|
||||
import com.starry.admin.modules.clerk.service.impl.PlayClerkUserReviewInfoServiceImpl;
|
||||
import com.starry.admin.modules.media.enums.MediaOwnerType;
|
||||
import com.starry.admin.modules.media.service.IPlayMediaService;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OperatorType;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||
import com.starry.admin.modules.order.module.vo.PlayOrderCompleteVo;
|
||||
@@ -54,6 +58,7 @@ import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -121,6 +126,10 @@ public class WxClerkController {
|
||||
private SmsUtils smsUtils;
|
||||
@Resource
|
||||
private WxCustomMpService wxCustomMpService;
|
||||
@Resource
|
||||
private com.starry.admin.modules.clerk.service.IPlayClerkMediaAssetService clerkMediaAssetService;
|
||||
@Resource
|
||||
private IPlayMediaService mediaService;
|
||||
|
||||
/**
|
||||
* 店员获取个人业绩信息
|
||||
@@ -313,18 +322,77 @@ public class WxClerkController {
|
||||
@PostMapping("/user/updateAlbum")
|
||||
public R updateAlbum(@Validated @RequestBody PlayClerkUserAlbumVo vo) {
|
||||
PlayClerkUserInfoEntity userInfo = ThreadLocalRequestDetail.getClerkUserInfo();
|
||||
// PlayClerkDataReviewInfoEntity entity =
|
||||
// playClerkDataReviewInfoService.queryByClerkId(userInfo.getId(), "2", "0");
|
||||
// if (entity != null) {
|
||||
// throw new CustomException("已有申请未审核");
|
||||
// }
|
||||
PlayClerkDataReviewInfoEntity entity = new PlayClerkDataReviewInfoEntity();
|
||||
entity.setClerkId(userInfo.getId());
|
||||
entity.setDataType("2");
|
||||
entity.setReviewState("0");
|
||||
entity.setDataContent(vo.getAlbum());
|
||||
playClerkDataReviewInfoService.create(entity);
|
||||
return R.ok("提交成功,等待审核~");
|
||||
List<String> requested = vo.getAlbum() == null ? new ArrayList<>() : vo.getAlbum().stream()
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.map(String::trim)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 查询当前所有已审核通过的 PROFILE 媒资
|
||||
List<PlayClerkMediaAssetEntity> approvedAssets = clerkMediaAssetService.listByState(
|
||||
userInfo.getId(),
|
||||
ClerkMediaUsage.PROFILE,
|
||||
Collections.singletonList(ClerkMediaReviewState.APPROVED));
|
||||
|
||||
LinkedHashSet<String> requestedSet = new LinkedHashSet<>(requested);
|
||||
if (requestedSet.isEmpty()) {
|
||||
throw new CustomException("最少上传一张照片");
|
||||
}
|
||||
|
||||
// 计算哪些是新媒资(需走审核),哪些是纯删除/排序
|
||||
java.util.Set<String> approvedIds = approvedAssets.stream()
|
||||
.map(PlayClerkMediaAssetEntity::getMediaId)
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.collect(java.util.stream.Collectors.toSet());
|
||||
java.util.Set<String> newMediaIds = requestedSet.stream()
|
||||
.filter(id -> !approvedIds.contains(id))
|
||||
.collect(java.util.stream.Collectors.toSet());
|
||||
|
||||
if (!newMediaIds.isEmpty()) {
|
||||
// 新增媒资必须是当前店员本人名下、已就绪的媒资,才能进入审核流程
|
||||
java.util.List<com.starry.admin.modules.media.entity.PlayMediaEntity> newMediaEntities =
|
||||
mediaService.lambdaQuery()
|
||||
.in(com.starry.admin.modules.media.entity.PlayMediaEntity::getId, newMediaIds)
|
||||
.list();
|
||||
if (newMediaEntities.size() != newMediaIds.size()) {
|
||||
throw new CustomException("存在未完成上传的照片/视频,请稍后重试");
|
||||
}
|
||||
for (com.starry.admin.modules.media.entity.PlayMediaEntity media : newMediaEntities) {
|
||||
if (!userInfo.getTenantId().equals(media.getTenantId())
|
||||
|| media.getOwnerType() != MediaOwnerType.CLERK
|
||||
|| !userInfo.getId().equals(media.getOwnerId())) {
|
||||
throw new CustomException("存在无效的照片/视频,请刷新后重试");
|
||||
}
|
||||
if (!com.starry.admin.modules.media.enums.MediaStatus.READY.getCode().equals(media.getStatus())) {
|
||||
throw new CustomException("存在未完成上传的照片/视频,请稍后重试");
|
||||
}
|
||||
}
|
||||
|
||||
// 只要存在新增媒资,则按原有逻辑走资料审核,由审核通过时统一生效
|
||||
PlayClerkDataReviewInfoEntity entity = new PlayClerkDataReviewInfoEntity();
|
||||
entity.setClerkId(userInfo.getId());
|
||||
entity.setDataType("2");
|
||||
entity.setReviewState("0");
|
||||
entity.setDataContent(new ArrayList<>(requestedSet));
|
||||
playClerkDataReviewInfoService.create(entity);
|
||||
return R.ok("提交成功,等待审核~");
|
||||
}
|
||||
|
||||
// 仅删除/排序:直接应用变更,不再生成审核记录
|
||||
// 先根据新的顺序更新 orderIndex
|
||||
clerkMediaAssetService.reorder(userInfo.getId(), ClerkMediaUsage.PROFILE, new ArrayList<>(requestedSet));
|
||||
|
||||
// 再对不再保留的媒资执行软删除
|
||||
java.util.Set<String> requestedOnly = new java.util.HashSet<>(requestedSet);
|
||||
java.util.Set<String> deletedMediaIds = approvedIds.stream()
|
||||
.filter(id -> !requestedOnly.contains(id))
|
||||
.collect(java.util.stream.Collectors.toSet());
|
||||
for (String mediaId : deletedMediaIds) {
|
||||
clerkMediaAssetService.softDelete(userInfo.getId(), mediaId);
|
||||
mediaService.softDelete(MediaOwnerType.CLERK, userInfo.getId(), mediaId);
|
||||
}
|
||||
|
||||
return R.ok("修改成功");
|
||||
}
|
||||
|
||||
@ClerkUserLogin
|
||||
|
||||
@@ -0,0 +1,271 @@
|
||||
package com.starry.admin.api;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.starry.admin.common.apitest.ApiTestDataSeeder;
|
||||
import com.starry.admin.modules.clerk.enums.ClerkMediaReviewState;
|
||||
import com.starry.admin.modules.clerk.enums.ClerkMediaUsage;
|
||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkMediaAssetEntity;
|
||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
|
||||
import com.starry.admin.modules.clerk.service.IPlayClerkDataReviewInfoService;
|
||||
import com.starry.admin.modules.clerk.service.IPlayClerkMediaAssetService;
|
||||
import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService;
|
||||
import com.starry.admin.modules.media.entity.PlayMediaEntity;
|
||||
import com.starry.admin.modules.media.enums.MediaOwnerType;
|
||||
import com.starry.admin.modules.media.enums.MediaStatus;
|
||||
import com.starry.admin.modules.media.service.IPlayMediaService;
|
||||
import com.starry.admin.modules.weichat.entity.clerk.MediaVo;
|
||||
import com.starry.admin.modules.weichat.entity.clerk.PlayClerkUserInfoResultVo;
|
||||
import com.starry.admin.modules.weichat.service.WxTokenService;
|
||||
import com.starry.admin.utils.SecurityUtils;
|
||||
import com.starry.common.constant.Constants;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
|
||||
/**
|
||||
* 专门验证 /wx/clerk/user/updateAlbum 在“只有删除/排序”时不会创建新的审核记录,
|
||||
* 并且会立即更新顾客端视图。
|
||||
*/
|
||||
class WxClerkAlbumUpdateApiTest extends AbstractApiTest {
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@Autowired
|
||||
private WxTokenService wxTokenService;
|
||||
|
||||
@Autowired
|
||||
private IPlayClerkUserInfoService clerkUserInfoService;
|
||||
|
||||
@Autowired
|
||||
private IPlayClerkDataReviewInfoService dataReviewInfoService;
|
||||
|
||||
@Autowired
|
||||
private IPlayClerkMediaAssetService mediaAssetService;
|
||||
|
||||
@Autowired
|
||||
private IPlayMediaService mediaService;
|
||||
|
||||
@Test
|
||||
void reorderExistingApprovedMediaDoesNotCreateNewReviewAndUpdatesOrder() throws Exception {
|
||||
ensureTenantContext();
|
||||
String clerkId = ApiTestDataSeeder.DEFAULT_CLERK_ID;
|
||||
String clerkToken = wxTokenService.createWxUserToken(clerkId);
|
||||
clerkUserInfoService.updateTokenById(clerkId, clerkToken);
|
||||
|
||||
PlayMediaEntity media1 = seedMedia(clerkId);
|
||||
PlayMediaEntity media2 = seedMedia(clerkId);
|
||||
PlayMediaEntity media3 = seedMedia(clerkId);
|
||||
|
||||
seedApprovedAsset(clerkId, media1.getId(), 0);
|
||||
seedApprovedAsset(clerkId, media2.getId(), 1);
|
||||
seedApprovedAsset(clerkId, media3.getId(), 2);
|
||||
|
||||
long reviewCountBefore = dataReviewInfoService.lambdaQuery()
|
||||
.eq(com.starry.admin.modules.clerk.module.entity.PlayClerkDataReviewInfoEntity::getClerkId, clerkId)
|
||||
.eq(com.starry.admin.modules.clerk.module.entity.PlayClerkDataReviewInfoEntity::getDataType, "2")
|
||||
.count();
|
||||
|
||||
// 仅调整顺序:album 只包含已审核通过的媒资 id,不引入任何新媒资
|
||||
List<String> reordered = List.of(media3.getId(), media1.getId(), media2.getId());
|
||||
ObjectNode payload = objectMapper.createObjectNode();
|
||||
com.fasterxml.jackson.databind.node.ArrayNode albumArray = payload.putArray("album");
|
||||
reordered.forEach(albumArray::add);
|
||||
|
||||
mockMvc.perform(post("/wx/clerk/user/updateAlbum")
|
||||
.header(USER_HEADER, DEFAULT_USER)
|
||||
.header(TENANT_HEADER, DEFAULT_TENANT)
|
||||
.header(Constants.CLERK_USER_LOGIN_TOKEN, Constants.TOKEN_PREFIX + clerkToken)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(payload.toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200));
|
||||
|
||||
ensureTenantContext();
|
||||
long reviewCountAfter = dataReviewInfoService.lambdaQuery()
|
||||
.eq(com.starry.admin.modules.clerk.module.entity.PlayClerkDataReviewInfoEntity::getClerkId, clerkId)
|
||||
.eq(com.starry.admin.modules.clerk.module.entity.PlayClerkDataReviewInfoEntity::getDataType, "2")
|
||||
.count();
|
||||
assertThat(reviewCountAfter)
|
||||
.as("reordering without introducing new media should not create review records")
|
||||
.isEqualTo(reviewCountBefore);
|
||||
|
||||
List<PlayClerkMediaAssetEntity> assets = mediaAssetService
|
||||
.listByState(clerkId, ClerkMediaUsage.PROFILE,
|
||||
Collections.singletonList(ClerkMediaReviewState.APPROVED));
|
||||
PlayClerkMediaAssetEntity asset1 = assets.stream()
|
||||
.filter(a -> media1.getId().equals(a.getMediaId()))
|
||||
.max(java.util.Comparator.comparing(PlayClerkMediaAssetEntity::getCreatedTime))
|
||||
.orElse(null);
|
||||
PlayClerkMediaAssetEntity asset2 = assets.stream()
|
||||
.filter(a -> media2.getId().equals(a.getMediaId()))
|
||||
.max(java.util.Comparator.comparing(PlayClerkMediaAssetEntity::getCreatedTime))
|
||||
.orElse(null);
|
||||
PlayClerkMediaAssetEntity asset3 = assets.stream()
|
||||
.filter(a -> media3.getId().equals(a.getMediaId()))
|
||||
.max(java.util.Comparator.comparing(PlayClerkMediaAssetEntity::getCreatedTime))
|
||||
.orElse(null);
|
||||
|
||||
assertThat(asset3).as("asset for media3 should exist").isNotNull();
|
||||
assertThat(asset1).as("asset for media1 should exist").isNotNull();
|
||||
assertThat(asset2).as("asset for media2 should exist").isNotNull();
|
||||
|
||||
assertThat(asset3.getOrderIndex()).isEqualTo(0);
|
||||
assertThat(asset1.getOrderIndex()).isEqualTo(1);
|
||||
assertThat(asset2.getOrderIndex()).isEqualTo(2);
|
||||
|
||||
PlayClerkUserInfoResultVo detail = clerkUserInfoService.buildCustomerDetail(clerkId, "");
|
||||
List<String> customerMediaIds = detail.getMediaList().stream()
|
||||
.map(MediaVo::getMediaId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
int index3 = customerMediaIds.indexOf(media3.getId());
|
||||
int index1 = customerMediaIds.indexOf(media1.getId());
|
||||
int index2 = customerMediaIds.indexOf(media2.getId());
|
||||
|
||||
assertThat(index3).isGreaterThanOrEqualTo(0);
|
||||
assertThat(index1).isGreaterThanOrEqualTo(0);
|
||||
assertThat(index2).isGreaterThanOrEqualTo(0);
|
||||
assertThat(index3).isLessThan(index1);
|
||||
assertThat(index1).isLessThan(index2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteAndReorderAlbumDoesNotCreateNewReviewAndIsImmediatelyVisible() throws Exception {
|
||||
ensureTenantContext();
|
||||
String clerkId = ApiTestDataSeeder.DEFAULT_CLERK_ID;
|
||||
String clerkToken = wxTokenService.createWxUserToken(clerkId);
|
||||
clerkUserInfoService.updateTokenById(clerkId, clerkToken);
|
||||
|
||||
// 预置两条已审核通过的 PROFILE 媒资
|
||||
PlayMediaEntity media1 = seedMedia(clerkId);
|
||||
PlayMediaEntity media2 = seedMedia(clerkId);
|
||||
|
||||
seedApprovedAsset(clerkId, media1.getId(), 0);
|
||||
seedApprovedAsset(clerkId, media2.getId(), 1);
|
||||
|
||||
// 顾客端初始视图应包含两条媒资
|
||||
PlayClerkUserInfoResultVo beforeDetail = clerkUserInfoService.buildCustomerDetail(clerkId, "");
|
||||
List<String> beforeIds = beforeDetail.getMediaList().stream()
|
||||
.map(MediaVo::getMediaId)
|
||||
.collect(Collectors.toList());
|
||||
assertThat(beforeIds).contains(media1.getId(), media2.getId());
|
||||
|
||||
// 记录当前相册审核记录数量
|
||||
long reviewCountBefore = dataReviewInfoService.lambdaQuery()
|
||||
.eq(com.starry.admin.modules.clerk.module.entity.PlayClerkDataReviewInfoEntity::getClerkId, clerkId)
|
||||
.eq(com.starry.admin.modules.clerk.module.entity.PlayClerkDataReviewInfoEntity::getDataType, "2")
|
||||
.count();
|
||||
|
||||
// 现在通过 updateAlbum 只保留 media2,相当于“删除 media1 + 不引入新媒资”
|
||||
ObjectNode payload = objectMapper.createObjectNode();
|
||||
payload.putArray("album").add(media2.getId());
|
||||
|
||||
mockMvc.perform(post("/wx/clerk/user/updateAlbum")
|
||||
.header(USER_HEADER, DEFAULT_USER)
|
||||
.header(TENANT_HEADER, DEFAULT_TENANT)
|
||||
.header(Constants.CLERK_USER_LOGIN_TOKEN, Constants.TOKEN_PREFIX + clerkToken)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(payload.toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200));
|
||||
|
||||
ensureTenantContext();
|
||||
long reviewCountAfter = dataReviewInfoService.lambdaQuery()
|
||||
.eq(com.starry.admin.modules.clerk.module.entity.PlayClerkDataReviewInfoEntity::getClerkId, clerkId)
|
||||
.eq(com.starry.admin.modules.clerk.module.entity.PlayClerkDataReviewInfoEntity::getDataType, "2")
|
||||
.count();
|
||||
assertThat(reviewCountAfter)
|
||||
.as("Deleting/reordering without new media should not create new review records")
|
||||
.isEqualTo(reviewCountBefore);
|
||||
|
||||
// 资产表中仅剩 media2 且仍为 APPROVED 状态
|
||||
List<PlayClerkMediaAssetEntity> assets = mediaAssetService
|
||||
.listByState(clerkId, ClerkMediaUsage.PROFILE,
|
||||
Collections.singletonList(ClerkMediaReviewState.APPROVED));
|
||||
List<String> assetMediaIds = assets.stream()
|
||||
.map(PlayClerkMediaAssetEntity::getMediaId)
|
||||
.collect(Collectors.toList());
|
||||
assertThat(assetMediaIds)
|
||||
.contains(media2.getId())
|
||||
.doesNotContain(media1.getId());
|
||||
|
||||
// 顾客端视图应立即反映删除结果
|
||||
PlayClerkUserInfoResultVo afterDetail = clerkUserInfoService.buildCustomerDetail(clerkId, "");
|
||||
List<String> afterIds = afterDetail.getMediaList().stream()
|
||||
.map(MediaVo::getMediaId)
|
||||
.collect(Collectors.toList());
|
||||
assertThat(afterIds)
|
||||
.contains(media2.getId())
|
||||
.doesNotContain(media1.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateAlbumRejectsEmptyAlbumPayload() throws Exception {
|
||||
ensureTenantContext();
|
||||
String clerkId = ApiTestDataSeeder.DEFAULT_CLERK_ID;
|
||||
String clerkToken = wxTokenService.createWxUserToken(clerkId);
|
||||
clerkUserInfoService.updateTokenById(clerkId, clerkToken);
|
||||
|
||||
ObjectNode payload = objectMapper.createObjectNode();
|
||||
payload.putArray("album"); // 空数组
|
||||
|
||||
MvcResult result = mockMvc.perform(post("/wx/clerk/user/updateAlbum")
|
||||
.header(USER_HEADER, DEFAULT_USER)
|
||||
.header(TENANT_HEADER, DEFAULT_TENANT)
|
||||
.header(Constants.CLERK_USER_LOGIN_TOKEN, Constants.TOKEN_PREFIX + clerkToken)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(payload.toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andReturn();
|
||||
|
||||
String body = result.getResponse().getContentAsString();
|
||||
com.fasterxml.jackson.databind.JsonNode root = new ObjectMapper().readTree(body);
|
||||
assertThat(root.path("code").asInt())
|
||||
.as("empty album should be rejected, response=%s", body)
|
||||
.isEqualTo(500);
|
||||
assertThat(root.path("message").asText())
|
||||
.as("error message for empty album should be present, response=%s", body)
|
||||
.isNotBlank();
|
||||
}
|
||||
|
||||
private PlayMediaEntity seedMedia(String clerkId) {
|
||||
String mediaId = "media-" + java.util.UUID.randomUUID().toString().substring(0, 16);
|
||||
PlayMediaEntity entity = new PlayMediaEntity();
|
||||
entity.setId(mediaId);
|
||||
entity.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
|
||||
entity.setOwnerType(MediaOwnerType.CLERK);
|
||||
entity.setOwnerId(clerkId);
|
||||
entity.setKind("image");
|
||||
entity.setStatus(MediaStatus.READY.getCode());
|
||||
entity.setUrl("https://oss.apitest/" + mediaId + ".png");
|
||||
mediaService.save(entity);
|
||||
return entity;
|
||||
}
|
||||
|
||||
private void seedApprovedAsset(String clerkId, String mediaId, int orderIndex) {
|
||||
PlayClerkMediaAssetEntity asset = new PlayClerkMediaAssetEntity();
|
||||
asset.setId("asset-" + mediaId);
|
||||
asset.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
|
||||
asset.setClerkId(clerkId);
|
||||
asset.setMediaId(mediaId);
|
||||
asset.setUsage(ClerkMediaUsage.PROFILE.getCode());
|
||||
asset.setOrderIndex(orderIndex);
|
||||
asset.setReviewState(ClerkMediaReviewState.APPROVED.getCode());
|
||||
asset.setDeleted(false);
|
||||
mediaAssetService.save(asset);
|
||||
}
|
||||
|
||||
private void ensureTenantContext() {
|
||||
SecurityUtils.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
|
||||
}
|
||||
}
|
||||
@@ -213,9 +213,23 @@ class WxClerkMediaControllerApiTest extends AbstractApiTest {
|
||||
assertThat(customerMediaIdsAfterFirst).containsAll(allMediaIds);
|
||||
|
||||
List<String> keptMedia = List.of(mediaIdA, mediaIdC);
|
||||
|
||||
// 第二次提交:只删除与重新排序,不再生成新的资料审核记录,应直接生效
|
||||
long reviewCountBeforeSecond = dataReviewInfoService.lambdaQuery()
|
||||
.eq(com.starry.admin.modules.clerk.module.entity.PlayClerkDataReviewInfoEntity::getClerkId,
|
||||
ApiTestDataSeeder.DEFAULT_CLERK_ID)
|
||||
.eq(com.starry.admin.modules.clerk.module.entity.PlayClerkDataReviewInfoEntity::getDataType, "2")
|
||||
.count();
|
||||
submitAlbumUpdate(keptMedia, clerkToken);
|
||||
ensureTenantContext();
|
||||
approveLatestAlbumReview();
|
||||
long reviewCountAfterSecond = dataReviewInfoService.lambdaQuery()
|
||||
.eq(com.starry.admin.modules.clerk.module.entity.PlayClerkDataReviewInfoEntity::getClerkId,
|
||||
ApiTestDataSeeder.DEFAULT_CLERK_ID)
|
||||
.eq(com.starry.admin.modules.clerk.module.entity.PlayClerkDataReviewInfoEntity::getDataType, "2")
|
||||
.count();
|
||||
assertThat(reviewCountAfterSecond)
|
||||
.as("deleting/reordering album should not create another review record")
|
||||
.isEqualTo(reviewCountBeforeSecond);
|
||||
|
||||
List<PlayClerkMediaAssetEntity> assetsAfterSecondApprove = mediaAssetService
|
||||
.listActiveByUsage(ApiTestDataSeeder.DEFAULT_CLERK_ID, ClerkMediaUsage.PROFILE);
|
||||
@@ -224,27 +238,14 @@ class WxClerkMediaControllerApiTest extends AbstractApiTest {
|
||||
.filter(a -> mediaIdA.equals(a.getMediaId()))
|
||||
.max(Comparator.comparing(PlayClerkMediaAssetEntity::getCreatedTime))
|
||||
.orElse(null);
|
||||
PlayClerkMediaAssetEntity assetB = assetsAfterSecondApprove.stream()
|
||||
.filter(a -> mediaIdB.equals(a.getMediaId()))
|
||||
.max(Comparator.comparing(PlayClerkMediaAssetEntity::getCreatedTime))
|
||||
.orElse(null);
|
||||
PlayClerkMediaAssetEntity assetC = assetsAfterSecondApprove.stream()
|
||||
.filter(a -> mediaIdC.equals(a.getMediaId()))
|
||||
.max(Comparator.comparing(PlayClerkMediaAssetEntity::getCreatedTime))
|
||||
.orElse(null);
|
||||
PlayClerkMediaAssetEntity assetD = assetsAfterSecondApprove.stream()
|
||||
.filter(a -> mediaIdD.equals(a.getMediaId()))
|
||||
.max(Comparator.comparing(PlayClerkMediaAssetEntity::getCreatedTime))
|
||||
.orElse(null);
|
||||
|
||||
assertThat(assetA).isNotNull();
|
||||
assertThat(assetA.getReviewState()).isEqualTo(ClerkMediaReviewState.APPROVED.getCode());
|
||||
assertThat(assetC).isNotNull();
|
||||
assertThat(assetA.getOrderIndex()).isEqualTo(0);
|
||||
assertThat(assetC.getReviewState()).isEqualTo(ClerkMediaReviewState.APPROVED.getCode());
|
||||
assertThat(assetB).isNotNull();
|
||||
assertThat(assetB.getReviewState()).isEqualTo(ClerkMediaReviewState.REJECTED.getCode());
|
||||
assertThat(assetD).isNotNull();
|
||||
assertThat(assetD.getReviewState()).isEqualTo(ClerkMediaReviewState.REJECTED.getCode());
|
||||
assertThat(assetC.getOrderIndex()).isEqualTo(1);
|
||||
|
||||
PlayClerkUserInfoResultVo customerDetailAfterSecond =
|
||||
clerkUserInfoService.buildCustomerDetail(ApiTestDataSeeder.DEFAULT_CLERK_ID, "");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.starry.admin.modules.clerk.service.impl;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
@@ -8,11 +9,15 @@ import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.starry.admin.common.exception.CustomException;
|
||||
import com.starry.admin.modules.clerk.enums.ClerkMediaUsage;
|
||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkDataReviewInfoEntity;
|
||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkMediaAssetEntity;
|
||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
|
||||
import com.starry.admin.modules.clerk.module.enums.ClerkDataType;
|
||||
import com.starry.admin.modules.clerk.module.vo.PlayClerkDataReviewReturnVo;
|
||||
import com.starry.admin.modules.clerk.module.vo.PlayClerkDataReviewStateEditVo;
|
||||
import com.starry.admin.modules.clerk.service.IPlayClerkMediaAssetService;
|
||||
import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService;
|
||||
@@ -49,7 +54,10 @@ class PlayClerkDataReviewInfoServiceImplTest {
|
||||
ReflectionTestUtils.setField(service, "playClerkUserInfoService", clerkUserInfoService);
|
||||
ReflectionTestUtils.setField(service, "clerkMediaAssetService", clerkMediaAssetService);
|
||||
ReflectionTestUtils.setField(service, "mediaService", mediaService);
|
||||
doReturn(true).when(service).update(any(PlayClerkDataReviewInfoEntity.class));
|
||||
org.mockito.Mockito.lenient()
|
||||
.doReturn(true)
|
||||
.when(service)
|
||||
.update(any(PlayClerkDataReviewInfoEntity.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -105,6 +113,71 @@ class PlayClerkDataReviewInfoServiceImplTest {
|
||||
.hasMessageContaining("店员信息不存在");
|
||||
}
|
||||
|
||||
@Test
|
||||
void enrichDataContentWithMediaPreviewPopulatesCoverAndVideoUrls() {
|
||||
PlayClerkDataReviewInfoServiceImpl impl = service;
|
||||
|
||||
PlayClerkDataReviewReturnVo videoRow = new PlayClerkDataReviewReturnVo();
|
||||
videoRow.setId("row-video");
|
||||
videoRow.setDataTypeEnum(ClerkDataType.PHOTO_ALBUM);
|
||||
videoRow.setDataContent(java.util.Collections.singletonList("media-video"));
|
||||
|
||||
PlayClerkDataReviewReturnVo imageRow = new PlayClerkDataReviewReturnVo();
|
||||
imageRow.setId("row-image");
|
||||
imageRow.setDataTypeEnum(ClerkDataType.PHOTO_ALBUM);
|
||||
imageRow.setDataContent(java.util.Collections.singletonList("media-image"));
|
||||
|
||||
IPage<PlayClerkDataReviewReturnVo> page = new Page<>(1, 10);
|
||||
page.setRecords(java.util.Arrays.asList(videoRow, imageRow));
|
||||
|
||||
PlayMediaEntity videoMedia = new PlayMediaEntity();
|
||||
videoMedia.setId("media-video");
|
||||
videoMedia.setKind(com.starry.admin.modules.media.enums.MediaKind.VIDEO.getCode());
|
||||
videoMedia.setUrl("https://oss/video.mp4");
|
||||
videoMedia.setCoverUrl("https://oss/video-cover.jpg");
|
||||
|
||||
PlayMediaEntity imageMedia = new PlayMediaEntity();
|
||||
imageMedia.setId("media-image");
|
||||
imageMedia.setKind(com.starry.admin.modules.media.enums.MediaKind.IMAGE.getCode());
|
||||
imageMedia.setUrl("https://oss/image.png");
|
||||
|
||||
when(mediaService.getById("media-video")).thenReturn(videoMedia);
|
||||
when(mediaService.getById("media-image")).thenReturn(imageMedia);
|
||||
|
||||
org.springframework.test.util.ReflectionTestUtils.invokeMethod(
|
||||
impl, "enrichDataContentWithMediaPreview", page);
|
||||
|
||||
assertThat(videoRow.getDataContent()).containsExactly("https://oss/video-cover.jpg");
|
||||
assertThat(videoRow.getMediaVideoUrls()).containsExactly("https://oss/video.mp4");
|
||||
|
||||
assertThat(imageRow.getDataContent()).containsExactly("https://oss/image.png");
|
||||
assertThat(imageRow.getMediaVideoUrls()).containsExactly((String) null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void enrichDataContentWithMediaPreviewFallsBackToLegacyUrlWhenMediaMissing() {
|
||||
PlayClerkDataReviewInfoServiceImpl impl = service;
|
||||
|
||||
String legacyUrl = "https://oss/legacy-only.png";
|
||||
PlayClerkDataReviewReturnVo row = new PlayClerkDataReviewReturnVo();
|
||||
row.setId("row-legacy");
|
||||
row.setDataTypeEnum(ClerkDataType.PHOTO_ALBUM);
|
||||
row.setDataContent(java.util.Collections.singletonList(legacyUrl));
|
||||
|
||||
IPage<PlayClerkDataReviewReturnVo> page = new Page<>(1, 10);
|
||||
page.setRecords(java.util.Collections.singletonList(row));
|
||||
|
||||
// 当无法通过 mediaId 查询到记录时,应当保持 dataContent 为原始 URL,
|
||||
// 并且对应的 mediaVideoUrls 位置为 null。
|
||||
when(mediaService.getById(legacyUrl)).thenReturn(null);
|
||||
|
||||
org.springframework.test.util.ReflectionTestUtils.invokeMethod(
|
||||
impl, "enrichDataContentWithMediaPreview", page);
|
||||
|
||||
assertThat(row.getDataContent()).containsExactly(legacyUrl);
|
||||
assertThat(row.getMediaVideoUrls()).containsExactly((String) null);
|
||||
}
|
||||
|
||||
private PlayClerkDataReviewInfoEntity buildReview(String id, String clerkId, String dataType,
|
||||
java.util.List<String> payload) {
|
||||
PlayClerkDataReviewInfoEntity entity = new PlayClerkDataReviewInfoEntity();
|
||||
|
||||
@@ -24,4 +24,23 @@ class PlayClerkUserInfoServiceImplTest {
|
||||
assertThat(merged.stream().map(MediaVo::getUrl))
|
||||
.containsExactlyInAnyOrder("https://oss/2.png", "https://oss/1.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
void mergeLegacyAlbumFillsUsageStatusAndReviewStateForLegacyEntries() {
|
||||
List<String> legacy = Arrays.asList("https://oss/legacy-only.png");
|
||||
List<MediaVo> destination = new ArrayList<>();
|
||||
|
||||
List<MediaVo> merged = PlayClerkUserInfoServiceImpl.mergeLegacyAlbum(legacy, destination);
|
||||
|
||||
assertThat(merged).hasSize(1);
|
||||
MediaVo legacyVo = merged.get(0);
|
||||
assertThat(legacyVo.getUrl()).isEqualTo("https://oss/legacy-only.png");
|
||||
assertThat(legacyVo.getId())
|
||||
.as("legacy media id should fallback to url for compatibility")
|
||||
.isEqualTo("https://oss/legacy-only.png");
|
||||
assertThat(legacyVo.getUsage()).isEqualTo(com.starry.admin.modules.clerk.enums.ClerkMediaUsage.PROFILE.getCode());
|
||||
assertThat(legacyVo.getStatus()).isEqualTo(com.starry.admin.modules.media.enums.MediaStatus.READY.getCode());
|
||||
assertThat(legacyVo.getReviewState())
|
||||
.isEqualTo(com.starry.admin.modules.clerk.enums.ClerkMediaReviewState.APPROVED.getCode());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,6 @@ class MediaUploadServiceTest {
|
||||
MultipartFile file = buildImageFile("avatar.png", "image/png");
|
||||
|
||||
when(mediaService.normalizeAndSave(any())).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
when(mediaService.updateById(any())).thenReturn(true);
|
||||
when(ossFileService.upload(any(), eq(clerk.getTenantId()), any()))
|
||||
.thenReturn("https://oss.mock/avatar.png");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user