2026-04-28 16:08:07 +08:00
|
|
|
package com.xuqm.im.service;
|
|
|
|
|
|
|
|
|
|
import com.fasterxml.jackson.databind.JsonNode;
|
|
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
|
|
|
import org.springframework.http.HttpEntity;
|
|
|
|
|
import org.springframework.http.HttpHeaders;
|
|
|
|
|
import org.springframework.http.HttpMethod;
|
|
|
|
|
import org.springframework.http.ResponseEntity;
|
|
|
|
|
import org.springframework.stereotype.Component;
|
|
|
|
|
import org.springframework.web.client.RestTemplate;
|
|
|
|
|
import org.springframework.web.util.UriComponentsBuilder;
|
|
|
|
|
|
|
|
|
|
@Component
|
|
|
|
|
public class ImFeatureConfigClient {
|
|
|
|
|
|
|
|
|
|
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
|
|
|
|
|
|
|
|
|
private final RestTemplate restTemplate;
|
|
|
|
|
|
|
|
|
|
@Value("${im.tenant-service-url:http://192.168.116.9:8081}")
|
|
|
|
|
private String tenantServiceUrl;
|
|
|
|
|
|
|
|
|
|
@Value("${im.internal-token:xuqm-internal-token}")
|
|
|
|
|
private String internalToken;
|
|
|
|
|
|
|
|
|
|
public ImFeatureConfigClient() {
|
|
|
|
|
this.restTemplate = new RestTemplate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean allowStrangerMessage(String appId) {
|
2026-04-29 12:33:25 +08:00
|
|
|
return readConfig(appId).path("allowStrangerMessage").asBoolean(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean allowFriendRequest(String appId) {
|
|
|
|
|
return readConfig(appId).path("allowFriendRequest").asBoolean(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public String friendRequestMode(String appId) {
|
|
|
|
|
JsonNode node = readConfig(appId);
|
|
|
|
|
String mode = node.path("friendRequestMode").asText("");
|
|
|
|
|
String normalized = mode == null ? "" : mode.trim().toUpperCase();
|
|
|
|
|
return switch (normalized) {
|
|
|
|
|
case "DIRECT_ACCEPT", "DISALLOW", "REQUIRE_CONFIRM" -> normalized;
|
|
|
|
|
default -> node.path("allowFriendRequest").asBoolean(true) ? "REQUIRE_CONFIRM" : "DISALLOW";
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean allowGroupJoinRequest(String appId) {
|
|
|
|
|
return readConfig(appId).path("allowGroupJoinRequest").asBoolean(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean blacklistSendSuccess(String appId) {
|
|
|
|
|
return readConfig(appId).path("blacklistSendSuccess").asBoolean(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int messageRecallMinutes(String appId) {
|
|
|
|
|
return Math.max(readConfig(appId).path("messageRecallMinutes").asInt(2), 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int historyRetentionDays(String appId) {
|
|
|
|
|
return Math.max(readConfig(appId).path("historyRetentionDays").asInt(7), 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int conversationPullLimit(String appId) {
|
|
|
|
|
return Math.min(Math.max(readConfig(appId).path("conversationPullLimit").asInt(100), 1), 500);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean multiClientConversationDeleteSync(String appId) {
|
|
|
|
|
return readConfig(appId).path("multiClientConversationDeleteSync").asBoolean(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private JsonNode readConfig(String appId) {
|
2026-04-28 16:08:07 +08:00
|
|
|
String url = UriComponentsBuilder.fromHttpUrl(tenantServiceUrl)
|
|
|
|
|
.path("/api/internal/sdk/apps/{appId}/services/{platform}/{serviceType}")
|
|
|
|
|
.buildAndExpand(appId, "ANDROID", "IM")
|
|
|
|
|
.toUriString();
|
|
|
|
|
HttpHeaders headers = new HttpHeaders();
|
|
|
|
|
headers.set("X-Internal-Token", internalToken);
|
|
|
|
|
try {
|
|
|
|
|
ResponseEntity<JsonNode> response = restTemplate.exchange(
|
|
|
|
|
url,
|
|
|
|
|
HttpMethod.GET,
|
|
|
|
|
new HttpEntity<>(headers),
|
|
|
|
|
JsonNode.class
|
|
|
|
|
);
|
|
|
|
|
JsonNode body = response.getBody();
|
|
|
|
|
if (response.getStatusCode().is2xxSuccessful() && body != null && body.path("code").asInt() == 200) {
|
|
|
|
|
String config = body.path("data").path("config").asText("");
|
2026-04-29 12:33:25 +08:00
|
|
|
if (!config.isBlank()) {
|
|
|
|
|
JsonNode node = OBJECT_MAPPER.readTree(config);
|
|
|
|
|
return node == null ? OBJECT_MAPPER.createObjectNode() : node;
|
2026-04-28 16:08:07 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception e) {
|
2026-04-29 12:33:25 +08:00
|
|
|
// Fail closed: if config cannot be read, keep the feature disabled.
|
2026-04-28 16:08:07 +08:00
|
|
|
}
|
2026-04-29 12:33:25 +08:00
|
|
|
return OBJECT_MAPPER.createObjectNode();
|
2026-04-28 16:08:07 +08:00
|
|
|
}
|
|
|
|
|
}
|