Files
peipei-backend/play-admin/src/test/java/com/starry/admin/api/WxClerkUserBackwardCompatApiTest.java

207 lines
8.8 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.status;
import com.fasterxml.jackson.databind.JsonNode;
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.PlayClerkUserInfoEntity;
import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService;
import com.starry.admin.modules.media.enums.MediaStatus;
import com.starry.admin.modules.weichat.entity.clerk.MediaVo;
import com.starry.admin.modules.weichat.entity.clerk.PlayClerkUserInfoResultVo;
import com.starry.admin.utils.SecurityUtils;
import com.starry.common.utils.IdUtils;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.junit.jupiter.api.AfterEach;
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;
/**
* 覆盖微信顾客端店员列表 / 详情在仅存在历史相册数据时的兼容行为。
*
* <p>重点校验:
* <ul>
* <li>旧字段 {@code album} 在列表接口中仍然可见;</li>
* <li>同时会被折叠进 {@code mediaList}且不会破坏后续媒资结构id/url/usage/status/reviewState</li>
* <li>详情接口中 {@link PlayClerkUserInfoResultVo#mediaList} 也包含这些历史相册。</li>
* </ul>
* </p>
*/
class WxClerkUserBackwardCompatApiTest extends AbstractApiTest {
private final ObjectMapper objectMapper = new ObjectMapper();
@Autowired
private IPlayClerkUserInfoService clerkUserInfoService;
private final List<String> clerkIdsToCleanup = new ArrayList<>();
@AfterEach
void tearDown() {
ensureTenantContext();
if (!clerkIdsToCleanup.isEmpty()) {
clerkUserInfoService.removeByIds(clerkIdsToCleanup);
clerkIdsToCleanup.clear();
}
}
/**
* 场景:只有旧相册(album) 而没有 play_media / play_clerk_media_asset 记录时,
* 微信顾客端列表接口仍然返回可用的 album 和 mediaList。
*/
@Test
void customerListIncludesLegacyAlbumAsMediaList() throws Exception {
ensureTenantContext();
String marker = "legacy-album-" + IdUtils.getUuid().substring(0, 8);
List<String> legacyAlbum = List.of(
"https://example.com/apitest/legacy/" + marker + "-a.png",
"https://example.com/apitest/legacy/" + marker + "-b.png");
String clerkId = createLegacyAlbumClerk(marker, legacyAlbum);
ObjectNode payload = objectMapper.createObjectNode();
payload.put("pageNum", 1);
payload.put("pageSize", 20);
payload.put("nickname", marker);
payload.put("typeId", "");
payload.put("levelId", "");
payload.put("sex", "");
payload.put("province", "");
MvcResult result = mockMvc.perform(post("/wx/clerk/user/queryByPage")
.header(TENANT_HEADER, DEFAULT_TENANT)
.header(USER_HEADER, DEFAULT_USER)
.contentType(MediaType.APPLICATION_JSON)
.content(payload.toString()))
.andExpect(status().isOk())
.andReturn();
String body = result.getResponse().getContentAsString();
JsonNode root = objectMapper.readTree(body);
assertThat(root.path("code").asInt()).as("response=%s", body).isEqualTo(200);
JsonNode records = root.path("data").path("records");
assertThat(records.isArray()).as("records should be array, response=%s", body).isTrue();
JsonNode target = null;
for (JsonNode record : records) {
if (clerkId.equals(record.path("id").asText())) {
target = record;
break;
}
}
assertThat(target)
.withFailMessage("列表中未找到目标店员 %s, response=%s", clerkId, body)
.isNotNull();
// album 字段仍然保留原有的历史值
JsonNode albumNode = target.path("album");
assertThat(albumNode.isArray()).isTrue();
Set<String> albumUrls = new HashSet<>();
albumNode.forEach(node -> albumUrls.add(node.asText()));
assertThat(albumUrls).containsExactlyInAnyOrderElementsOf(legacyAlbum);
// mediaList 中应当包含折叠后的 Legacy 媒资,并为其补齐 usage/status/reviewState 等字段
JsonNode mediaListNode = target.path("mediaList");
assertThat(mediaListNode.isArray()).isTrue();
assertThat(mediaListNode.size()).isEqualTo(legacyAlbum.size());
Set<String> mediaUrls = new HashSet<>();
for (JsonNode media : mediaListNode) {
String url = media.path("url").asText();
mediaUrls.add(url);
if (!legacyAlbum.contains(url)) {
continue;
}
// 对于从 album 折叠出来的条目,我们要求:
// id 与 url 相同usage/status/reviewState 均为兼容值。
assertThat(media.path("id").asText()).isEqualTo(url);
assertThat(media.path("usage").asText()).isEqualTo(ClerkMediaUsage.PROFILE.getCode());
assertThat(media.path("status").asText()).isEqualTo(MediaStatus.READY.getCode());
assertThat(media.path("reviewState").asText())
.isEqualTo(ClerkMediaReviewState.APPROVED.getCode());
}
assertThat(mediaUrls).containsAll(legacyAlbum);
}
/**
* 场景:只有旧相册(album) 数据时,顾客端店员详情接口 buildCustomerDetail 也会将其折叠进 mediaList
* 同时保留 album 字段不变。
*/
@Test
void customerDetailMergesLegacyAlbumIntoMediaList() {
ensureTenantContext();
String marker = "legacy-detail-" + IdUtils.getUuid().substring(0, 8);
List<String> legacyAlbum = List.of(
"https://example.com/apitest/legacy/" + marker + "-a.png",
"https://example.com/apitest/legacy/" + marker + "-b.png");
String clerkId = createLegacyAlbumClerk(marker, legacyAlbum);
PlayClerkUserInfoResultVo detail = clerkUserInfoService.buildCustomerDetail(clerkId, "");
assertThat(detail.getAlbum()).containsExactlyElementsOf(legacyAlbum);
List<MediaVo> mediaList = detail.getMediaList();
assertThat(mediaList).isNotNull();
assertThat(mediaList).hasSize(legacyAlbum.size());
Set<String> mediaUrls = new HashSet<>();
for (MediaVo media : mediaList) {
String url = media.getUrl();
mediaUrls.add(url);
if (!legacyAlbum.contains(url)) {
continue;
}
assertThat(media.getId()).isEqualTo(url);
assertThat(media.getUsage()).isEqualTo(ClerkMediaUsage.PROFILE.getCode());
assertThat(media.getStatus()).isEqualTo(MediaStatus.READY.getCode());
assertThat(media.getReviewState()).isEqualTo(ClerkMediaReviewState.APPROVED.getCode());
}
assertThat(mediaUrls).containsAll(legacyAlbum);
}
private String createLegacyAlbumClerk(String marker, List<String> album) {
String clerkId = IdUtils.getUuid();
PlayClerkUserInfoEntity clerk = new PlayClerkUserInfoEntity();
clerk.setId(clerkId);
clerk.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
clerk.setNickname("兼容测试店员-" + marker);
clerk.setLevelId(ApiTestDataSeeder.DEFAULT_CLERK_LEVEL_ID);
clerk.setClerkState("1");
clerk.setOnboardingState("1");
clerk.setListingState("1");
clerk.setDisplayState("1");
clerk.setRecommendationState("0");
clerk.setPinToTopState("0");
clerk.setRandomOrderState("1");
clerk.setFixingLevel("0");
clerk.setOnlineState("1");
clerk.setPhone("138" + clerkId.substring(0, 8));
clerk.setOpenid("openid-legacy-" + marker);
clerk.setWeiChatCode("wx-code-legacy-" + marker);
clerk.setTypeId("api-type-legacy");
clerk.setProvince("API省");
clerk.setCity("API市");
clerk.setEntryTime(LocalDateTime.now());
clerk.setAddTime(LocalDateTime.now());
clerk.setAlbum(new ArrayList<>(album));
clerkUserInfoService.save(clerk);
clerkIdsToCleanup.add(clerkId);
return clerkId;
}
private void ensureTenantContext() {
SecurityUtils.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
}
}