删除品牌

This commit is contained in:
super
2026-03-27 21:32:00 +08:00
parent ea212c8931
commit 5dd7a92cf5
22 changed files with 675 additions and 214 deletions
@@ -6,13 +6,11 @@ import com.nanri.aiimage.modules.ziniao.model.vo.ZiniaoStaffItemVo;
import java.util.List;
public interface ZiniaoClient {
String getAppToken();
Long getCompanyIdByApiKey();
List<ZiniaoStaffItemVo> listStaff(Long companyId);
List<ZiniaoShopCacheDto> listUserStores(Long companyId, Long userId, String userToken);
List<ZiniaoShopCacheDto> listUserStores(Long companyId, Long userId);
String getUserLoginToken(Long companyId, Long userId);
}
@@ -6,8 +6,8 @@ import com.nanri.aiimage.common.exception.BusinessException;
import com.nanri.aiimage.config.ZiniaoProperties;
import com.nanri.aiimage.modules.ziniao.model.cache.ZiniaoShopCacheDto;
import com.nanri.aiimage.modules.ziniao.model.vo.ZiniaoStaffItemVo;
import com.nanri.aiimage.modules.ziniao.service.ZiniaoSessionCacheService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.stereotype.Component;
@@ -24,35 +24,6 @@ public class ZiniaoClientImpl implements ZiniaoClient {
private final ZiniaoProperties ziniaoProperties;
private final ObjectMapper objectMapper;
private final ZiniaoSessionCacheService ziniaoSessionCacheService;
@Override
public String getAppToken() {
String cachedToken = ziniaoSessionCacheService.getAppToken();
if (cachedToken != null && !cachedToken.isBlank()) {
return cachedToken;
}
String raw = postWithApiKeyEmptyJsonBody(ziniaoProperties.getAppTokenPath(), "获取 appToken");
try {
JsonNode root = objectMapper.readTree(raw);
JsonNode data = firstNonNull(root.get("data"), root.get("result"), root);
String token = text(firstNonNull(
data == null ? null : data.get("appAuthToken"),
data == null ? null : data.get("appToken"),
root.get("appAuthToken"),
root.get("appToken")
));
if (token == null || token.isBlank()) {
throw new BusinessException("紫鸟 appToken 响应缺少 token");
}
ziniaoSessionCacheService.saveAppToken(token);
return token;
} catch (BusinessException ex) {
throw ex;
} catch (Exception ex) {
throw new BusinessException("解析紫鸟 appToken 响应失败");
}
}
@Override
public Long getCompanyIdByApiKey() {
@@ -104,14 +75,14 @@ public class ZiniaoClientImpl implements ZiniaoClient {
}
@Override
public List<ZiniaoShopCacheDto> listUserStores(Long companyId, Long userId, String userToken) {
String raw = postWithAuthorization(ziniaoProperties.getUserStoresPath(), userToken, Map.of(
"companyId", companyId,
"userId", userId,
public List<ZiniaoShopCacheDto> listUserStores(Long companyId, Long userId) {
String raw = postWithApiKey(ziniaoProperties.getUserStoresPath(), Map.of(
"companyId", String.valueOf(companyId),
"isAccurate", "",
"limit", "100",
"storeName", "",
"isAccurate", 1,
"page", 1,
"limit", 10
"page", "1",
"userId", String.valueOf(userId)
), "获取员工店铺列表");
return parseUserStores(raw);
}
@@ -128,7 +99,15 @@ public class ZiniaoClientImpl implements ZiniaoClient {
throw new BusinessException("获取紫鸟员工登录 token 失败: " + Objects.toString(text(root.get("msg")), "未知错误"));
}
JsonNode data = firstNonNull(root.get("data"), root.get("result"));
JsonNode payload = firstNonNull(
data == null ? null : data.get("data"),
data == null ? null : data.get("result"),
data,
root
);
String token = text(firstNonNull(
payload == null ? null : payload.get("token"),
payload == null ? null : payload.get("loginToken"),
data == null ? null : data.get("token"),
data == null ? null : data.get("loginToken"),
root.get("token"),
@@ -152,21 +131,9 @@ public class ZiniaoClientImpl implements ZiniaoClient {
.uri(joinUrl(ziniaoProperties.getBaseUrl(), path))
.headers(headers -> headers.setBearerAuth(apiKey))
.retrieve()
.body(String.class);
validateSuccess(raw, action, path);
return raw;
}
private String postWithApiKeyEmptyJsonBody(String path, String action) {
String apiKey = requireText(ziniaoProperties.getApiKey(), "紫鸟 apiKey 未配置");
String raw = getRestClient().post()
.uri(joinUrl(ziniaoProperties.getBaseUrl(), path))
.headers(headers -> {
headers.setBearerAuth(apiKey);
headers.setContentType(MediaType.APPLICATION_JSON);
.onStatus(HttpStatusCode::isError, (req, res) -> {
// 保留响应体,交给后续 validateSuccess 统一解析
})
.body("")
.retrieve()
.body(String.class);
validateSuccess(raw, action, path);
return raw;
@@ -185,6 +152,9 @@ public class ZiniaoClientImpl implements ZiniaoClient {
}
String raw = request
.retrieve()
.onStatus(HttpStatusCode::isError, (req, res) -> {
// 保留响应体,交给后续 validateSuccess 统一解析 code/sub_code/sub_msg
})
.body(String.class);
validateSuccess(raw, action, path);
return raw;
@@ -195,7 +165,11 @@ public class ZiniaoClientImpl implements ZiniaoClient {
RestClient.RequestBodySpec request = getRestClient().post()
.uri(joinUrl(ziniaoProperties.getBaseUrl(), path))
.headers(headers -> {
headers.set("Authorization", token);
if (token.regionMatches(true, 0, "Bearer ", 0, 7)) {
headers.set("Authorization", token);
} else {
headers.setBearerAuth(token);
}
headers.setContentType(MediaType.APPLICATION_JSON);
});
if (body != null) {
@@ -212,7 +186,29 @@ public class ZiniaoClientImpl implements ZiniaoClient {
try {
JsonNode root = objectMapper.readTree(raw);
if (!success(root)) {
throw new BusinessException("紫鸟接口返回失败(" + action + ", " + path + "): " + Objects.toString(text(root.get("msg")), "未知错误"));
String code = Objects.toString(text(root.get("code")), "");
String msg = Objects.toString(text(root.get("msg")), "未知错误");
String subCode = Objects.toString(text(root.get("sub_code")), "");
String subMsg = Objects.toString(text(root.get("sub_msg")), "");
StringBuilder detail = new StringBuilder();
if (!code.isBlank()) {
detail.append("code=").append(code);
}
if (!subCode.isBlank()) {
if (!detail.isEmpty()) detail.append(", ");
detail.append("sub_code=").append(subCode);
}
if (!subMsg.isBlank()) {
if (!detail.isEmpty()) detail.append(", ");
detail.append("sub_msg=").append(subMsg);
}
if (!detail.isEmpty()) {
detail.append(", ");
}
detail.append("msg=").append(msg);
throw new BusinessException("紫鸟接口返回失败(" + action + ", " + path + "): " + detail);
}
} catch (BusinessException ex) {
throw ex;
@@ -284,7 +280,13 @@ public class ZiniaoClientImpl implements ZiniaoClient {
private List<ZiniaoShopCacheDto> parseUserStores(String raw) {
try {
JsonNode root = objectMapper.readTree(raw);
JsonNode itemsNode = firstNonNull(root.get("data"), root.get("result"));
JsonNode wrapper = firstNonNull(root.get("data"), root.get("result"), root);
JsonNode itemsNode = firstNonNull(
wrapper == null ? null : wrapper.get("data"),
wrapper == null ? null : wrapper.get("items"),
wrapper
);
List<ZiniaoShopCacheDto> items = new ArrayList<>();
if (itemsNode != null && itemsNode.isArray()) {
for (JsonNode itemNode : itemsNode) {
@@ -26,6 +26,75 @@ import java.util.UUID;
@RequiredArgsConstructor
public class ZiniaoAuthService {
public StoreMatchResult matchStoreByNameAcrossStaff(String targetShopName, Long preferUserId) {
ensureEnabled();
String normalizedTarget = normalizeShopName(targetShopName);
if (normalizedTarget.isBlank()) {
return new StoreMatchResult(false, null, null, null, null, null);
}
Long companyId = resolveCompanyId();
List<ZiniaoStaffItemVo> staff = ziniaoClient.listStaff(companyId);
List<Long> userIds = new java.util.ArrayList<>();
if (preferUserId != null && preferUserId > 0) {
userIds.add(preferUserId);
}
for (ZiniaoStaffItemVo item : staff) {
if (item != null && item.getUserId() != null && item.getUserId() > 0 && (userIds.isEmpty() || !userIds.contains(item.getUserId()))) {
userIds.add(item.getUserId());
}
}
for (Long staffUserId : userIds) {
if (staffUserId == null || staffUserId <= 0) {
continue;
}
List<ZiniaoShopCacheDto> stores;
try {
stores = ziniaoClient.listUserStores(companyId, staffUserId);
} catch (BusinessException ex) {
if (isSkippableUserStoresError(ex)) {
continue;
}
throw ex;
}
for (ZiniaoShopCacheDto store : stores) {
String storeName = normalizeShopName(store == null ? null : store.getShopName());
if (!storeName.isBlank() && storeName.equals(normalizedTarget)) {
String openStoreUrl = buildOpenStoreUrl(store.getShopId(), staffUserId, ziniaoClient.getUserLoginToken(companyId, staffUserId));
return new StoreMatchResult(true, store.getShopId(), store.getShopName(), store.getPlatform(), staffUserId, openStoreUrl);
}
}
}
return new StoreMatchResult(false, null, null, null, null, null);
}
private boolean isSkippableUserStoresError(BusinessException ex) {
String message = ex == null ? null : ex.getMessage();
if (message == null || message.isBlank()) {
return false;
}
return message.contains("code=40004")
|| message.contains("userId存在无效的参数值")
|| message.contains("无权限")
|| message.contains("没有权限");
}
private String normalizeShopName(String value) {
if (value == null) {
return "";
}
return value.replace("\u3000", " ")
.trim();
}
public record StoreMatchResult(boolean matched, String shopId, String shopName, String platform, Long matchedUserId,
String openStoreUrl) {
}
private final ZiniaoProperties ziniaoProperties;
private final ZiniaoClient ziniaoClient;
private final ZiniaoSessionCacheService ziniaoSessionCacheService;
@@ -36,13 +105,13 @@ public class ZiniaoAuthService {
ZiniaoSessionVo vo = new ZiniaoSessionVo();
vo.setSessionId(session.getSessionId());
vo.setEnabled(ziniaoProperties.isEnabled());
vo.setAuthenticated(session.getAccessToken() != null && !session.getAccessToken().isBlank());
vo.setAuthenticated(Boolean.TRUE);
vo.setCompanyId(session.getCompanyId());
vo.setCurrentUserId(session.getCurrentUserId());
vo.setZiniaoUserId(session.getZiniaoUserId());
vo.setNickname(session.getNickname());
vo.setAppToken(session.getAccessToken());
vo.setTokenType(session.getTokenType());
vo.setAppToken(null);
vo.setTokenType(null);
vo.setExpireAt(session.getExpireAt());
vo.setShopCount(ziniaoSessionCacheService.getShops(session.getSessionId()).size());
vo.setCurrentShopId(session.getDefaultShopId());
@@ -61,8 +130,7 @@ public class ZiniaoAuthService {
public ZiniaoShopListVo listShops(String sessionId, Long userId) {
ZiniaoSessionCacheDto session = requireOrInitSession(sessionId, userId);
Long currentUserId = requireUserId(session.getCurrentUserId());
String userToken = ziniaoClient.getUserLoginToken(resolveCompanyId(), currentUserId);
List<ZiniaoShopCacheDto> shops = ziniaoClient.listUserStores(resolveCompanyId(), currentUserId, userToken);
List<ZiniaoShopCacheDto> shops = ziniaoClient.listUserStores(resolveCompanyId(), currentUserId);
ziniaoSessionCacheService.saveShops(session.getSessionId(), shops);
if (!shops.isEmpty() && (session.getDefaultShopId() == null || session.getDefaultShopId().isBlank())) {
session.setDefaultShopId(shops.get(0).getShopId());
@@ -86,7 +154,7 @@ public class ZiniaoAuthService {
String userToken = ziniaoClient.getUserLoginToken(resolveCompanyId(), currentUserId);
List<ZiniaoShopCacheDto> shops = ziniaoSessionCacheService.getShops(session.getSessionId());
if (shops.isEmpty()) {
shops = ziniaoClient.listUserStores(resolveCompanyId(), currentUserId, userToken);
shops = ziniaoClient.listUserStores(resolveCompanyId(), currentUserId);
ziniaoSessionCacheService.saveShops(session.getSessionId(), shops);
}
ZiniaoShopCacheDto targetShop = shops.stream()
@@ -110,8 +178,7 @@ public class ZiniaoAuthService {
public List<ZiniaoShopCacheDto> listShopsForConfiguredUser() {
ensureEnabled();
Long userId = parseConfiguredOpenStoreUserId();
String userToken = ziniaoClient.getUserLoginToken(resolveCompanyId(), userId);
return ziniaoClient.listUserStores(resolveCompanyId(), userId, userToken);
return ziniaoClient.listUserStores(resolveCompanyId(), userId);
}
public String buildOpenStoreUrlForShop(ZiniaoShopCacheDto shop) {
@@ -128,9 +195,6 @@ public class ZiniaoAuthService {
String sessionId = "ziniao_" + UUID.randomUUID().toString().replace("-", "");
ZiniaoSessionCacheDto session = new ZiniaoSessionCacheDto();
session.setSessionId(sessionId);
String appToken = ziniaoClient.getAppToken();
session.setAccessToken(maskToken(appToken));
session.setTokenType("Bearer");
session.setExpireAt(Instant.now().plusSeconds(ziniaoProperties.getSessionTtlHours() * 3600).toEpochMilli());
session.setCompanyId(resolveCompanyId());
session.setCurrentUserId(userId);
@@ -186,7 +250,7 @@ public class ZiniaoAuthService {
if (ziniaoProperties.getOpenStoreExtraArgs() != null && !ziniaoProperties.getOpenStoreExtraArgs().isBlank()) {
builder.queryParam("extraArgs", ziniaoProperties.getOpenStoreExtraArgs());
}
return builder.build(true).toUriString();
return builder.build().encode().toUriString();
}
private void ensureEnabled() {
@@ -17,27 +17,10 @@ import java.util.List;
@RequiredArgsConstructor
public class ZiniaoSessionCacheService {
private static final String APP_TOKEN_KEY = "ziniao:app-token";
private final StringRedisTemplate stringRedisTemplate;
private final ObjectMapper objectMapper;
private final ZiniaoProperties ziniaoProperties;
public void saveAppToken(String appToken) {
if (appToken == null || appToken.isBlank()) {
return;
}
stringRedisTemplate.opsForValue().set(APP_TOKEN_KEY, appToken.trim(), Duration.ofHours(ziniaoProperties.getSessionTtlHours()));
}
public String getAppToken() {
String value = stringRedisTemplate.opsForValue().get(APP_TOKEN_KEY);
if (value == null || value.isBlank()) {
return null;
}
return value.trim();
}
public void saveSession(ZiniaoSessionCacheDto session) {
try {
stringRedisTemplate.opsForValue().set(buildSessionKey(session.getSessionId()), objectMapper.writeValueAsString(session), Duration.ofHours(ziniaoProperties.getSessionTtlHours()));