2026-04-25 16:41:10 +08:00
|
|
|
package com.xuqm.tenant.controller;
|
|
|
|
|
|
2026-04-29 17:35:52 +08:00
|
|
|
import com.fasterxml.jackson.databind.JsonNode;
|
|
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
2026-04-25 16:41:10 +08:00
|
|
|
import com.xuqm.common.model.ApiResponse;
|
2026-04-27 23:41:58 +08:00
|
|
|
import com.xuqm.tenant.entity.AppEntity;
|
2026-04-25 16:41:10 +08:00
|
|
|
import com.xuqm.tenant.entity.FeatureServiceEntity;
|
|
|
|
|
import com.xuqm.tenant.repository.FeatureServiceRepository;
|
2026-04-27 23:41:58 +08:00
|
|
|
import com.xuqm.tenant.service.SdkAppProvisioningService;
|
2026-04-25 16:41:10 +08:00
|
|
|
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;
|
2026-04-27 23:41:58 +08:00
|
|
|
private final SdkAppProvisioningService sdkAppProvisioningService;
|
2026-04-29 17:35:52 +08:00
|
|
|
private final ObjectMapper objectMapper;
|
2026-04-25 16:41:10 +08:00
|
|
|
|
2026-04-27 19:23:11 +08:00
|
|
|
@Value("${sdk.im-ws-url:wss://im.dev.xuqinmin.com/ws/im}")
|
2026-04-25 16:41:10 +08:00
|
|
|
private String imWsUrl;
|
|
|
|
|
|
2026-04-30 11:47:01 +08:00
|
|
|
@Value("${sdk.file-service-url:https://file.dev.xuqinmin.com}")
|
2026-04-25 16:41:10 +08:00
|
|
|
private String fileServiceUrl;
|
|
|
|
|
|
2026-04-27 19:23:11 +08:00
|
|
|
@Value("${sdk.im-api-url:https://im.dev.xuqinmin.com}")
|
2026-04-25 16:41:10 +08:00
|
|
|
private String imApiUrl;
|
|
|
|
|
|
2026-04-27 23:41:58 +08:00
|
|
|
public SdkConfigController(FeatureServiceRepository featureServiceRepository,
|
2026-04-29 17:35:52 +08:00
|
|
|
SdkAppProvisioningService sdkAppProvisioningService,
|
|
|
|
|
ObjectMapper objectMapper) {
|
2026-04-25 16:41:10 +08:00
|
|
|
this.featureServiceRepository = featureServiceRepository;
|
2026-04-27 23:41:58 +08:00
|
|
|
this.sdkAppProvisioningService = sdkAppProvisioningService;
|
2026-04-29 17:35:52 +08:00
|
|
|
this.objectMapper = objectMapper;
|
2026-04-25 16:41:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2026-05-07 19:39:42 +08:00
|
|
|
* GET /api/sdk/config?appKey=XXX&platform=ANDROID — public, no auth required.
|
2026-04-25 16:41:10 +08:00
|
|
|
*
|
2026-05-07 19:39:42 +08:00
|
|
|
* Returns SDK configuration URLs and enabled feature flags for the given appKey/appKey.
|
2026-04-27 23:41:58 +08:00
|
|
|
* The demo app (`ak_demo_chat`) is auto-provisioned if it does not exist.
|
2026-04-29 17:35:52 +08:00
|
|
|
* For update releases, the platform-specific UPDATE row drives both the enabled flag and
|
|
|
|
|
* the default release configuration.
|
2026-04-25 16:41:10 +08:00
|
|
|
*/
|
|
|
|
|
@GetMapping("/config")
|
|
|
|
|
public ResponseEntity<ApiResponse<SdkConfigResponse>> getConfig(
|
2026-05-07 19:39:42 +08:00
|
|
|
@RequestParam String appKey,
|
2026-04-29 17:35:52 +08:00
|
|
|
@RequestParam(required = false, defaultValue = "ANDROID") FeatureServiceEntity.Platform platform) {
|
2026-04-25 16:41:10 +08:00
|
|
|
|
2026-05-07 19:39:42 +08:00
|
|
|
AppEntity app = sdkAppProvisioningService.resolveApp(appKey);
|
2026-05-08 18:32:00 +08:00
|
|
|
List<FeatureServiceEntity> features = featureServiceRepository.findByAppKey(app.getAppKey());
|
2026-04-25 16:41:10 +08:00
|
|
|
|
|
|
|
|
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());
|
2026-04-29 17:35:52 +08:00
|
|
|
JsonNode updateConfig = featureServiceRepository
|
2026-05-08 18:32:00 +08:00
|
|
|
.findByAppKeyAndPlatformAndServiceType(app.getAppKey(), platform, FeatureServiceEntity.ServiceType.UPDATE)
|
2026-04-29 17:35:52 +08:00
|
|
|
.map(feature -> parseConfig(feature.getConfig()))
|
|
|
|
|
.orElseGet(objectMapper::createObjectNode);
|
2026-05-05 17:54:59 +08:00
|
|
|
JsonNode pushConfig = featureServiceRepository
|
2026-05-08 18:32:00 +08:00
|
|
|
.findByAppKeyAndPlatformAndServiceType(app.getAppKey(), platform, FeatureServiceEntity.ServiceType.PUSH)
|
2026-05-05 17:54:59 +08:00
|
|
|
.map(feature -> parseConfig(feature.getConfig()))
|
|
|
|
|
.orElseGet(objectMapper::createObjectNode);
|
2026-04-29 17:35:52 +08:00
|
|
|
boolean updateEnabled = featureServiceRepository
|
2026-05-08 18:32:00 +08:00
|
|
|
.findByAppKeyAndPlatformAndServiceType(app.getAppKey(), platform, FeatureServiceEntity.ServiceType.UPDATE)
|
2026-04-29 17:35:52 +08:00
|
|
|
.map(FeatureServiceEntity::isEnabled)
|
|
|
|
|
.orElse(false);
|
2026-04-25 16:41:10 +08:00
|
|
|
|
|
|
|
|
SdkConfigResponse response = new SdkConfigResponse(
|
|
|
|
|
imWsUrl,
|
|
|
|
|
fileServiceUrl,
|
|
|
|
|
imApiUrl,
|
|
|
|
|
Map.of(
|
|
|
|
|
"im", imEnabled,
|
|
|
|
|
"push", pushEnabled,
|
|
|
|
|
"update", updateEnabled
|
2026-04-29 17:35:52 +08:00
|
|
|
),
|
|
|
|
|
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(""),
|
2026-05-05 17:54:59 +08:00
|
|
|
updateConfig.path("defaultMarketUrl").asText(""),
|
|
|
|
|
pushConfig
|
2026-04-25 16:41:10 +08:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return ResponseEntity.ok(ApiResponse.success(response));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public record SdkConfigResponse(
|
|
|
|
|
String imWsUrl,
|
|
|
|
|
String fileServiceUrl,
|
|
|
|
|
String imApiUrl,
|
2026-04-29 17:35:52 +08:00
|
|
|
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,
|
2026-05-05 17:54:59 +08:00
|
|
|
String updateDefaultMarketUrl,
|
|
|
|
|
JsonNode pushConfig
|
2026-04-25 16:41:10 +08:00
|
|
|
) {}
|
2026-04-29 17:35:52 +08:00
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
}
|
2026-04-25 16:41:10 +08:00
|
|
|
}
|