更新后端新增内容
This commit is contained in:
+10
-1
@@ -1,9 +1,18 @@
|
||||
package com.nanri.aiimage.modules.ziniao.client;
|
||||
|
||||
import com.nanri.aiimage.modules.ziniao.model.cache.ZiniaoShopCacheDto;
|
||||
import com.nanri.aiimage.modules.ziniao.model.vo.ZiniaoStaffItemVo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ZiniaoClient {
|
||||
List<ZiniaoShopCacheDto> listShops();
|
||||
String getAppToken();
|
||||
|
||||
Long getCompanyIdByApiKey();
|
||||
|
||||
List<ZiniaoStaffItemVo> listStaff(Long companyId);
|
||||
|
||||
List<ZiniaoShopCacheDto> listUserStores(Long companyId, Long userId, String userToken);
|
||||
|
||||
String getUserLoginToken(Long companyId, Long userId);
|
||||
}
|
||||
|
||||
+254
-24
@@ -5,6 +5,8 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
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.MediaType;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
@@ -13,6 +15,7 @@ import org.springframework.web.client.RestClient;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
@Component
|
||||
@@ -21,22 +24,222 @@ public class ZiniaoClientImpl implements ZiniaoClient {
|
||||
|
||||
private final ZiniaoProperties ziniaoProperties;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final ZiniaoSessionCacheService ziniaoSessionCacheService;
|
||||
|
||||
@Override
|
||||
public List<ZiniaoShopCacheDto> listShops() {
|
||||
if (ziniaoProperties.getCompanyId() == null || ziniaoProperties.getCompanyId() <= 0) {
|
||||
throw new BusinessException("紫鸟 companyId 未配置");
|
||||
public String getAppToken() {
|
||||
String cachedToken = ziniaoSessionCacheService.getAppToken();
|
||||
if (cachedToken != null && !cachedToken.isBlank()) {
|
||||
return cachedToken;
|
||||
}
|
||||
String raw = getRestClient().post()
|
||||
.uri(joinUrl(ziniaoProperties.getBaseUrl(), ziniaoProperties.getShopsPath()))
|
||||
.headers(headers -> {
|
||||
headers.setBearerAuth(ziniaoProperties.getApiKey());
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
})
|
||||
.body(java.util.Map.of("companyId", ziniaoProperties.getCompanyId()))
|
||||
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() {
|
||||
String raw = getWithApiKey("/app/builtin/company", "获取 companyId");
|
||||
try {
|
||||
JsonNode root = objectMapper.readTree(raw);
|
||||
JsonNode data = firstNonNull(root.get("data"), root.get("result"), root);
|
||||
Long companyId = longValue(firstNonNull(data == null ? null : data.get("companyId"), root.get("companyId")));
|
||||
if (companyId == null || companyId <= 0) {
|
||||
throw new BusinessException("紫鸟 companyId 响应缺少 companyId");
|
||||
}
|
||||
return companyId;
|
||||
} catch (BusinessException ex) {
|
||||
throw ex;
|
||||
} catch (Exception ex) {
|
||||
throw new BusinessException("解析紫鸟 companyId 响应失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ZiniaoStaffItemVo> listStaff(Long companyId) {
|
||||
String raw = postWithApiKey(ziniaoProperties.getStaffListPath(), Map.of(
|
||||
"companyId", String.valueOf(companyId),
|
||||
"level", "",
|
||||
"isAccurate", "",
|
||||
"limit", "100",
|
||||
"page", "1",
|
||||
"departmentIds", List.of(),
|
||||
"delflag", "",
|
||||
"name", "",
|
||||
"username", ""
|
||||
), "获取员工列表");
|
||||
try {
|
||||
JsonNode root = objectMapper.readTree(raw);
|
||||
JsonNode dataNode = firstNonNull(root.get("data"), root.get("result"));
|
||||
JsonNode itemsNode = dataNode == null ? null : firstNonNull(dataNode.get("data"), dataNode.get("items"), dataNode);
|
||||
List<ZiniaoStaffItemVo> items = new ArrayList<>();
|
||||
if (itemsNode != null && itemsNode.isArray()) {
|
||||
for (JsonNode itemNode : itemsNode) {
|
||||
addStaffItem(items, itemNode);
|
||||
}
|
||||
} else if (itemsNode != null && itemsNode.isObject()) {
|
||||
addStaffItem(items, itemsNode);
|
||||
}
|
||||
return items;
|
||||
} catch (Exception ex) {
|
||||
throw new BusinessException("解析紫鸟员工列表失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ZiniaoShopCacheDto> listUserStores(Long companyId, Long userId, String userToken) {
|
||||
String raw = postWithAuthorization(ziniaoProperties.getUserStoresPath(), userToken, Map.of(
|
||||
"companyId", companyId,
|
||||
"userId", userId,
|
||||
"storeName", "",
|
||||
"isAccurate", 1,
|
||||
"page", 1,
|
||||
"limit", 10
|
||||
), "获取员工店铺列表");
|
||||
return parseUserStores(raw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUserLoginToken(Long companyId, Long userId) {
|
||||
String raw = postWithApiKey(ziniaoProperties.getUserLoginTokenPath(), Map.of(
|
||||
"companyId", String.valueOf(companyId),
|
||||
"userId", String.valueOf(userId)
|
||||
), "获取员工登录 token");
|
||||
try {
|
||||
JsonNode root = objectMapper.readTree(raw);
|
||||
if (!success(root)) {
|
||||
throw new BusinessException("获取紫鸟员工登录 token 失败: " + Objects.toString(text(root.get("msg")), "未知错误"));
|
||||
}
|
||||
JsonNode data = firstNonNull(root.get("data"), root.get("result"));
|
||||
String token = text(firstNonNull(
|
||||
data == null ? null : data.get("token"),
|
||||
data == null ? null : data.get("loginToken"),
|
||||
root.get("token"),
|
||||
root.get("loginToken")
|
||||
));
|
||||
if (token == null || token.isBlank()) {
|
||||
throw new BusinessException("紫鸟员工登录 token 响应缺少 token");
|
||||
}
|
||||
return token;
|
||||
} catch (BusinessException ex) {
|
||||
throw ex;
|
||||
} catch (Exception ex) {
|
||||
throw new BusinessException("解析紫鸟员工登录 token 响应失败");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String getWithApiKey(String path, String action) {
|
||||
String apiKey = requireText(ziniaoProperties.getApiKey(), "紫鸟 apiKey 未配置");
|
||||
String raw = getRestClient().get()
|
||||
.uri(joinUrl(ziniaoProperties.getBaseUrl(), path))
|
||||
.headers(headers -> headers.setBearerAuth(apiKey))
|
||||
.retrieve()
|
||||
.body(String.class);
|
||||
return parseShops(raw);
|
||||
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);
|
||||
})
|
||||
.body("")
|
||||
.retrieve()
|
||||
.body(String.class);
|
||||
validateSuccess(raw, action, path);
|
||||
return raw;
|
||||
}
|
||||
|
||||
private String postWithApiKey(String path, Map<String, Object> body, String action) {
|
||||
String apiKey = requireText(ziniaoProperties.getApiKey(), "紫鸟 apiKey 未配置");
|
||||
RestClient.RequestBodySpec request = getRestClient().post()
|
||||
.uri(joinUrl(ziniaoProperties.getBaseUrl(), path))
|
||||
.headers(headers -> {
|
||||
headers.setBearerAuth(apiKey);
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
});
|
||||
if (body != null) {
|
||||
request.body(body);
|
||||
}
|
||||
String raw = request
|
||||
.retrieve()
|
||||
.body(String.class);
|
||||
validateSuccess(raw, action, path);
|
||||
return raw;
|
||||
}
|
||||
|
||||
private String postWithAuthorization(String path, String authorization, Map<String, Object> body, String action) {
|
||||
String token = requireText(authorization, "紫鸟授权 token 未配置");
|
||||
RestClient.RequestBodySpec request = getRestClient().post()
|
||||
.uri(joinUrl(ziniaoProperties.getBaseUrl(), path))
|
||||
.headers(headers -> {
|
||||
headers.set("Authorization", token);
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
});
|
||||
if (body != null) {
|
||||
request.body(body);
|
||||
}
|
||||
String raw = request
|
||||
.retrieve()
|
||||
.body(String.class);
|
||||
validateSuccess(raw, action, path);
|
||||
return raw;
|
||||
}
|
||||
|
||||
private void validateSuccess(String raw, String action, String path) {
|
||||
try {
|
||||
JsonNode root = objectMapper.readTree(raw);
|
||||
if (!success(root)) {
|
||||
throw new BusinessException("紫鸟接口返回失败(" + action + ", " + path + "): " + Objects.toString(text(root.get("msg")), "未知错误"));
|
||||
}
|
||||
} catch (BusinessException ex) {
|
||||
throw ex;
|
||||
} catch (Exception ex) {
|
||||
throw new BusinessException("解析紫鸟接口响应失败(" + action + ", " + path + ")");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean success(JsonNode root) {
|
||||
String code = text(root.get("code"));
|
||||
if (code != null && !"0".equals(code)) {
|
||||
return false;
|
||||
}
|
||||
String status = text(root.get("status"));
|
||||
Long ret = longValue(root.get("ret"));
|
||||
if ((ret != null && ret != 0L) || (status != null && !"success".equalsIgnoreCase(status))) {
|
||||
return false;
|
||||
}
|
||||
JsonNode wrapper = root.get("data");
|
||||
if (wrapper != null && wrapper.isObject()) {
|
||||
String wrapperStatus = text(wrapper.get("status"));
|
||||
Long wrapperRet = longValue(wrapper.get("ret"));
|
||||
if ((wrapperRet != null && wrapperRet != 0L) || (wrapperStatus != null && !"success".equalsIgnoreCase(wrapperStatus))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private RestClient getRestClient() {
|
||||
@@ -46,21 +249,20 @@ public class ZiniaoClientImpl implements ZiniaoClient {
|
||||
return RestClient.builder().requestFactory(requestFactory).build();
|
||||
}
|
||||
|
||||
private void addStaffItem(List<ZiniaoStaffItemVo> items, JsonNode itemNode) {
|
||||
ZiniaoStaffItemVo item = new ZiniaoStaffItemVo();
|
||||
item.setUserId(longValue(firstNonNull(itemNode.get("userId"), itemNode.get("user_id"))));
|
||||
item.setUsername(text(firstNonNull(itemNode.get("username"), itemNode.get("userName"))));
|
||||
item.setName(text(itemNode.get("name")));
|
||||
if (item.getUserId() != null) {
|
||||
items.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
private List<ZiniaoShopCacheDto> parseShops(String raw) {
|
||||
try {
|
||||
JsonNode root = objectMapper.readTree(raw);
|
||||
String code = text(root.get("code"));
|
||||
if (code != null && !"0".equals(code)) {
|
||||
throw new BusinessException("紫鸟店铺接口返回失败: " + Objects.toString(text(root.get("msg")), "未知错误"));
|
||||
}
|
||||
JsonNode wrapper = root.get("data");
|
||||
if (wrapper != null && wrapper.isObject()) {
|
||||
String status = text(wrapper.get("status"));
|
||||
Long ret = longValue(wrapper.get("ret"));
|
||||
if ((ret != null && ret != 0L) || (status != null && !"success".equalsIgnoreCase(status))) {
|
||||
throw new BusinessException("紫鸟店铺接口业务失败: " + Objects.toString(status, "unknown"));
|
||||
}
|
||||
}
|
||||
JsonNode itemsNode = wrapper == null ? null : wrapper.get("data");
|
||||
List<ZiniaoShopCacheDto> items = new ArrayList<>();
|
||||
if (itemsNode != null && itemsNode.isArray()) {
|
||||
@@ -75,12 +277,33 @@ public class ZiniaoClientImpl implements ZiniaoClient {
|
||||
}
|
||||
}
|
||||
return items;
|
||||
} catch (BusinessException ex) {
|
||||
throw ex;
|
||||
} catch (Exception ex) {
|
||||
throw new BusinessException("解析紫鸟店铺列表失败");
|
||||
}
|
||||
}
|
||||
private List<ZiniaoShopCacheDto> parseUserStores(String raw) {
|
||||
try {
|
||||
JsonNode root = objectMapper.readTree(raw);
|
||||
JsonNode itemsNode = firstNonNull(root.get("data"), root.get("result"));
|
||||
List<ZiniaoShopCacheDto> items = new ArrayList<>();
|
||||
if (itemsNode != null && itemsNode.isArray()) {
|
||||
for (JsonNode itemNode : itemsNode) {
|
||||
ZiniaoShopCacheDto item = new ZiniaoShopCacheDto();
|
||||
item.setShopId(text(firstNonNull(itemNode.get("id"), itemNode.get("shopId"), itemNode.get("accountId"), itemNode.get("account_id"))));
|
||||
item.setShopName(text(firstNonNull(itemNode.get("name"), itemNode.get("shopName"), itemNode.get("accountName"), itemNode.get("account_name"))));
|
||||
item.setPlatform(text(firstNonNull(itemNode.get("platform"), itemNode.get("platformName"), itemNode.get("site"), itemNode.get("siteName"))));
|
||||
if (item.getShopId() != null && !item.getShopId().isBlank()) {
|
||||
items.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
return items;
|
||||
} catch (Exception ex) {
|
||||
throw new BusinessException("解析紫鸟员工店铺列表失败");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private String joinUrl(String baseUrl, String path) {
|
||||
String base = Objects.toString(baseUrl, "").trim();
|
||||
@@ -121,4 +344,11 @@ public class ZiniaoClientImpl implements ZiniaoClient {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String requireText(String value, String message) {
|
||||
if (value == null || value.isBlank() || "change-me".equalsIgnoreCase(value.trim())) {
|
||||
throw new BusinessException(message);
|
||||
}
|
||||
return value.trim();
|
||||
}
|
||||
}
|
||||
|
||||
+24
-54
@@ -1,12 +1,11 @@
|
||||
package com.nanri.aiimage.modules.ziniao.controller;
|
||||
|
||||
import com.nanri.aiimage.common.api.ApiResponse;
|
||||
import com.nanri.aiimage.modules.ziniao.model.dto.ZiniaoSwitchShopRequest;
|
||||
import com.nanri.aiimage.modules.ziniao.model.vo.ZiniaoCurrentShopVo;
|
||||
import com.nanri.aiimage.modules.ziniao.model.vo.ZiniaoLoginUrlVo;
|
||||
import com.nanri.aiimage.modules.ziniao.model.dto.ZiniaoOpenShopRequest;
|
||||
import com.nanri.aiimage.modules.ziniao.model.vo.ZiniaoOpenShopVo;
|
||||
import com.nanri.aiimage.modules.ziniao.model.vo.ZiniaoSessionVo;
|
||||
import com.nanri.aiimage.modules.ziniao.model.vo.ZiniaoShopListVo;
|
||||
import com.nanri.aiimage.modules.ziniao.model.vo.ZiniaoSwitchShopVo;
|
||||
import com.nanri.aiimage.modules.ziniao.model.vo.ZiniaoStaffListVo;
|
||||
import com.nanri.aiimage.modules.ziniao.service.ZiniaoAuthService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
@@ -15,8 +14,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
@@ -27,71 +24,44 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/api/ziniao")
|
||||
@Tag(name = "紫鸟接入", description = "紫鸟 ApiKey 模式下的配置状态、店铺列表、当前店铺与切换店铺接口")
|
||||
@Tag(name = "紫鸟接入", description = "基于 API Key 直连紫鸟接口,获取 appToken、员工、店铺并生成打开店铺链接")
|
||||
public class ZiniaoAuthController {
|
||||
|
||||
private final ZiniaoAuthService ziniaoAuthService;
|
||||
|
||||
@GetMapping("/login-url")
|
||||
@Operation(summary = "获取紫鸟接入信息", description = "兼容保留接口,返回当前 ApiKey 模式的接入信息和 sessionId。")
|
||||
@ApiResponses({
|
||||
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "获取成功", content = @Content(schema = @Schema(implementation = ZiniaoLoginUrlVo.class)))
|
||||
})
|
||||
public ApiResponse<ZiniaoLoginUrlVo> getLoginUrl() {
|
||||
return ApiResponse.success(ziniaoAuthService.getLoginUrl());
|
||||
}
|
||||
|
||||
@GetMapping("/callback")
|
||||
@Operation(summary = "兼容保留回调接口", description = "ApiKey 模式下无真实登录回调,该接口仅兼容旧调用并返回 302。")
|
||||
@ApiResponses({
|
||||
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "302", description = "重定向到兼容地址")
|
||||
})
|
||||
public ResponseEntity<Void> callback(@RequestParam(required = false) String code, @RequestParam(required = false) String state) {
|
||||
try {
|
||||
String redirectUrl = ziniaoAuthService.handleCallback(code, state);
|
||||
return ResponseEntity.status(302)
|
||||
.header(HttpHeaders.LOCATION, redirectUrl)
|
||||
.build();
|
||||
} catch (Exception ex) {
|
||||
return ResponseEntity.status(302)
|
||||
.header(HttpHeaders.LOCATION, ziniaoAuthService.buildFailureRedirect(ex.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/session")
|
||||
@Operation(summary = "获取紫鸟接入状态", description = "根据 sessionId 获取当前 ApiKey 模式会话与当前店铺信息。")
|
||||
@Operation(summary = "获取紫鸟会话状态", description = "自动获取或复用 appToken,并返回 companyId、当前员工 userId、脱敏 token 和当前店铺信息。")
|
||||
@ApiResponses({
|
||||
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "获取成功", content = @Content(schema = @Schema(implementation = ZiniaoSessionVo.class)))
|
||||
})
|
||||
public ApiResponse<ZiniaoSessionVo> getSession(@RequestParam String sessionId) {
|
||||
return ApiResponse.success(ziniaoAuthService.getSession(sessionId));
|
||||
public ApiResponse<ZiniaoSessionVo> getSession(@RequestParam(required = false) String sessionId, @RequestParam(required = false) Long userId) {
|
||||
return ApiResponse.success(ziniaoAuthService.getSession(sessionId, userId));
|
||||
}
|
||||
|
||||
@GetMapping("/staff")
|
||||
@Operation(summary = "获取紫鸟员工列表", description = "使用 API Key 直连紫鸟接口,查询当前 companyId 下的员工列表,供前端绑定 employee userId。")
|
||||
@ApiResponses({
|
||||
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "获取成功", content = @Content(schema = @Schema(implementation = ZiniaoStaffListVo.class)))
|
||||
})
|
||||
public ApiResponse<ZiniaoStaffListVo> listStaff() {
|
||||
return ApiResponse.success(ziniaoAuthService.listStaff());
|
||||
}
|
||||
|
||||
@GetMapping("/shops")
|
||||
@Operation(summary = "获取紫鸟店铺列表", description = "根据 sessionId 使用配置的 ApiKey 与 companyId 拉取最新店铺列表。")
|
||||
@Operation(summary = "获取员工可见店铺列表", description = "先通过 API Key 获取 companyId,再按员工 userId 查询该员工有权限的店铺列表,并缓存到当前 session。")
|
||||
@ApiResponses({
|
||||
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "获取成功", content = @Content(schema = @Schema(implementation = ZiniaoShopListVo.class)))
|
||||
})
|
||||
public ApiResponse<ZiniaoShopListVo> listShops(@RequestParam String sessionId) {
|
||||
return ApiResponse.success(ziniaoAuthService.listShops(sessionId));
|
||||
public ApiResponse<ZiniaoShopListVo> listShops(@RequestParam String sessionId, @RequestParam Long userId) {
|
||||
return ApiResponse.success(ziniaoAuthService.listShops(sessionId, userId));
|
||||
}
|
||||
|
||||
@GetMapping("/shops/current")
|
||||
@Operation(summary = "获取当前紫鸟店铺", description = "根据 sessionId 获取当前已选中的店铺。")
|
||||
@PostMapping("/shops/open")
|
||||
@Operation(summary = "生成店铺打开链接", description = "按指定员工 userId 和 shopId 获取员工登录 token,并返回可直接拉起紫鸟客户端的 openStoreUrl。")
|
||||
@ApiResponses({
|
||||
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "获取成功", content = @Content(schema = @Schema(implementation = ZiniaoCurrentShopVo.class)))
|
||||
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "打开链接生成成功", content = @Content(schema = @Schema(implementation = ZiniaoOpenShopVo.class)))
|
||||
})
|
||||
public ApiResponse<ZiniaoCurrentShopVo> getCurrentShop(@RequestParam String sessionId) {
|
||||
return ApiResponse.success(ziniaoAuthService.getCurrentShop(sessionId));
|
||||
}
|
||||
|
||||
@PostMapping("/shops/switch")
|
||||
@Operation(summary = "切换当前紫鸟店铺", description = "根据 sessionId 和 shopId 切换当前选中的店铺。")
|
||||
@ApiResponses({
|
||||
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "切换成功", content = @Content(schema = @Schema(implementation = ZiniaoSwitchShopVo.class)))
|
||||
})
|
||||
public ApiResponse<ZiniaoSwitchShopVo> switchShop(@Valid @RequestBody ZiniaoSwitchShopRequest request) {
|
||||
return ApiResponse.success(ziniaoAuthService.switchShop(request));
|
||||
public ApiResponse<ZiniaoOpenShopVo> openShop(@Valid @RequestBody ZiniaoOpenShopRequest request) {
|
||||
return ApiResponse.success(ziniaoAuthService.openShop(request));
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+2
@@ -11,5 +11,7 @@ public class ZiniaoSessionCacheDto {
|
||||
private Long expireAt;
|
||||
private String ziniaoUserId;
|
||||
private String nickname;
|
||||
private Long companyId;
|
||||
private Long currentUserId;
|
||||
private String defaultShopId;
|
||||
}
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.nanri.aiimage.modules.ziniao.model.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ZiniaoOpenShopRequest {
|
||||
@NotBlank(message = "sessionId 不能为空")
|
||||
private String sessionId;
|
||||
|
||||
@NotNull(message = "userId 不能为空")
|
||||
private Long userId;
|
||||
|
||||
@NotBlank(message = "shopId 不能为空")
|
||||
private String shopId;
|
||||
}
|
||||
+4
@@ -1,6 +1,7 @@
|
||||
package com.nanri.aiimage.modules.ziniao.model.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@@ -8,6 +9,9 @@ public class ZiniaoSwitchShopRequest {
|
||||
@NotBlank(message = "sessionId 不能为空")
|
||||
private String sessionId;
|
||||
|
||||
@NotNull(message = "userId 不能为空")
|
||||
private Long userId;
|
||||
|
||||
@NotBlank(message = "shopId 不能为空")
|
||||
private String shopId;
|
||||
}
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.nanri.aiimage.modules.ziniao.model.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "紫鸟打开店铺结果")
|
||||
public class ZiniaoOpenShopVo {
|
||||
@Schema(description = "店铺ID")
|
||||
private String shopId;
|
||||
|
||||
@Schema(description = "店铺名称")
|
||||
private String shopName;
|
||||
|
||||
@Schema(description = "员工用户ID")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "通过 API Key 调用接口获取的员工登录 token")
|
||||
private String loginToken;
|
||||
|
||||
@Schema(description = "打开店铺URL")
|
||||
private String openStoreUrl;
|
||||
}
|
||||
+16
-1
@@ -9,15 +9,30 @@ public class ZiniaoSessionVo {
|
||||
@Schema(description = "会话 ID")
|
||||
private String sessionId;
|
||||
|
||||
@Schema(description = "是否已认证")
|
||||
@Schema(description = "是否已启用")
|
||||
private Boolean enabled;
|
||||
|
||||
@Schema(description = "是否已获取应用 token")
|
||||
private Boolean authenticated;
|
||||
|
||||
@Schema(description = "公司 ID")
|
||||
private Long companyId;
|
||||
|
||||
@Schema(description = "当前员工 userId")
|
||||
private Long currentUserId;
|
||||
|
||||
@Schema(description = "紫鸟用户 ID")
|
||||
private String ziniaoUserId;
|
||||
|
||||
@Schema(description = "紫鸟昵称")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "脱敏后的 appToken")
|
||||
private String appToken;
|
||||
|
||||
@Schema(description = "token 类型")
|
||||
private String tokenType;
|
||||
|
||||
@Schema(description = "会话过期时间戳(毫秒)")
|
||||
private Long expireAt;
|
||||
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.nanri.aiimage.modules.ziniao.model.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "紫鸟员工")
|
||||
public class ZiniaoStaffItemVo {
|
||||
@Schema(description = "员工用户ID")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "员工账号")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "员工姓名")
|
||||
private String name;
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.nanri.aiimage.modules.ziniao.model.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Schema(description = "紫鸟员工列表")
|
||||
public class ZiniaoStaffListVo {
|
||||
@Schema(description = "员工列表")
|
||||
private List<ZiniaoStaffItemVo> items = new ArrayList<>();
|
||||
}
|
||||
+145
-110
@@ -5,22 +5,22 @@ import com.nanri.aiimage.config.ZiniaoProperties;
|
||||
import com.nanri.aiimage.modules.ziniao.client.ZiniaoClient;
|
||||
import com.nanri.aiimage.modules.ziniao.model.cache.ZiniaoSessionCacheDto;
|
||||
import com.nanri.aiimage.modules.ziniao.model.cache.ZiniaoShopCacheDto;
|
||||
import com.nanri.aiimage.modules.ziniao.model.dto.ZiniaoSwitchShopRequest;
|
||||
import com.nanri.aiimage.modules.ziniao.model.vo.ZiniaoCurrentShopVo;
|
||||
import com.nanri.aiimage.modules.ziniao.model.vo.ZiniaoLoginUrlVo;
|
||||
import com.nanri.aiimage.modules.ziniao.model.dto.ZiniaoOpenShopRequest;
|
||||
import com.nanri.aiimage.modules.ziniao.model.vo.ZiniaoOpenShopVo;
|
||||
import com.nanri.aiimage.modules.ziniao.model.vo.ZiniaoSessionVo;
|
||||
import com.nanri.aiimage.modules.ziniao.model.vo.ZiniaoShopItemVo;
|
||||
import com.nanri.aiimage.modules.ziniao.model.vo.ZiniaoShopListVo;
|
||||
import com.nanri.aiimage.modules.ziniao.model.vo.ZiniaoSwitchShopVo;
|
||||
import com.nanri.aiimage.modules.ziniao.model.vo.ZiniaoStaffItemVo;
|
||||
import com.nanri.aiimage.modules.ziniao.model.vo.ZiniaoStaffListVo;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.Base64;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@@ -30,163 +30,150 @@ public class ZiniaoAuthService {
|
||||
private final ZiniaoClient ziniaoClient;
|
||||
private final ZiniaoSessionCacheService ziniaoSessionCacheService;
|
||||
|
||||
public ZiniaoLoginUrlVo getLoginUrl() {
|
||||
public ZiniaoSessionVo getSession(String sessionId, Long userId) {
|
||||
ensureEnabled();
|
||||
String sessionId = ensureSession();
|
||||
ZiniaoLoginUrlVo vo = new ZiniaoLoginUrlVo();
|
||||
vo.setState(sessionId);
|
||||
vo.setExpireAt(Instant.now().plusSeconds(ziniaoProperties.getSessionTtlHours() * 3600).toEpochMilli());
|
||||
vo.setLoginUrl(ziniaoProperties.getBaseUrl());
|
||||
return vo;
|
||||
}
|
||||
|
||||
public String handleCallback(String code, String state) {
|
||||
ensureEnabled();
|
||||
String sessionId = ensureSession();
|
||||
return ziniaoProperties.getBaseUrl() + "?sessionId=" + sessionId;
|
||||
}
|
||||
|
||||
public ZiniaoSessionVo getSession(String sessionId) {
|
||||
ZiniaoSessionCacheDto session = requireSession(sessionId);
|
||||
List<ZiniaoShopCacheDto> shops = ziniaoSessionCacheService.getShops(sessionId);
|
||||
ZiniaoShopCacheDto currentShop = getOrInitCurrentShop(sessionId, shops);
|
||||
ZiniaoSessionCacheDto session = sessionId == null || sessionId.isBlank() ? initSession(userId) : requireOrInitSession(sessionId, userId);
|
||||
ZiniaoSessionVo vo = new ZiniaoSessionVo();
|
||||
vo.setSessionId(session.getSessionId());
|
||||
vo.setAuthenticated(true);
|
||||
vo.setEnabled(ziniaoProperties.isEnabled());
|
||||
vo.setAuthenticated(session.getAccessToken() != null && !session.getAccessToken().isBlank());
|
||||
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.setExpireAt(session.getExpireAt());
|
||||
vo.setShopCount(shops.size());
|
||||
if (currentShop != null) {
|
||||
vo.setCurrentShopId(currentShop.getShopId());
|
||||
vo.setCurrentShopName(currentShop.getShopName());
|
||||
}
|
||||
vo.setShopCount(ziniaoSessionCacheService.getShops(session.getSessionId()).size());
|
||||
vo.setCurrentShopId(session.getDefaultShopId());
|
||||
vo.setCurrentShopName(resolveCurrentShopName(session.getSessionId(), session.getDefaultShopId()));
|
||||
return vo;
|
||||
}
|
||||
|
||||
public ZiniaoShopListVo listShops(String sessionId) {
|
||||
requireSession(sessionId);
|
||||
List<ZiniaoShopCacheDto> shops = ziniaoClient.listShops();
|
||||
ziniaoSessionCacheService.saveShops(sessionId, shops);
|
||||
ZiniaoShopCacheDto currentShop = getOrInitCurrentShop(sessionId, shops);
|
||||
public ZiniaoStaffListVo listStaff() {
|
||||
ensureEnabled();
|
||||
ZiniaoStaffListVo vo = new ZiniaoStaffListVo();
|
||||
Long companyId = resolveCompanyIdForStaff();
|
||||
vo.getItems().addAll(ziniaoClient.listStaff(companyId));
|
||||
return vo;
|
||||
}
|
||||
|
||||
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);
|
||||
ziniaoSessionCacheService.saveShops(session.getSessionId(), shops);
|
||||
if (!shops.isEmpty() && (session.getDefaultShopId() == null || session.getDefaultShopId().isBlank())) {
|
||||
session.setDefaultShopId(shops.get(0).getShopId());
|
||||
ziniaoSessionCacheService.saveSession(session);
|
||||
}
|
||||
ZiniaoShopListVo vo = new ZiniaoShopListVo();
|
||||
for (ZiniaoShopCacheDto shop : shops) {
|
||||
ZiniaoShopItemVo item = new ZiniaoShopItemVo();
|
||||
item.setShopId(shop.getShopId());
|
||||
item.setShopName(shop.getShopName());
|
||||
item.setPlatform(shop.getPlatform());
|
||||
item.setSelected(currentShop != null && shop.getShopId().equals(currentShop.getShopId()));
|
||||
item.setSelected(shop.getShopId() != null && shop.getShopId().equals(session.getDefaultShopId()));
|
||||
vo.getItems().add(item);
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
|
||||
public ZiniaoCurrentShopVo getCurrentShop(String sessionId) {
|
||||
requireSession(sessionId);
|
||||
ZiniaoShopCacheDto currentShop = getOrInitCurrentShop(sessionId, ziniaoSessionCacheService.getShops(sessionId));
|
||||
if (currentShop == null) {
|
||||
throw new BusinessException("当前会话没有可用店铺");
|
||||
public ZiniaoOpenShopVo openShop(ZiniaoOpenShopRequest request) {
|
||||
ZiniaoSessionCacheDto session = requireOrInitSession(request.getSessionId(), request.getUserId());
|
||||
Long currentUserId = requireUserId(request.getUserId() != null ? request.getUserId() : session.getCurrentUserId());
|
||||
String userToken = ziniaoClient.getUserLoginToken(resolveCompanyId(), currentUserId);
|
||||
List<ZiniaoShopCacheDto> shops = ziniaoSessionCacheService.getShops(session.getSessionId());
|
||||
if (shops.isEmpty()) {
|
||||
shops = ziniaoClient.listUserStores(resolveCompanyId(), currentUserId, userToken);
|
||||
ziniaoSessionCacheService.saveShops(session.getSessionId(), shops);
|
||||
}
|
||||
ZiniaoCurrentShopVo vo = new ZiniaoCurrentShopVo();
|
||||
vo.setShopId(currentShop.getShopId());
|
||||
vo.setShopName(currentShop.getShopName());
|
||||
vo.setPlatform(currentShop.getPlatform());
|
||||
vo.setSelectedAt(currentShop.getSelectedAt());
|
||||
return vo;
|
||||
}
|
||||
|
||||
public ZiniaoSwitchShopVo switchShop(ZiniaoSwitchShopRequest request) {
|
||||
requireSession(request.getSessionId());
|
||||
List<ZiniaoShopCacheDto> shops = ziniaoSessionCacheService.getShops(request.getSessionId());
|
||||
ZiniaoShopCacheDto targetShop = shops.stream()
|
||||
.filter(item -> item.getShopId() != null && item.getShopId().equals(request.getShopId()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new BusinessException("店铺不存在或不属于当前会话"));
|
||||
targetShop.setSelectedAt(Instant.now().toEpochMilli());
|
||||
ziniaoSessionCacheService.saveCurrentShop(request.getSessionId(), targetShop);
|
||||
ZiniaoSwitchShopVo vo = new ZiniaoSwitchShopVo();
|
||||
vo.setSuccess(true);
|
||||
vo.setCurrentShopId(targetShop.getShopId());
|
||||
vo.setCurrentShopName(targetShop.getShopName());
|
||||
vo.setOpenStoreUrl(buildOpenStoreUrl(targetShop));
|
||||
.orElseThrow(() -> new BusinessException("店铺不存在或不属于当前员工"));
|
||||
session.setCurrentUserId(currentUserId);
|
||||
session.setDefaultShopId(targetShop.getShopId());
|
||||
ziniaoSessionCacheService.saveSession(session);
|
||||
ziniaoSessionCacheService.saveCurrentShop(session.getSessionId(), targetShop);
|
||||
|
||||
ZiniaoOpenShopVo vo = new ZiniaoOpenShopVo();
|
||||
vo.setUserId(currentUserId);
|
||||
vo.setShopId(targetShop.getShopId());
|
||||
vo.setShopName(targetShop.getShopName());
|
||||
vo.setLoginToken(userToken);
|
||||
vo.setOpenStoreUrl(buildOpenStoreUrl(targetShop.getShopId(), currentUserId, userToken));
|
||||
return vo;
|
||||
}
|
||||
|
||||
public String buildFailureRedirect(String message) {
|
||||
return ziniaoProperties.getBaseUrl() + "?message=" + (message == null || message.isBlank() ? "紫鸟授权失败" : message);
|
||||
public List<ZiniaoShopCacheDto> listShopsForConfiguredUser() {
|
||||
ensureEnabled();
|
||||
Long userId = parseConfiguredOpenStoreUserId();
|
||||
String userToken = ziniaoClient.getUserLoginToken(resolveCompanyId(), userId);
|
||||
return ziniaoClient.listUserStores(resolveCompanyId(), userId, userToken);
|
||||
}
|
||||
|
||||
private String ensureSession() {
|
||||
public String buildOpenStoreUrlForShop(ZiniaoShopCacheDto shop) {
|
||||
ensureEnabled();
|
||||
if (shop == null || shop.getShopId() == null || shop.getShopId().isBlank()) {
|
||||
throw new BusinessException("店铺不存在");
|
||||
}
|
||||
Long userId = parseConfiguredOpenStoreUserId();
|
||||
String loginToken = ziniaoClient.getUserLoginToken(resolveCompanyId(), userId);
|
||||
return buildOpenStoreUrl(shop.getShopId(), userId, loginToken);
|
||||
}
|
||||
|
||||
private ZiniaoSessionCacheDto initSession(Long userId) {
|
||||
String sessionId = "ziniao_" + UUID.randomUUID().toString().replace("-", "");
|
||||
ZiniaoSessionCacheDto session = new ZiniaoSessionCacheDto();
|
||||
session.setSessionId(sessionId);
|
||||
session.setAccessToken(maskApiKey(ziniaoProperties.getApiKey()));
|
||||
String appToken = ziniaoClient.getAppToken();
|
||||
session.setAccessToken(maskToken(appToken));
|
||||
session.setTokenType("Bearer");
|
||||
session.setExpireAt(Instant.now().plusSeconds(ziniaoProperties.getSessionTtlHours() * 3600).toEpochMilli());
|
||||
session.setZiniaoUserId(String.valueOf(defaultCompanyId()));
|
||||
session.setNickname("ApiKey模式");
|
||||
session.setCompanyId(resolveCompanyId());
|
||||
session.setCurrentUserId(userId);
|
||||
session.setZiniaoUserId(String.valueOf(userId == null ? 0L : userId));
|
||||
session.setNickname("API Key模式");
|
||||
ziniaoSessionCacheService.saveSession(session);
|
||||
return sessionId;
|
||||
return session;
|
||||
}
|
||||
|
||||
private ZiniaoSessionCacheDto requireSession(String sessionId) {
|
||||
ensureEnabled();
|
||||
private ZiniaoSessionCacheDto requireOrInitSession(String sessionId, Long userId) {
|
||||
if (sessionId == null || sessionId.isBlank()) {
|
||||
throw new BusinessException("sessionId 不能为空");
|
||||
return initSession(userId);
|
||||
}
|
||||
ZiniaoSessionCacheDto session = ziniaoSessionCacheService.getSession(sessionId);
|
||||
if (session == null) {
|
||||
throw new BusinessException("当前会话不存在或已过期");
|
||||
return initSession(userId);
|
||||
}
|
||||
if (userId != null && userId > 0) {
|
||||
session.setCurrentUserId(userId);
|
||||
session.setZiniaoUserId(String.valueOf(userId));
|
||||
ziniaoSessionCacheService.saveSession(session);
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
private ZiniaoShopCacheDto getOrInitCurrentShop(String sessionId, List<ZiniaoShopCacheDto> shops) {
|
||||
ZiniaoShopCacheDto current = ziniaoSessionCacheService.getCurrentShop(sessionId);
|
||||
if (current != null) {
|
||||
return current;
|
||||
}
|
||||
if (shops == null || shops.isEmpty()) {
|
||||
private String resolveCurrentShopName(String sessionId, String currentShopId) {
|
||||
if (currentShopId == null || currentShopId.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
ZiniaoShopCacheDto first = shops.get(0);
|
||||
first.setSelectedAt(Instant.now().toEpochMilli());
|
||||
ziniaoSessionCacheService.saveCurrentShop(sessionId, first);
|
||||
return first;
|
||||
return ziniaoSessionCacheService.getShops(sessionId).stream()
|
||||
.filter(item -> currentShopId.equals(item.getShopId()))
|
||||
.map(ZiniaoShopCacheDto::getShopName)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private void ensureEnabled() {
|
||||
if (!ziniaoProperties.isEnabled()) {
|
||||
throw new BusinessException("紫鸟集成未启用,请先配置环境变量");
|
||||
}
|
||||
if (ziniaoProperties.getApiKey() == null || ziniaoProperties.getApiKey().isBlank() || "change-me".equalsIgnoreCase(ziniaoProperties.getApiKey())) {
|
||||
throw new BusinessException("紫鸟 ApiKey 未配置");
|
||||
}
|
||||
}
|
||||
|
||||
private long defaultCompanyId() {
|
||||
return ziniaoProperties.getCompanyId() == null ? 0L : ziniaoProperties.getCompanyId();
|
||||
}
|
||||
|
||||
private String maskApiKey(String apiKey) {
|
||||
if (apiKey == null || apiKey.isBlank()) {
|
||||
return "";
|
||||
}
|
||||
if (apiKey.length() <= 8) {
|
||||
return apiKey;
|
||||
}
|
||||
return apiKey.substring(0, 4) + "****" + apiKey.substring(apiKey.length() - 4);
|
||||
}
|
||||
|
||||
private String buildOpenStoreUrl(ZiniaoShopCacheDto shop) {
|
||||
private String buildOpenStoreUrl(String storeId, Long userId, String loginToken) {
|
||||
String scheme = requireText(ziniaoProperties.getOpenStoreScheme(), "紫鸟 open-store-scheme 未配置");
|
||||
String userId = requireText(ziniaoProperties.getOpenStoreUserId(), "紫鸟 open-store-user-id 未配置");
|
||||
String apiKey = requireText(ziniaoProperties.getApiKey(), "紫鸟 ApiKey 未配置");
|
||||
String launchUrl = requireText(ziniaoProperties.getOpenStoreLaunchUrl(), "紫鸟 open-store-launch-url 未配置");
|
||||
String encodedLaunchUrl = Base64.getEncoder().encodeToString(launchUrl.getBytes(StandardCharsets.UTF_8));
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(scheme)
|
||||
.queryParam("storeId", shop.getShopId())
|
||||
.queryParam("openapiToken", apiKey)
|
||||
.queryParam("storeId", storeId)
|
||||
.queryParam("openapiToken", loginToken)
|
||||
.queryParam("userId", userId)
|
||||
.queryParam("lanuchUrl", encodedLaunchUrl)
|
||||
.queryParam("autoopen", Boolean.TRUE.equals(ziniaoProperties.getOpenStoreAutoOpen()))
|
||||
@@ -202,8 +189,56 @@ public class ZiniaoAuthService {
|
||||
return builder.build(true).toUriString();
|
||||
}
|
||||
|
||||
private void ensureEnabled() {
|
||||
if (!ziniaoProperties.isEnabled()) {
|
||||
throw new BusinessException("紫鸟集成未启用,请先配置环境变量");
|
||||
}
|
||||
requireText(ziniaoProperties.getApiKey(), "紫鸟 apiKey 未配置");
|
||||
}
|
||||
|
||||
private Long resolveCompanyIdForStaff() {
|
||||
return resolveCompanyId();
|
||||
}
|
||||
|
||||
private Long requireUserId(Long userId) {
|
||||
if (userId == null || userId <= 0) {
|
||||
throw new BusinessException("请先选择紫鸟员工");
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
private Long resolveCompanyId() {
|
||||
if (ziniaoProperties.getCompanyId() != null && ziniaoProperties.getCompanyId() > 0) {
|
||||
return ziniaoProperties.getCompanyId();
|
||||
}
|
||||
return ziniaoClient.getCompanyIdByApiKey();
|
||||
}
|
||||
|
||||
private Long requireCompanyId() {
|
||||
return resolveCompanyId();
|
||||
}
|
||||
|
||||
private Long parseConfiguredOpenStoreUserId() {
|
||||
String configured = requireText(ziniaoProperties.getOpenStoreUserId(), "紫鸟 open-store-user-id 未配置");
|
||||
try {
|
||||
return Long.parseLong(configured);
|
||||
} catch (Exception ex) {
|
||||
throw new BusinessException("紫鸟 open-store-user-id 配置不合法");
|
||||
}
|
||||
}
|
||||
|
||||
private String maskToken(String token) {
|
||||
if (token == null || token.isBlank()) {
|
||||
return "";
|
||||
}
|
||||
if (token.length() <= 8) {
|
||||
return token;
|
||||
}
|
||||
return token.substring(0, 4) + "****" + token.substring(token.length() - 4);
|
||||
}
|
||||
|
||||
private String requireText(String value, String message) {
|
||||
if (value == null || value.isBlank()) {
|
||||
if (value == null || value.isBlank() || "change-me".equalsIgnoreCase(value.trim())) {
|
||||
throw new BusinessException(message);
|
||||
}
|
||||
return value.trim();
|
||||
|
||||
+17
@@ -17,10 +17,27 @@ 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()));
|
||||
|
||||
Reference in New Issue
Block a user