XuqmGroup-Server/tenant-service/src/main/java/com/xuqm/tenant/controller/SdkConfigController.java

157 行
6.7 KiB
Java

package com.xuqm.tenant.controller;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xuqm.common.model.ApiResponse;
import com.xuqm.tenant.entity.AppEntity;
import com.xuqm.tenant.entity.FeatureServiceEntity;
import com.xuqm.tenant.repository.FeatureServiceRepository;
import com.xuqm.tenant.service.SdkAppProvisioningService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/sdk")
public class SdkConfigController {
private final FeatureServiceRepository featureServiceRepository;
private final SdkAppProvisioningService sdkAppProvisioningService;
private final ObjectMapper objectMapper;
@Value("${sdk.im-ws-url:wss://im.dev.xuqinmin.com/ws/im}")
private String imWsUrl;
@Value("${sdk.file-service-url:https://file.dev.xuqinmin.com}")
private String fileServiceUrl;
@Value("${sdk.im-api-url:https://im.dev.xuqinmin.com}")
private String imApiUrl;
public SdkConfigController(FeatureServiceRepository featureServiceRepository,
SdkAppProvisioningService sdkAppProvisioningService,
ObjectMapper objectMapper) {
this.featureServiceRepository = featureServiceRepository;
this.sdkAppProvisioningService = sdkAppProvisioningService;
this.objectMapper = objectMapper;
}
/**
2026-05-07 19:39:42 +08:00
* GET /api/sdk/config?appKey=XXX&platform=ANDROID public, no auth required.
*
2026-05-07 19:39:42 +08:00
* Returns SDK configuration URLs and enabled feature flags for the given appKey/appKey.
* The demo app (`ak_demo_chat`) is auto-provisioned if it does not exist.
* For update releases, the platform-specific UPDATE row drives both the enabled flag and
* the default release configuration.
*/
@GetMapping("/config")
public ResponseEntity<ApiResponse<SdkConfigResponse>> getConfig(
2026-05-07 19:39:42 +08:00
@RequestParam String appKey,
@RequestParam(required = false, defaultValue = "ANDROID") FeatureServiceEntity.Platform platform) {
2026-05-07 19:39:42 +08:00
AppEntity app = sdkAppProvisioningService.resolveApp(appKey);
List<FeatureServiceEntity> features = featureServiceRepository.findByAppKey(app.getAppKey());
boolean imEnabled = features.stream()
.anyMatch(f -> f.getServiceType() == FeatureServiceEntity.ServiceType.IM && f.isEnabled());
boolean pushEnabled = features.stream()
.anyMatch(f -> f.getServiceType() == FeatureServiceEntity.ServiceType.PUSH && f.isEnabled());
JsonNode updateConfig = featureServiceRepository
.findByAppKeyAndPlatformAndServiceType(app.getAppKey(), platform, FeatureServiceEntity.ServiceType.UPDATE)
.map(feature -> parseConfig(feature.getConfig()))
.orElseGet(objectMapper::createObjectNode);
JsonNode pushConfig = featureServiceRepository
.findByAppKeyAndPlatformAndServiceType(app.getAppKey(), platform, FeatureServiceEntity.ServiceType.PUSH)
.map(feature -> parseConfig(feature.getConfig()))
.orElseGet(objectMapper::createObjectNode);
boolean updateEnabled = featureServiceRepository
.findByAppKeyAndPlatformAndServiceType(app.getAppKey(), platform, FeatureServiceEntity.ServiceType.UPDATE)
.map(FeatureServiceEntity::isEnabled)
.orElse(false);
SdkConfigResponse response = new SdkConfigResponse(
imWsUrl,
fileServiceUrl,
imApiUrl,
Map.of(
"im", imEnabled,
"push", pushEnabled,
"update", updateEnabled
),
updateEnabled,
updateConfig.path("defaultPublishMode").asText("MANUAL"),
updateConfig.path("defaultPublishImmediately").asBoolean(false),
updateConfig.path("defaultScheduledPublishAt").asText(""),
updateConfig.path("defaultAutoPublishAfterReview").asBoolean(false),
updateConfig.path("defaultWebhookUrl").asText(""),
csv(updateConfig.path("defaultStoreTargets")),
updateConfig.path("defaultForceUpdate").asBoolean(false),
updateConfig.path("defaultGrayEnabled").asBoolean(false),
updateConfig.path("defaultGrayPercent").asInt(0),
updateConfig.path("defaultPackageName").asText(""),
updateConfig.path("defaultAppStoreUrl").asText(""),
updateConfig.path("defaultMarketUrl").asText(""),
pushConfig
);
return ResponseEntity.ok(ApiResponse.success(response));
}
public record SdkConfigResponse(
String imWsUrl,
String fileServiceUrl,
String imApiUrl,
Map<String, Boolean> features,
boolean updateEnabled,
String updateDefaultPublishMode,
boolean updateDefaultPublishImmediately,
String updateDefaultScheduledPublishAt,
boolean updateDefaultAutoPublishAfterReview,
String updateDefaultWebhookUrl,
String updateDefaultStoreTargets,
boolean updateDefaultForceUpdate,
boolean updateDefaultGrayEnabled,
int updateDefaultGrayPercent,
String updateDefaultPackageName,
String updateDefaultAppStoreUrl,
String updateDefaultMarketUrl,
JsonNode pushConfig
) {}
private JsonNode parseConfig(String config) {
if (config == null || config.isBlank()) {
return objectMapper.createObjectNode();
}
try {
JsonNode node = objectMapper.readTree(config);
return node == null ? objectMapper.createObjectNode() : node;
} catch (Exception e) {
return objectMapper.createObjectNode();
}
}
private String csv(JsonNode node) {
if (node == null || !node.isArray()) {
return "";
}
StringBuilder sb = new StringBuilder();
for (JsonNode item : node) {
String value = item.asText(null);
if (value == null || value.isBlank()) {
continue;
}
if (!sb.isEmpty()) {
sb.append(',');
}
sb.append(value.trim());
}
return sb.toString();
}
}