diff --git a/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxClerkController.java b/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxClerkController.java index e07c999..f9f23c1 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxClerkController.java +++ b/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxClerkController.java @@ -359,17 +359,35 @@ public class WxClerkController { mediaService.lambdaQuery() .in(com.starry.admin.modules.media.entity.PlayMediaEntity::getId, newMediaIds) .list(); - if (newMediaEntities.size() != newMediaIds.size()) { + + java.util.Set existingMediaIds = newMediaEntities.stream() + .map(com.starry.admin.modules.media.entity.PlayMediaEntity::getId) + .collect(java.util.stream.Collectors.toSet()); + java.util.Set missingMediaIds = new java.util.HashSet<>(newMediaIds); + missingMediaIds.removeAll(existingMediaIds); + + if (!missingMediaIds.isEmpty()) { + // 这里很可能是历史相册里的纯 URL(未经过媒资化),我们记录日志但不直接失败, + // 在审核内容中仍然保留这些字符串,由审核端用回显逻辑处理。 log.warn( - "[ClerkAlbumUpdate] new media size mismatch, clerkId={}, tenantId={}, expectedIds={}, foundIds={}", + "[ClerkAlbumUpdate] some album entries not found in play_media, treating as legacy values, clerkId={} tenantId={} missingIds={} existingIds={}", userInfo.getId(), userInfo.getTenantId(), - newMediaIds, - newMediaEntities.stream() - .map(com.starry.admin.modules.media.entity.PlayMediaEntity::getId) - .collect(java.util.stream.Collectors.toSet())); - throw new CustomException("存在未完成上传的照片/视频,请稍后重试"); + missingMediaIds, + existingMediaIds); } + + if (log.isInfoEnabled()) { + log.info( + "[ClerkAlbumUpdate] loaded newMediaEntities for validation, clerkId={} tenantId={} mediaSummaries={}", + userInfo.getId(), + userInfo.getTenantId(), + newMediaEntities.stream() + .map(m -> String.format("id=%s,status=%s,ownerType=%s,ownerId=%s,tenantId=%s", + m.getId(), m.getStatus(), m.getOwnerType(), m.getOwnerId(), m.getTenantId())) + .collect(java.util.stream.Collectors.toList())); + } + for (com.starry.admin.modules.media.entity.PlayMediaEntity media : newMediaEntities) { boolean tenantMatched = userInfo.getTenantId().equals(media.getTenantId()); boolean ownerTypeMatched = MediaOwnerType.CLERK.equals(media.getOwnerType()); @@ -394,7 +412,6 @@ public class WxClerkController { throw new CustomException("存在无效的照片/视频,请刷新后重试"); } if (!statusReady) { - // 理论上上面的分支已经覆盖,这里多打一条保护日志,便于排查历史数据 log.warn( "[ClerkAlbumUpdate] media not in READY state for clerk, clerkId={} tenantId={} mediaId={} mediaStatus={}", userInfo.getId(), diff --git a/play-admin/src/test/java/com/starry/admin/api/WxClerkAlbumUpdateApiTest.java b/play-admin/src/test/java/com/starry/admin/api/WxClerkAlbumUpdateApiTest.java index 260fbb6..0447d1d 100644 --- a/play-admin/src/test/java/com/starry/admin/api/WxClerkAlbumUpdateApiTest.java +++ b/play-admin/src/test/java/com/starry/admin/api/WxClerkAlbumUpdateApiTest.java @@ -238,6 +238,56 @@ class WxClerkAlbumUpdateApiTest extends AbstractApiTest { .isNotBlank(); } + @Test + void updateAlbumAllowsMixedLegacyUrlsAndNewMediaIdsForReview() throws Exception { + ensureTenantContext(); + String clerkId = ApiTestDataSeeder.DEFAULT_CLERK_ID; + String clerkToken = wxTokenService.createWxUserToken(clerkId); + clerkUserInfoService.updateTokenById(clerkId, clerkToken); + + // 预置一条已就绪的媒资,模拟“新上传的视频/图片” + PlayMediaEntity media = seedMedia(clerkId); + + // 模拟老相册中的 URL(未媒资化的历史数据) + String legacyUrl1 = "https://oss.apitest/legacy-1.png"; + String legacyUrl2 = "https://oss.apitest/legacy-2.png"; + + 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(); + + ObjectNode payload = objectMapper.createObjectNode(); + com.fasterxml.jackson.databind.node.ArrayNode albumArray = payload.putArray("album"); + albumArray.add(legacyUrl1); + albumArray.add(legacyUrl2); + albumArray.add(media.getId()); + + 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("mixed legacy URLs and new media ids should be accepted for review, response=%s", body) + .isEqualTo(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("mixed legacy URLs and new media ids should create exactly one new review record") + .isEqualTo(reviewCountBefore + 1); + } + private PlayMediaEntity seedMedia(String clerkId) { String mediaId = "media-" + java.util.UUID.randomUUID().toString().substring(0, 16); PlayMediaEntity entity = new PlayMediaEntity();