chore: sync local changes
这个提交包含在:
父节点
9cb352bb99
当前提交
77dafd76bf
@ -13,11 +13,11 @@ public final class AppRequestSignatureUtil {
|
|||||||
private AppRequestSignatureUtil() {
|
private AppRequestSignatureUtil() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String payload(String appId,
|
public static String payload(String appKey,
|
||||||
String userId,
|
String userId,
|
||||||
long timestamp,
|
long timestamp,
|
||||||
String nonce) {
|
String nonce) {
|
||||||
return normalize(appId) + '\n'
|
return normalize(appKey) + '\n'
|
||||||
+ normalize(userId) + '\n'
|
+ normalize(userId) + '\n'
|
||||||
+ timestamp + '\n'
|
+ timestamp + '\n'
|
||||||
+ normalize(nonce);
|
+ normalize(nonce);
|
||||||
|
|||||||
@ -19,8 +19,8 @@ public class DemoAuthController {
|
|||||||
|
|
||||||
@PostMapping("/register")
|
@PostMapping("/register")
|
||||||
public ApiResponse<Map<String, Object>> register(@RequestBody RegisterRequest body) {
|
public ApiResponse<Map<String, Object>> register(@RequestBody RegisterRequest body) {
|
||||||
if (body.appId() == null || body.appId().isBlank()) {
|
if (body.appKey() == null || body.appKey().isBlank()) {
|
||||||
return ApiResponse.badRequest("appId is required");
|
return ApiResponse.badRequest("appKey is required");
|
||||||
}
|
}
|
||||||
if (body.userId() == null || body.userId().isBlank()) {
|
if (body.userId() == null || body.userId().isBlank()) {
|
||||||
return ApiResponse.badRequest("userId is required");
|
return ApiResponse.badRequest("userId is required");
|
||||||
@ -30,15 +30,15 @@ public class DemoAuthController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DemoAuthService.AuthResult result = authService.register(
|
DemoAuthService.AuthResult result = authService.register(
|
||||||
body.appId(), body.userId(), body.password(), body.nickname());
|
body.appKey(), body.userId(), body.password(), body.nickname());
|
||||||
|
|
||||||
return ApiResponse.success(buildResponse(result));
|
return ApiResponse.success(buildResponse(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/login")
|
@PostMapping("/login")
|
||||||
public ApiResponse<Map<String, Object>> login(@RequestBody LoginRequest body) {
|
public ApiResponse<Map<String, Object>> login(@RequestBody LoginRequest body) {
|
||||||
if (body.appId() == null || body.appId().isBlank()) {
|
if (body.appKey() == null || body.appKey().isBlank()) {
|
||||||
return ApiResponse.badRequest("appId is required");
|
return ApiResponse.badRequest("appKey is required");
|
||||||
}
|
}
|
||||||
if (body.userId() == null || body.userId().isBlank()) {
|
if (body.userId() == null || body.userId().isBlank()) {
|
||||||
return ApiResponse.badRequest("userId is required");
|
return ApiResponse.badRequest("userId is required");
|
||||||
@ -48,7 +48,7 @@ public class DemoAuthController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DemoAuthService.AuthResult result = authService.login(
|
DemoAuthService.AuthResult result = authService.login(
|
||||||
body.appId(), body.userId(), body.password());
|
body.appKey(), body.userId(), body.password());
|
||||||
|
|
||||||
return ApiResponse.success(buildResponse(result));
|
return ApiResponse.success(buildResponse(result));
|
||||||
}
|
}
|
||||||
@ -63,8 +63,8 @@ public class DemoAuthController {
|
|||||||
|
|
||||||
@PostMapping("/reset-password")
|
@PostMapping("/reset-password")
|
||||||
public ApiResponse<Void> resetPassword(@RequestBody ResetPasswordRequest body) {
|
public ApiResponse<Void> resetPassword(@RequestBody ResetPasswordRequest body) {
|
||||||
if (body.appId() == null || body.appId().isBlank()) {
|
if (body.appKey() == null || body.appKey().isBlank()) {
|
||||||
return ApiResponse.badRequest("appId is required");
|
return ApiResponse.badRequest("appKey is required");
|
||||||
}
|
}
|
||||||
if (body.userId() == null || body.userId().isBlank()) {
|
if (body.userId() == null || body.userId().isBlank()) {
|
||||||
return ApiResponse.badRequest("userId is required");
|
return ApiResponse.badRequest("userId is required");
|
||||||
@ -72,11 +72,11 @@ public class DemoAuthController {
|
|||||||
if (body.newPassword() == null || body.newPassword().length() < 6) {
|
if (body.newPassword() == null || body.newPassword().length() < 6) {
|
||||||
return ApiResponse.badRequest("password must be at least 6 characters");
|
return ApiResponse.badRequest("password must be at least 6 characters");
|
||||||
}
|
}
|
||||||
authService.resetPassword(body.appId(), body.userId(), body.newPassword());
|
authService.resetPassword(body.appKey(), body.userId(), body.newPassword());
|
||||||
return ApiResponse.ok();
|
return ApiResponse.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
public record RegisterRequest(String appId, String userId, String password, String nickname) {}
|
public record RegisterRequest(String appKey, String userId, String password, String nickname) {}
|
||||||
public record LoginRequest(String appId, String userId, String password) {}
|
public record LoginRequest(String appKey, String userId, String password) {}
|
||||||
public record ResetPasswordRequest(String appId, String userId, String newPassword) {}
|
public record ResetPasswordRequest(String appKey, String userId, String newPassword) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,46 +20,46 @@ public class DemoUserController {
|
|||||||
|
|
||||||
@GetMapping("/user/profile")
|
@GetMapping("/user/profile")
|
||||||
public ApiResponse<DemoUserService.UserProfile> getProfile(
|
public ApiResponse<DemoUserService.UserProfile> getProfile(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
Authentication auth) {
|
Authentication auth) {
|
||||||
String userId = resolveUserId(auth);
|
String userId = resolveUserId(auth);
|
||||||
return ApiResponse.success(userService.getProfile(appId, userId));
|
return ApiResponse.success(userService.getProfile(appKey, userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/user/profile")
|
@PutMapping("/user/profile")
|
||||||
public ApiResponse<DemoUserService.UserProfile> updateProfile(
|
public ApiResponse<DemoUserService.UserProfile> updateProfile(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
Authentication auth,
|
Authentication auth,
|
||||||
@RequestBody UpdateProfileRequest body) {
|
@RequestBody UpdateProfileRequest body) {
|
||||||
String userId = resolveUserId(auth);
|
String userId = resolveUserId(auth);
|
||||||
return ApiResponse.success(
|
return ApiResponse.success(
|
||||||
userService.updateProfile(appId, userId, body.nickname(), body.avatar(), body.gender()));
|
userService.updateProfile(appKey, userId, body.nickname(), body.avatar(), body.gender()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/user/change-password")
|
@PostMapping("/user/change-password")
|
||||||
public ApiResponse<Void> changePassword(
|
public ApiResponse<Void> changePassword(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
Authentication auth,
|
Authentication auth,
|
||||||
@RequestBody ResetPasswordRequest body) {
|
@RequestBody ResetPasswordRequest body) {
|
||||||
String userId = resolveUserId(auth);
|
String userId = resolveUserId(auth);
|
||||||
if (body.oldPassword() == null || body.newPassword() == null) {
|
if (body.oldPassword() == null || body.newPassword() == null) {
|
||||||
return ApiResponse.badRequest("oldPassword and newPassword are required");
|
return ApiResponse.badRequest("oldPassword and newPassword are required");
|
||||||
}
|
}
|
||||||
userService.resetPassword(appId, userId, body.oldPassword(), body.newPassword());
|
userService.resetPassword(appKey, userId, body.oldPassword(), body.newPassword());
|
||||||
return ApiResponse.ok();
|
return ApiResponse.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/users/search")
|
@GetMapping("/users/search")
|
||||||
public ApiResponse<List<DemoUserService.UserProfile>> searchUsers(
|
public ApiResponse<List<DemoUserService.UserProfile>> searchUsers(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam String keyword) {
|
@RequestParam String keyword) {
|
||||||
return ApiResponse.success(userService.searchUsers(appId, keyword));
|
return ApiResponse.success(userService.searchUsers(appKey, keyword));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/users/members")
|
@GetMapping("/users/members")
|
||||||
public ApiResponse<List<DemoUserService.UserProfile>> listMembers(
|
public ApiResponse<List<DemoUserService.UserProfile>> listMembers(
|
||||||
@RequestParam String appId) {
|
@RequestParam String appKey) {
|
||||||
return ApiResponse.success(userService.listMembers(appId));
|
return ApiResponse.success(userService.listMembers(appKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String resolveUserId(Authentication auth) {
|
private String resolveUserId(Authentication auth) {
|
||||||
|
|||||||
@ -31,14 +31,14 @@ public class DemoAppSecretClient {
|
|||||||
this.restTemplate = restTemplate;
|
this.restTemplate = restTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAppSecret(String appId) {
|
public String getAppSecret(String appKey) {
|
||||||
return cache.computeIfAbsent(appId, this::fetchAppSecret);
|
return cache.computeIfAbsent(appKey, this::fetchAppSecret);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String fetchAppSecret(String appId) {
|
private String fetchAppSecret(String appKey) {
|
||||||
String url = UriComponentsBuilder.fromHttpUrl(tenantServiceUrl)
|
String url = UriComponentsBuilder.fromHttpUrl(tenantServiceUrl)
|
||||||
.path("/api/internal/sdk/apps/{appId}/secret")
|
.path("/api/internal/sdk/apps/{appKey}/secret")
|
||||||
.buildAndExpand(appId)
|
.buildAndExpand(appKey)
|
||||||
.toUriString();
|
.toUriString();
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
headers.set("X-Internal-Token", internalToken);
|
headers.set("X-Internal-Token", internalToken);
|
||||||
@ -58,6 +58,6 @@ public class DemoAppSecretClient {
|
|||||||
} catch (RestClientException e) {
|
} catch (RestClientException e) {
|
||||||
throw new BusinessException(502, "Failed to resolve app secret: " + e.getMessage());
|
throw new BusinessException(502, "Failed to resolve app secret: " + e.getMessage());
|
||||||
}
|
}
|
||||||
throw new BusinessException(502, "Failed to resolve app secret for appId: " + appId);
|
throw new BusinessException(502, "Failed to resolve app secret for appKey: " + appKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -54,17 +54,17 @@ public class DemoAuthService {
|
|||||||
public record AuthResult(String demoToken, String imToken, UserProfile profile) {}
|
public record AuthResult(String demoToken, String imToken, UserProfile profile) {}
|
||||||
public record ImCredential(String token) {}
|
public record ImCredential(String token) {}
|
||||||
|
|
||||||
public record UserProfile(String appId, String userId, String nickname, String avatar, String gender) {}
|
public record UserProfile(String appKey, String userId, String nickname, String avatar, String gender) {}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public AuthResult register(String appId, String userId, String password, String nickname) {
|
public AuthResult register(String appKey, String userId, String password, String nickname) {
|
||||||
if (userRepository.existsByAppIdAndUserId(appId, userId)) {
|
if (userRepository.existsByAppIdAndUserId(appKey, userId)) {
|
||||||
throw new BusinessException(409, "User already exists: " + userId);
|
throw new BusinessException(409, "User already exists: " + userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
DemoUserEntity user = new DemoUserEntity();
|
DemoUserEntity user = new DemoUserEntity();
|
||||||
user.setId(UUID.randomUUID().toString());
|
user.setId(UUID.randomUUID().toString());
|
||||||
user.setAppId(appId);
|
user.setAppId(appKey);
|
||||||
user.setUserId(userId);
|
user.setUserId(userId);
|
||||||
user.setPasswordHash(passwordEncoder.encode(password));
|
user.setPasswordHash(passwordEncoder.encode(password));
|
||||||
user.setNickname(nickname != null ? nickname : userId);
|
user.setNickname(nickname != null ? nickname : userId);
|
||||||
@ -72,8 +72,8 @@ public class DemoAuthService {
|
|||||||
user.setCreatedAt(Instant.now());
|
user.setCreatedAt(Instant.now());
|
||||||
userRepository.save(user);
|
userRepository.save(user);
|
||||||
|
|
||||||
String demoToken = generateDemoToken(appId, userId);
|
String demoToken = generateDemoToken(appKey, userId);
|
||||||
ImCredential imCredential = callImServiceLogin(appId, userId);
|
ImCredential imCredential = callImServiceLogin(appKey, userId);
|
||||||
|
|
||||||
return new AuthResult(
|
return new AuthResult(
|
||||||
demoToken,
|
demoToken,
|
||||||
@ -83,16 +83,16 @@ public class DemoAuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public AuthResult login(String appId, String userId, String password) {
|
public AuthResult login(String appKey, String userId, String password) {
|
||||||
DemoUserEntity user = userRepository.findByAppIdAndUserId(appId, userId)
|
DemoUserEntity user = userRepository.findByAppIdAndUserId(appKey, userId)
|
||||||
.orElseThrow(() -> new BusinessException(401, "Invalid credentials"));
|
.orElseThrow(() -> new BusinessException(401, "Invalid credentials"));
|
||||||
|
|
||||||
if (!passwordEncoder.matches(password, user.getPasswordHash())) {
|
if (!passwordEncoder.matches(password, user.getPasswordHash())) {
|
||||||
throw new BusinessException(401, "Invalid credentials");
|
throw new BusinessException(401, "Invalid credentials");
|
||||||
}
|
}
|
||||||
|
|
||||||
String demoToken = generateDemoToken(appId, userId);
|
String demoToken = generateDemoToken(appKey, userId);
|
||||||
ImCredential imCredential = callImServiceLogin(appId, userId);
|
ImCredential imCredential = callImServiceLogin(appKey, userId);
|
||||||
|
|
||||||
return new AuthResult(
|
return new AuthResult(
|
||||||
demoToken,
|
demoToken,
|
||||||
@ -102,30 +102,30 @@ public class DemoAuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void resetPassword(String appId, String userId, String newPassword) {
|
public void resetPassword(String appKey, String userId, String newPassword) {
|
||||||
DemoUserEntity user = userRepository.findByAppIdAndUserId(appId, userId)
|
DemoUserEntity user = userRepository.findByAppIdAndUserId(appKey, userId)
|
||||||
.orElseThrow(() -> new BusinessException(404, "User not found: " + userId));
|
.orElseThrow(() -> new BusinessException(404, "User not found: " + userId));
|
||||||
user.setPasswordHash(passwordEncoder.encode(newPassword));
|
user.setPasswordHash(passwordEncoder.encode(newPassword));
|
||||||
userRepository.save(user);
|
userRepository.save(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String generateDemoToken(String appId, String userId) {
|
private String generateDemoToken(String appKey, String userId) {
|
||||||
return jwtUtil.generate(userId, Map.of("appId", appId, "role", "USER"));
|
return jwtUtil.generate(userId, Map.of("appKey", appKey, "role", "USER"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls im-service to ensure the IM account exists and obtain an IM token.
|
* Calls im-service to ensure the IM account exists and obtain an IM token.
|
||||||
*/
|
*/
|
||||||
private ImCredential callImServiceLogin(String appId, String userId) {
|
private ImCredential callImServiceLogin(String appKey, String userId) {
|
||||||
long timestamp = System.currentTimeMillis();
|
long timestamp = System.currentTimeMillis();
|
||||||
String nonce = UUID.randomUUID().toString();
|
String nonce = UUID.randomUUID().toString();
|
||||||
String appSecret = appSecretClient.getAppSecret(appId);
|
String appSecret = appSecretClient.getAppSecret(appKey);
|
||||||
String payload = AppRequestSignatureUtil.payload(appId, userId, timestamp, nonce);
|
String payload = AppRequestSignatureUtil.payload(appKey, userId, timestamp, nonce);
|
||||||
String signature = AppRequestSignatureUtil.sign(appSecret, payload);
|
String signature = AppRequestSignatureUtil.sign(appSecret, payload);
|
||||||
|
|
||||||
URI uri = UriComponentsBuilder.fromHttpUrl(imServiceUrl)
|
URI uri = UriComponentsBuilder.fromHttpUrl(imServiceUrl)
|
||||||
.path("/api/im/auth/login")
|
.path("/api/im/auth/login")
|
||||||
.queryParam("appId", appId)
|
.queryParam("appKey", appKey)
|
||||||
.queryParam("userId", userId)
|
.queryParam("userId", userId)
|
||||||
.encode()
|
.encode()
|
||||||
.build()
|
.build()
|
||||||
@ -151,10 +151,10 @@ public class DemoAuthService {
|
|||||||
}
|
}
|
||||||
return new ImCredential(token);
|
return new ImCredential(token);
|
||||||
}
|
}
|
||||||
log.warn("im-service login returned unexpected response for appId={} userId={}: {}", appId, userId, body);
|
log.warn("im-service login returned unexpected response for appKey={} userId={}: {}", appKey, userId, body);
|
||||||
throw new BusinessException(502, "Failed to acquire IM token");
|
throw new BusinessException(502, "Failed to acquire IM token");
|
||||||
} catch (RestClientException e) {
|
} catch (RestClientException e) {
|
||||||
log.error("Failed to call im-service login for appId={} userId={}: {}", appId, userId, e.getMessage());
|
log.error("Failed to call im-service login for appKey={} userId={}: {}", appKey, userId, e.getMessage());
|
||||||
throw new BusinessException(502, "Failed to acquire IM token");
|
throw new BusinessException(502, "Failed to acquire IM token");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,18 +20,18 @@ public class DemoUserService {
|
|||||||
this.passwordEncoder = passwordEncoder;
|
this.passwordEncoder = passwordEncoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public record UserProfile(String appId, String userId, String nickname, String avatar, String gender) {}
|
public record UserProfile(String appKey, String userId, String nickname, String avatar, String gender) {}
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public UserProfile getProfile(String appId, String userId) {
|
public UserProfile getProfile(String appKey, String userId) {
|
||||||
DemoUserEntity user = userRepository.findByAppIdAndUserId(appId, userId)
|
DemoUserEntity user = userRepository.findByAppIdAndUserId(appKey, userId)
|
||||||
.orElseThrow(() -> new BusinessException(404, "User not found"));
|
.orElseThrow(() -> new BusinessException(404, "User not found"));
|
||||||
return toProfile(user);
|
return toProfile(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public UserProfile updateProfile(String appId, String userId, String nickname, String avatar, String gender) {
|
public UserProfile updateProfile(String appKey, String userId, String nickname, String avatar, String gender) {
|
||||||
DemoUserEntity user = userRepository.findByAppIdAndUserId(appId, userId)
|
DemoUserEntity user = userRepository.findByAppIdAndUserId(appKey, userId)
|
||||||
.orElseThrow(() -> new BusinessException(404, "User not found"));
|
.orElseThrow(() -> new BusinessException(404, "User not found"));
|
||||||
|
|
||||||
if (nickname != null && !nickname.isBlank()) {
|
if (nickname != null && !nickname.isBlank()) {
|
||||||
@ -53,8 +53,8 @@ public class DemoUserService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void resetPassword(String appId, String userId, String oldPassword, String newPassword) {
|
public void resetPassword(String appKey, String userId, String oldPassword, String newPassword) {
|
||||||
DemoUserEntity user = userRepository.findByAppIdAndUserId(appId, userId)
|
DemoUserEntity user = userRepository.findByAppIdAndUserId(appKey, userId)
|
||||||
.orElseThrow(() -> new BusinessException(404, "User not found"));
|
.orElseThrow(() -> new BusinessException(404, "User not found"));
|
||||||
|
|
||||||
if (!passwordEncoder.matches(oldPassword, user.getPasswordHash())) {
|
if (!passwordEncoder.matches(oldPassword, user.getPasswordHash())) {
|
||||||
@ -69,19 +69,19 @@ public class DemoUserService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public List<UserProfile> searchUsers(String appId, String keyword) {
|
public List<UserProfile> searchUsers(String appKey, String keyword) {
|
||||||
if (keyword == null || keyword.isBlank()) {
|
if (keyword == null || keyword.isBlank()) {
|
||||||
throw new BusinessException(400, "Search keyword must not be blank");
|
throw new BusinessException(400, "Search keyword must not be blank");
|
||||||
}
|
}
|
||||||
return userRepository.searchByKeyword(appId, keyword.trim())
|
return userRepository.searchByKeyword(appKey, keyword.trim())
|
||||||
.stream()
|
.stream()
|
||||||
.map(this::toProfile)
|
.map(this::toProfile)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public List<UserProfile> listMembers(String appId) {
|
public List<UserProfile> listMembers(String appKey) {
|
||||||
return userRepository.findAllByAppIdOrderByCreatedAtAsc(appId)
|
return userRepository.findAllByAppIdOrderByCreatedAtAsc(appKey)
|
||||||
.stream()
|
.stream()
|
||||||
.map(this::toProfile)
|
.map(this::toProfile)
|
||||||
.toList();
|
.toList();
|
||||||
|
|||||||
@ -35,7 +35,7 @@ jwt:
|
|||||||
expiration: 3153600000000
|
expiration: 3153600000000
|
||||||
|
|
||||||
demo:
|
demo:
|
||||||
tenant-service-url: ${TENANT_SERVICE_URL:http://127.0.0.1:8081}
|
tenant-service-url: ${TENANT_SERVICE_URL:http://127.0.0.1:9001}
|
||||||
internal-token: ${SDK_INTERNAL_TOKEN:xuqm-internal-token}
|
internal-token: ${SDK_INTERNAL_TOKEN:xuqm-internal-token}
|
||||||
im-service-url: ${IM_SERVICE_URL:http://127.0.0.1:8082}
|
im-service-url: ${IM_SERVICE_URL:http://127.0.0.1:8082}
|
||||||
|
|
||||||
|
|||||||
@ -41,7 +41,7 @@ public final class XuqmImServerSdk {
|
|||||||
private final String baseUrl;
|
private final String baseUrl;
|
||||||
private final String pushBaseUrl;
|
private final String pushBaseUrl;
|
||||||
private final String updateBaseUrl;
|
private final String updateBaseUrl;
|
||||||
private final String appId;
|
private final String appKey;
|
||||||
private final String appSecret;
|
private final String appSecret;
|
||||||
private final Supplier<String> bearerTokenSupplier;
|
private final Supplier<String> bearerTokenSupplier;
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ public final class XuqmImServerSdk {
|
|||||||
this.baseUrl = trimTrailingSlash(builder.baseUrl);
|
this.baseUrl = trimTrailingSlash(builder.baseUrl);
|
||||||
this.pushBaseUrl = trimTrailingSlash(builder.pushBaseUrl == null ? builder.baseUrl : builder.pushBaseUrl);
|
this.pushBaseUrl = trimTrailingSlash(builder.pushBaseUrl == null ? builder.baseUrl : builder.pushBaseUrl);
|
||||||
this.updateBaseUrl = trimTrailingSlash(builder.updateBaseUrl == null ? builder.baseUrl : builder.updateBaseUrl);
|
this.updateBaseUrl = trimTrailingSlash(builder.updateBaseUrl == null ? builder.baseUrl : builder.updateBaseUrl);
|
||||||
this.appId = Objects.requireNonNull(builder.appId, "appId");
|
this.appKey = Objects.requireNonNull(builder.appKey, "appKey");
|
||||||
this.appSecret = Objects.requireNonNull(builder.appSecret, "appSecret");
|
this.appSecret = Objects.requireNonNull(builder.appSecret, "appSecret");
|
||||||
this.bearerTokenSupplier = builder.bearerTokenSupplier;
|
this.bearerTokenSupplier = builder.bearerTokenSupplier;
|
||||||
}
|
}
|
||||||
@ -65,7 +65,7 @@ public final class XuqmImServerSdk {
|
|||||||
public ImMessage sendMessage(SendMessageRequest request) {
|
public ImMessage sendMessage(SendMessageRequest request) {
|
||||||
ApiResponse<ImMessage> response = request(
|
ApiResponse<ImMessage> response = request(
|
||||||
"POST",
|
"POST",
|
||||||
buildUri("/api/im/messages/send", Map.of("appId", appId)),
|
buildUri("/api/im/messages/send", Map.of("appKey", appKey)),
|
||||||
request,
|
request,
|
||||||
authorizedHeaders(),
|
authorizedHeaders(),
|
||||||
new TypeReference<>() {}
|
new TypeReference<>() {}
|
||||||
@ -76,7 +76,7 @@ public final class XuqmImServerSdk {
|
|||||||
public ImMessage revokeMessage(String messageId) {
|
public ImMessage revokeMessage(String messageId) {
|
||||||
ApiResponse<ImMessage> response = request(
|
ApiResponse<ImMessage> response = request(
|
||||||
"POST",
|
"POST",
|
||||||
buildUri("/api/im/messages/" + encode(messageId) + "/revoke", Map.of("appId", appId)),
|
buildUri("/api/im/messages/" + encode(messageId) + "/revoke", Map.of("appKey", appKey)),
|
||||||
null,
|
null,
|
||||||
authorizedHeaders(),
|
authorizedHeaders(),
|
||||||
new TypeReference<>() {}
|
new TypeReference<>() {}
|
||||||
@ -87,7 +87,7 @@ public final class XuqmImServerSdk {
|
|||||||
public ImMessage editMessage(String messageId, String content) {
|
public ImMessage editMessage(String messageId, String content) {
|
||||||
ApiResponse<ImMessage> response = request(
|
ApiResponse<ImMessage> response = request(
|
||||||
"PUT",
|
"PUT",
|
||||||
buildUri("/api/im/messages/" + encode(messageId), Map.of("appId", appId)),
|
buildUri("/api/im/messages/" + encode(messageId), Map.of("appKey", appKey)),
|
||||||
Map.of("content", content),
|
Map.of("content", content),
|
||||||
authorizedHeaders(),
|
authorizedHeaders(),
|
||||||
new TypeReference<>() {}
|
new TypeReference<>() {}
|
||||||
@ -142,7 +142,7 @@ public final class XuqmImServerSdk {
|
|||||||
public List<ConversationView> listConversations(int size) {
|
public List<ConversationView> listConversations(int size) {
|
||||||
ApiResponse<List<ConversationView>> response = request(
|
ApiResponse<List<ConversationView>> response = request(
|
||||||
"GET",
|
"GET",
|
||||||
buildUri("/api/im/conversations", Map.of("appId", appId, "page", "0", "size", String.valueOf(size))),
|
buildUri("/api/im/conversations", Map.of("appKey", appKey, "page", "0", "size", String.valueOf(size))),
|
||||||
null,
|
null,
|
||||||
authorizedHeaders(),
|
authorizedHeaders(),
|
||||||
new TypeReference<>() {}
|
new TypeReference<>() {}
|
||||||
@ -318,7 +318,7 @@ public final class XuqmImServerSdk {
|
|||||||
if (Math.abs(now - ts) > 5 * 60 * 1000L) {
|
if (Math.abs(now - ts) > 5 * 60 * 1000L) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
String payload = appId + "\n" + timestamp + "\n" + normalize(nonce) + "\n" + sha256Hex(body);
|
String payload = appKey + "\n" + timestamp + "\n" + normalize(nonce) + "\n" + sha256Hex(body);
|
||||||
String expected = hmacSha256Hex(appSecret, payload);
|
String expected = hmacSha256Hex(appSecret, payload);
|
||||||
return MessageDigest.isEqual(
|
return MessageDigest.isEqual(
|
||||||
expected.getBytes(StandardCharsets.UTF_8),
|
expected.getBytes(StandardCharsets.UTF_8),
|
||||||
@ -336,7 +336,7 @@ public final class XuqmImServerSdk {
|
|||||||
longValue(root, "requestTime"),
|
longValue(root, "requestTime"),
|
||||||
root.get("payload"),
|
root.get("payload"),
|
||||||
text(root, "signature"),
|
text(root, "signature"),
|
||||||
text(root, "appId")
|
text(root, "appKey")
|
||||||
);
|
);
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JsonProcessingException e) {
|
||||||
throw new ImSdkException("Invalid callback body", e);
|
throw new ImSdkException("Invalid callback body", e);
|
||||||
@ -399,7 +399,7 @@ public final class XuqmImServerSdk {
|
|||||||
request(
|
request(
|
||||||
"POST",
|
"POST",
|
||||||
buildUri(pushBaseUrl, "/api/push/register", Map.of(
|
buildUri(pushBaseUrl, "/api/push/register", Map.of(
|
||||||
"appId", appId,
|
"appKey", appKey,
|
||||||
"userId", userId,
|
"userId", userId,
|
||||||
"vendor", vendor,
|
"vendor", vendor,
|
||||||
"token", token
|
"token", token
|
||||||
@ -412,7 +412,7 @@ public final class XuqmImServerSdk {
|
|||||||
|
|
||||||
public void sendPush(String userId, String title, String body, String payload) {
|
public void sendPush(String userId, String title, String body, String payload) {
|
||||||
Map<String, String> query = new LinkedHashMap<>();
|
Map<String, String> query = new LinkedHashMap<>();
|
||||||
query.put("appId", appId);
|
query.put("appKey", appKey);
|
||||||
query.put("userId", userId);
|
query.put("userId", userId);
|
||||||
query.put("title", title);
|
query.put("title", title);
|
||||||
query.put("body", body);
|
query.put("body", body);
|
||||||
@ -432,7 +432,7 @@ public final class XuqmImServerSdk {
|
|||||||
ApiResponse<Map<String, Object>> response = request(
|
ApiResponse<Map<String, Object>> response = request(
|
||||||
"GET",
|
"GET",
|
||||||
buildUri(updateBaseUrl, "/api/v1/updates/app/check", Map.of(
|
buildUri(updateBaseUrl, "/api/v1/updates/app/check", Map.of(
|
||||||
"appId", appId,
|
"appKey", appKey,
|
||||||
"platform", platform,
|
"platform", platform,
|
||||||
"currentVersionCode", String.valueOf(currentVersionCode)
|
"currentVersionCode", String.valueOf(currentVersionCode)
|
||||||
)),
|
)),
|
||||||
@ -451,7 +451,7 @@ public final class XuqmImServerSdk {
|
|||||||
boolean forceUpdate,
|
boolean forceUpdate,
|
||||||
Path apkFile) {
|
Path apkFile) {
|
||||||
Map<String, String> form = new LinkedHashMap<>();
|
Map<String, String> form = new LinkedHashMap<>();
|
||||||
form.put("appId", appId);
|
form.put("appKey", appKey);
|
||||||
form.put("platform", platform);
|
form.put("platform", platform);
|
||||||
form.put("versionName", versionName);
|
form.put("versionName", versionName);
|
||||||
form.put("versionCode", String.valueOf(versionCode));
|
form.put("versionCode", String.valueOf(versionCode));
|
||||||
@ -473,7 +473,7 @@ public final class XuqmImServerSdk {
|
|||||||
|
|
||||||
public UnifiedReleaseResult uploadUnifiedRelease(UnifiedReleaseManifest manifest, Map<String, Path> files) {
|
public UnifiedReleaseResult uploadUnifiedRelease(UnifiedReleaseManifest manifest, Map<String, Path> files) {
|
||||||
Map<String, String> form = new LinkedHashMap<>();
|
Map<String, String> form = new LinkedHashMap<>();
|
||||||
form.put("appId", appId);
|
form.put("appKey", appKey);
|
||||||
try {
|
try {
|
||||||
form.put("manifest", objectMapper.writeValueAsString(manifest));
|
form.put("manifest", objectMapper.writeValueAsString(manifest));
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JsonProcessingException e) {
|
||||||
@ -529,7 +529,7 @@ public final class XuqmImServerSdk {
|
|||||||
public List<AppVersionView> listAppVersions(String platform) {
|
public List<AppVersionView> listAppVersions(String platform) {
|
||||||
ApiResponse<List<AppVersionView>> response = request(
|
ApiResponse<List<AppVersionView>> response = request(
|
||||||
"GET",
|
"GET",
|
||||||
buildUri(updateBaseUrl, "/api/v1/updates/app/list", Map.of("appId", appId, "platform", platform)),
|
buildUri(updateBaseUrl, "/api/v1/updates/app/list", Map.of("appKey", appKey, "platform", platform)),
|
||||||
null,
|
null,
|
||||||
publicHeaders(),
|
publicHeaders(),
|
||||||
new TypeReference<>() {}
|
new TypeReference<>() {}
|
||||||
@ -541,7 +541,7 @@ public final class XuqmImServerSdk {
|
|||||||
ApiResponse<RnBundleView> response = request(
|
ApiResponse<RnBundleView> response = request(
|
||||||
"GET",
|
"GET",
|
||||||
buildUri(updateBaseUrl, "/api/v1/rn/update/check", Map.of(
|
buildUri(updateBaseUrl, "/api/v1/rn/update/check", Map.of(
|
||||||
"appId", appId,
|
"appKey", appKey,
|
||||||
"moduleId", moduleId,
|
"moduleId", moduleId,
|
||||||
"platform", platform,
|
"platform", platform,
|
||||||
"currentVersion", currentVersion
|
"currentVersion", currentVersion
|
||||||
@ -561,7 +561,7 @@ public final class XuqmImServerSdk {
|
|||||||
String note,
|
String note,
|
||||||
Path bundle) {
|
Path bundle) {
|
||||||
Map<String, String> form = new LinkedHashMap<>();
|
Map<String, String> form = new LinkedHashMap<>();
|
||||||
form.put("appId", appId);
|
form.put("appKey", appKey);
|
||||||
form.put("moduleId", moduleId);
|
form.put("moduleId", moduleId);
|
||||||
form.put("platform", platform);
|
form.put("platform", platform);
|
||||||
form.put("version", version);
|
form.put("version", version);
|
||||||
@ -607,7 +607,7 @@ public final class XuqmImServerSdk {
|
|||||||
|
|
||||||
public List<RnBundleView> listRnBundles(String moduleId, String platform) {
|
public List<RnBundleView> listRnBundles(String moduleId, String platform) {
|
||||||
Map<String, String> query = new LinkedHashMap<>();
|
Map<String, String> query = new LinkedHashMap<>();
|
||||||
query.put("appId", appId);
|
query.put("appKey", appKey);
|
||||||
if (moduleId != null) {
|
if (moduleId != null) {
|
||||||
query.put("moduleId", moduleId);
|
query.put("moduleId", moduleId);
|
||||||
}
|
}
|
||||||
@ -1146,9 +1146,9 @@ public final class XuqmImServerSdk {
|
|||||||
return response.data();
|
return response.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void kickUsers(String appId, List<String> userIds) {
|
public void kickUsers(String appKey, List<String> userIds) {
|
||||||
Map<String, String> query = new LinkedHashMap<>();
|
Map<String, String> query = new LinkedHashMap<>();
|
||||||
query.put("appId", appId);
|
query.put("appKey", appKey);
|
||||||
request(
|
request(
|
||||||
"POST",
|
"POST",
|
||||||
buildUri("/api/im/admin/users/kick", query),
|
buildUri("/api/im/admin/users/kick", query),
|
||||||
@ -1158,9 +1158,9 @@ public final class XuqmImServerSdk {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImMessage> batchSendMessage(String appId, List<String> toIds, String msgType, String content) {
|
public List<ImMessage> batchSendMessage(String appKey, List<String> toIds, String msgType, String content) {
|
||||||
Map<String, String> query = new LinkedHashMap<>();
|
Map<String, String> query = new LinkedHashMap<>();
|
||||||
query.put("appId", appId);
|
query.put("appKey", appKey);
|
||||||
ApiResponse<List<ImMessage>> response = request(
|
ApiResponse<List<ImMessage>> response = request(
|
||||||
"POST",
|
"POST",
|
||||||
buildUri("/api/im/admin/messages/batch-send", query),
|
buildUri("/api/im/admin/messages/batch-send", query),
|
||||||
@ -1171,9 +1171,9 @@ public final class XuqmImServerSdk {
|
|||||||
return response.data();
|
return response.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void adminSetMsgRead(String appId, String userId) {
|
public void adminSetMsgRead(String appKey, String userId) {
|
||||||
Map<String, String> query = new LinkedHashMap<>();
|
Map<String, String> query = new LinkedHashMap<>();
|
||||||
query.put("appId", appId);
|
query.put("appKey", appKey);
|
||||||
request(
|
request(
|
||||||
"POST",
|
"POST",
|
||||||
buildUri("/api/im/admin/messages/read", query),
|
buildUri("/api/im/admin/messages/read", query),
|
||||||
@ -1183,9 +1183,9 @@ public final class XuqmImServerSdk {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImMessage> importMessages(String appId, List<ImportMessageRequest> requests) {
|
public List<ImMessage> importMessages(String appKey, List<ImportMessageRequest> requests) {
|
||||||
Map<String, String> query = new LinkedHashMap<>();
|
Map<String, String> query = new LinkedHashMap<>();
|
||||||
query.put("appId", appId);
|
query.put("appKey", appKey);
|
||||||
ApiResponse<List<ImMessage>> response = request(
|
ApiResponse<List<ImMessage>> response = request(
|
||||||
"POST",
|
"POST",
|
||||||
buildUri("/api/im/admin/messages/import", query),
|
buildUri("/api/im/admin/messages/import", query),
|
||||||
@ -1196,9 +1196,9 @@ public final class XuqmImServerSdk {
|
|||||||
return response.data();
|
return response.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FriendCheckResult> checkFriends(String appId, List<String> friendIds) {
|
public List<FriendCheckResult> checkFriends(String appKey, List<String> friendIds) {
|
||||||
Map<String, String> query = new LinkedHashMap<>();
|
Map<String, String> query = new LinkedHashMap<>();
|
||||||
query.put("appId", appId);
|
query.put("appKey", appKey);
|
||||||
ApiResponse<List<FriendCheckResult>> response = request(
|
ApiResponse<List<FriendCheckResult>> response = request(
|
||||||
"POST",
|
"POST",
|
||||||
buildUri("/api/im/friends/check", query),
|
buildUri("/api/im/friends/check", query),
|
||||||
@ -1648,7 +1648,7 @@ public final class XuqmImServerSdk {
|
|||||||
|
|
||||||
private Map<String, String> appQuery() {
|
private Map<String, String> appQuery() {
|
||||||
Map<String, String> query = new LinkedHashMap<>();
|
Map<String, String> query = new LinkedHashMap<>();
|
||||||
query.put("appId", appId);
|
query.put("appKey", appKey);
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1668,7 +1668,7 @@ public final class XuqmImServerSdk {
|
|||||||
|
|
||||||
private Map<String, String> queryParams(String msgType, String keyword, LocalDateTime startTime, LocalDateTime endTime, int page, int size) {
|
private Map<String, String> queryParams(String msgType, String keyword, LocalDateTime startTime, LocalDateTime endTime, int page, int size) {
|
||||||
Map<String, String> query = new LinkedHashMap<>();
|
Map<String, String> query = new LinkedHashMap<>();
|
||||||
query.put("appId", appId);
|
query.put("appKey", appKey);
|
||||||
if (msgType != null && !msgType.isBlank()) {
|
if (msgType != null && !msgType.isBlank()) {
|
||||||
query.put("msgType", msgType);
|
query.put("msgType", msgType);
|
||||||
}
|
}
|
||||||
@ -1696,7 +1696,7 @@ public final class XuqmImServerSdk {
|
|||||||
int size
|
int size
|
||||||
) {
|
) {
|
||||||
Map<String, String> query = new LinkedHashMap<>();
|
Map<String, String> query = new LinkedHashMap<>();
|
||||||
query.put("appId", appId);
|
query.put("appKey", appKey);
|
||||||
if (chatType != null && !chatType.isBlank()) {
|
if (chatType != null && !chatType.isBlank()) {
|
||||||
query.put("chatType", chatType);
|
query.put("chatType", chatType);
|
||||||
}
|
}
|
||||||
@ -1741,7 +1741,7 @@ public final class XuqmImServerSdk {
|
|||||||
private String baseUrl = DEFAULT_BASE_URL;
|
private String baseUrl = DEFAULT_BASE_URL;
|
||||||
private String pushBaseUrl;
|
private String pushBaseUrl;
|
||||||
private String updateBaseUrl;
|
private String updateBaseUrl;
|
||||||
private String appId;
|
private String appKey;
|
||||||
private String appSecret;
|
private String appSecret;
|
||||||
private Supplier<String> bearerTokenSupplier;
|
private Supplier<String> bearerTokenSupplier;
|
||||||
|
|
||||||
@ -1762,8 +1762,8 @@ public final class XuqmImServerSdk {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder appId(String appId) {
|
public Builder appKey(String appKey) {
|
||||||
this.appId = appId;
|
this.appKey = appKey;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1778,7 +1778,7 @@ public final class XuqmImServerSdk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public XuqmImServerSdk build() {
|
public XuqmImServerSdk build() {
|
||||||
Objects.requireNonNull(this.appId, "appId is required");
|
Objects.requireNonNull(this.appKey, "appKey is required");
|
||||||
Objects.requireNonNull(this.appSecret, "appSecret is required");
|
Objects.requireNonNull(this.appSecret, "appSecret is required");
|
||||||
return new XuqmImServerSdk(this);
|
return new XuqmImServerSdk(this);
|
||||||
}
|
}
|
||||||
@ -1806,7 +1806,7 @@ public final class XuqmImServerSdk {
|
|||||||
|
|
||||||
public record AccountView(
|
public record AccountView(
|
||||||
String id,
|
String id,
|
||||||
String appId,
|
String appKey,
|
||||||
String userId,
|
String userId,
|
||||||
String nickname,
|
String nickname,
|
||||||
String gender,
|
String gender,
|
||||||
@ -1825,7 +1825,7 @@ public final class XuqmImServerSdk {
|
|||||||
|
|
||||||
public record FriendLinkView(
|
public record FriendLinkView(
|
||||||
Long id,
|
Long id,
|
||||||
String appId,
|
String appKey,
|
||||||
String userId,
|
String userId,
|
||||||
String friendId,
|
String friendId,
|
||||||
String friendGroup,
|
String friendGroup,
|
||||||
@ -1836,7 +1836,7 @@ public final class XuqmImServerSdk {
|
|||||||
|
|
||||||
public record FriendRequestView(
|
public record FriendRequestView(
|
||||||
String id,
|
String id,
|
||||||
String appId,
|
String appKey,
|
||||||
String fromUserId,
|
String fromUserId,
|
||||||
String toUserId,
|
String toUserId,
|
||||||
String remark,
|
String remark,
|
||||||
@ -1847,7 +1847,7 @@ public final class XuqmImServerSdk {
|
|||||||
|
|
||||||
public record BlacklistView(
|
public record BlacklistView(
|
||||||
String id,
|
String id,
|
||||||
String appId,
|
String appKey,
|
||||||
String userId,
|
String userId,
|
||||||
String blockedUserId,
|
String blockedUserId,
|
||||||
LocalDateTime createdAt
|
LocalDateTime createdAt
|
||||||
@ -1862,7 +1862,7 @@ public final class XuqmImServerSdk {
|
|||||||
|
|
||||||
public record GroupView(
|
public record GroupView(
|
||||||
String id,
|
String id,
|
||||||
String appId,
|
String appKey,
|
||||||
String name,
|
String name,
|
||||||
String groupType,
|
String groupType,
|
||||||
String creatorId,
|
String creatorId,
|
||||||
@ -1892,7 +1892,7 @@ public final class XuqmImServerSdk {
|
|||||||
|
|
||||||
public record GroupJoinRequestView(
|
public record GroupJoinRequestView(
|
||||||
String id,
|
String id,
|
||||||
String appId,
|
String appKey,
|
||||||
String groupId,
|
String groupId,
|
||||||
String requesterId,
|
String requesterId,
|
||||||
String remark,
|
String remark,
|
||||||
@ -1908,7 +1908,7 @@ public final class XuqmImServerSdk {
|
|||||||
long requestTime,
|
long requestTime,
|
||||||
JsonNode payload,
|
JsonNode payload,
|
||||||
String signature,
|
String signature,
|
||||||
String appId
|
String appKey
|
||||||
) {
|
) {
|
||||||
public boolean isType(String type) {
|
public boolean isType(String type) {
|
||||||
return type != null && callbackType != null && callbackType.equalsIgnoreCase(type);
|
return type != null && callbackType != null && callbackType.equalsIgnoreCase(type);
|
||||||
@ -1941,7 +1941,7 @@ public final class XuqmImServerSdk {
|
|||||||
|
|
||||||
public record AppVersionView(
|
public record AppVersionView(
|
||||||
String id,
|
String id,
|
||||||
String appId,
|
String appKey,
|
||||||
String platform,
|
String platform,
|
||||||
String versionName,
|
String versionName,
|
||||||
int versionCode,
|
int versionCode,
|
||||||
@ -1958,7 +1958,7 @@ public final class XuqmImServerSdk {
|
|||||||
|
|
||||||
public record RnBundleView(
|
public record RnBundleView(
|
||||||
String id,
|
String id,
|
||||||
String appId,
|
String appKey,
|
||||||
String moduleId,
|
String moduleId,
|
||||||
String platform,
|
String platform,
|
||||||
String version,
|
String version,
|
||||||
@ -2013,7 +2013,7 @@ public final class XuqmImServerSdk {
|
|||||||
|
|
||||||
public record ImMessage(
|
public record ImMessage(
|
||||||
String id,
|
String id,
|
||||||
String appId,
|
String appKey,
|
||||||
String fromUserId,
|
String fromUserId,
|
||||||
String toId,
|
String toId,
|
||||||
String chatType,
|
String chatType,
|
||||||
@ -2043,7 +2043,7 @@ public final class XuqmImServerSdk {
|
|||||||
|
|
||||||
public record WebhookConfigView(
|
public record WebhookConfigView(
|
||||||
String id,
|
String id,
|
||||||
String appId,
|
String appKey,
|
||||||
String url,
|
String url,
|
||||||
String secret,
|
String secret,
|
||||||
boolean enabled,
|
boolean enabled,
|
||||||
@ -2058,7 +2058,7 @@ public final class XuqmImServerSdk {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
public record AppVersionUploadRequest(
|
public record AppVersionUploadRequest(
|
||||||
String appId,
|
String appKey,
|
||||||
String platform,
|
String platform,
|
||||||
String versionName,
|
String versionName,
|
||||||
int versionCode,
|
int versionCode,
|
||||||
@ -2067,7 +2067,7 @@ public final class XuqmImServerSdk {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
public record RnBundleUploadRequest(
|
public record RnBundleUploadRequest(
|
||||||
String appId,
|
String appKey,
|
||||||
String moduleId,
|
String moduleId,
|
||||||
String platform,
|
String platform,
|
||||||
String version,
|
String version,
|
||||||
@ -2076,7 +2076,7 @@ public final class XuqmImServerSdk {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
public record MessageReadCallbackPayload(
|
public record MessageReadCallbackPayload(
|
||||||
String appId,
|
String appKey,
|
||||||
String readerId,
|
String readerId,
|
||||||
String peerId,
|
String peerId,
|
||||||
String groupId,
|
String groupId,
|
||||||
|
|||||||
@ -63,15 +63,15 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
|
|||||||
token = token.substring(7);
|
token = token.substring(7);
|
||||||
if (jwtUtil.isValid(token)) {
|
if (jwtUtil.isValid(token)) {
|
||||||
String userId = jwtUtil.getSubject(token);
|
String userId = jwtUtil.getSubject(token);
|
||||||
String appId = "";
|
String appKey = "";
|
||||||
try {
|
try {
|
||||||
Object claim = jwtUtil.parse(token).get("appId");
|
Object claim = jwtUtil.parse(token).get("appKey");
|
||||||
if (claim != null) appId = claim.toString();
|
if (claim != null) appKey = claim.toString();
|
||||||
} catch (Exception ignored) {}
|
} catch (Exception ignored) {}
|
||||||
UsernamePasswordAuthenticationToken auth =
|
UsernamePasswordAuthenticationToken auth =
|
||||||
new UsernamePasswordAuthenticationToken(userId, null,
|
new UsernamePasswordAuthenticationToken(userId, null,
|
||||||
List.of(new SimpleGrantedAuthority("ROLE_USER")));
|
List.of(new SimpleGrantedAuthority("ROLE_USER")));
|
||||||
auth.setDetails(java.util.Map.of("appId", appId));
|
auth.setDetails(java.util.Map.of("appKey", appKey));
|
||||||
accessor.setUser(auth);
|
accessor.setUser(auth);
|
||||||
userPresenceService.markOnline(userId);
|
userPresenceService.markOnline(userId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,15 +33,15 @@ public class AccountController {
|
|||||||
@GetMapping("/{userId}")
|
@GetMapping("/{userId}")
|
||||||
public ResponseEntity<ApiResponse<ImAccountEntity>> get(
|
public ResponseEntity<ApiResponse<ImAccountEntity>> get(
|
||||||
@AuthenticationPrincipal String currentUserId,
|
@AuthenticationPrincipal String currentUserId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String userId) {
|
@PathVariable String userId) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(accountService.getAccount(appId, userId)));
|
return ResponseEntity.ok(ApiResponse.success(accountService.getAccount(appKey, userId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/{userId}")
|
@PutMapping("/{userId}")
|
||||||
public ResponseEntity<ApiResponse<ImAccountEntity>> update(
|
public ResponseEntity<ApiResponse<ImAccountEntity>> update(
|
||||||
@AuthenticationPrincipal String currentUserId,
|
@AuthenticationPrincipal String currentUserId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String userId,
|
@PathVariable String userId,
|
||||||
@RequestParam(required = false) String nickname,
|
@RequestParam(required = false) String nickname,
|
||||||
@RequestParam(required = false) String avatar,
|
@RequestParam(required = false) String avatar,
|
||||||
@ -50,31 +50,31 @@ public class AccountController {
|
|||||||
throw new BusinessException(403, "Only the account owner can update profile");
|
throw new BusinessException(403, "Only the account owner can update profile");
|
||||||
}
|
}
|
||||||
return ResponseEntity.ok(ApiResponse.success(
|
return ResponseEntity.ok(ApiResponse.success(
|
||||||
accountService.updateAccount(appId, userId, nickname, avatar, gender)));
|
accountService.updateAccount(appKey, userId, nickname, avatar, gender)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/search")
|
@GetMapping("/search")
|
||||||
public ResponseEntity<ApiResponse<List<ImAccountEntity>>> search(
|
public ResponseEntity<ApiResponse<List<ImAccountEntity>>> search(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam String keyword,
|
@RequestParam String keyword,
|
||||||
@RequestParam(defaultValue = "20") int size) {
|
@RequestParam(defaultValue = "20") int size) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(accountService.searchAccounts(appId, keyword, size)));
|
return ResponseEntity.ok(ApiResponse.success(accountService.searchAccounts(appKey, keyword, size)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/import")
|
@PostMapping("/import")
|
||||||
public ResponseEntity<ApiResponse<ImAccountEntity>> importAccount(
|
public ResponseEntity<ApiResponse<ImAccountEntity>> importAccount(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestBody ImportAccountRequest req) {
|
@RequestBody ImportAccountRequest req) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(
|
return ResponseEntity.ok(ApiResponse.success(
|
||||||
accountService.importAccount(appId, req.userId(), req.nickname(), req.avatar(), req.gender(), req.status())));
|
accountService.importAccount(appKey, req.userId(), req.nickname(), req.avatar(), req.gender(), req.status())));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/import/batch")
|
@PostMapping("/import/batch")
|
||||||
public ResponseEntity<ApiResponse<List<ImAccountEntity>>> importAccounts(
|
public ResponseEntity<ApiResponse<List<ImAccountEntity>>> importAccounts(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestBody List<ImportAccountRequest> req) {
|
@RequestBody List<ImportAccountRequest> req) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(accountService.importAccounts(
|
return ResponseEntity.ok(ApiResponse.success(accountService.importAccounts(
|
||||||
appId,
|
appKey,
|
||||||
req == null ? List.of() : req.stream()
|
req == null ? List.of() : req.stream()
|
||||||
.map(item -> new ImAccountService.ImportAccountRequest(
|
.map(item -> new ImAccountService.ImportAccountRequest(
|
||||||
item.userId(), item.nickname(), item.avatar(), item.gender(), item.status()))
|
item.userId(), item.nickname(), item.avatar(), item.gender(), item.status()))
|
||||||
@ -83,17 +83,17 @@ public class AccountController {
|
|||||||
|
|
||||||
@DeleteMapping("/{userId}")
|
@DeleteMapping("/{userId}")
|
||||||
public ResponseEntity<ApiResponse<Void>> delete(
|
public ResponseEntity<ApiResponse<Void>> delete(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String userId) {
|
@PathVariable String userId) {
|
||||||
accountService.deleteAccount(appId, userId);
|
accountService.deleteAccount(appKey, userId);
|
||||||
return ResponseEntity.ok(ApiResponse.ok());
|
return ResponseEntity.ok(ApiResponse.ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{userId}/exists")
|
@GetMapping("/{userId}/exists")
|
||||||
public ResponseEntity<ApiResponse<Map<String, Boolean>>> exists(
|
public ResponseEntity<ApiResponse<Map<String, Boolean>>> exists(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String userId) {
|
@PathVariable String userId) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(Map.of("exists", accountService.exists(appId, userId))));
|
return ResponseEntity.ok(ApiResponse.success(Map.of("exists", accountService.exists(appKey, userId))));
|
||||||
}
|
}
|
||||||
|
|
||||||
public record ImportAccountRequest(
|
public record ImportAccountRequest(
|
||||||
|
|||||||
@ -24,7 +24,7 @@ public class AuthController {
|
|||||||
|
|
||||||
@PostMapping("/login")
|
@PostMapping("/login")
|
||||||
public ResponseEntity<ApiResponse<Map<String, Object>>> login(
|
public ResponseEntity<ApiResponse<Map<String, Object>>> login(
|
||||||
@RequestParam @NotBlank String appId,
|
@RequestParam @NotBlank String appKey,
|
||||||
@RequestParam @NotBlank String userId,
|
@RequestParam @NotBlank String userId,
|
||||||
@RequestHeader(value = "X-App-Timestamp", required = false) String timestamp,
|
@RequestHeader(value = "X-App-Timestamp", required = false) String timestamp,
|
||||||
@RequestHeader(value = "X-App-Nonce", required = false) String nonce,
|
@RequestHeader(value = "X-App-Nonce", required = false) String nonce,
|
||||||
@ -32,8 +32,8 @@ public class AuthController {
|
|||||||
if (timestamp == null || nonce == null || signature == null) {
|
if (timestamp == null || nonce == null || signature == null) {
|
||||||
return ResponseEntity.status(401).body(ApiResponse.error(401, "Missing app signature"));
|
return ResponseEntity.status(401).body(ApiResponse.error(401, "Missing app signature"));
|
||||||
}
|
}
|
||||||
accountService.validateSignature(appId, userId, timestamp, nonce, signature);
|
accountService.validateSignature(appKey, userId, timestamp, nonce, signature);
|
||||||
ImAccountService.LoginResult result = accountService.loginOrRegister(appId, userId);
|
ImAccountService.LoginResult result = accountService.loginOrRegister(appKey, userId);
|
||||||
return ResponseEntity.ok(ApiResponse.success(Map.of("token", result.token())));
|
return ResponseEntity.ok(ApiResponse.success(Map.of("token", result.token())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,34 +27,34 @@ public class BlacklistController {
|
|||||||
@GetMapping
|
@GetMapping
|
||||||
public ResponseEntity<ApiResponse<List<ImBlacklistEntity>>> list(
|
public ResponseEntity<ApiResponse<List<ImBlacklistEntity>>> list(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId) {
|
@RequestParam String appKey) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(blacklistService.list(appId, userId)));
|
return ResponseEntity.ok(ApiResponse.success(blacklistService.list(appKey, userId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public ResponseEntity<ApiResponse<ImBlacklistEntity>> add(
|
public ResponseEntity<ApiResponse<ImBlacklistEntity>> add(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam String blockedUserId) {
|
@RequestParam String blockedUserId) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(blacklistService.add(appId, userId, blockedUserId)));
|
return ResponseEntity.ok(ApiResponse.success(blacklistService.add(appKey, userId, blockedUserId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping
|
@DeleteMapping
|
||||||
public ResponseEntity<ApiResponse<Void>> remove(
|
public ResponseEntity<ApiResponse<Void>> remove(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam String blockedUserId) {
|
@RequestParam String blockedUserId) {
|
||||||
blacklistService.remove(appId, userId, blockedUserId);
|
blacklistService.remove(appKey, userId, blockedUserId);
|
||||||
return ResponseEntity.ok(ApiResponse.ok());
|
return ResponseEntity.ok(ApiResponse.ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/check")
|
@GetMapping("/check")
|
||||||
public ResponseEntity<ApiResponse<BlacklistCheckResult>> check(
|
public ResponseEntity<ApiResponse<BlacklistCheckResult>> check(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam String targetUserId) {
|
@RequestParam String targetUserId) {
|
||||||
boolean blockedByMe = blacklistService.isBlocked(appId, userId, targetUserId);
|
boolean blockedByMe = blacklistService.isBlocked(appKey, userId, targetUserId);
|
||||||
boolean blockedByTarget = blacklistService.isBlocked(appId, targetUserId, userId);
|
boolean blockedByTarget = blacklistService.isBlocked(appKey, targetUserId, userId);
|
||||||
return ResponseEntity.ok(ApiResponse.success(
|
return ResponseEntity.ok(ApiResponse.success(
|
||||||
new BlacklistCheckResult(targetUserId, blockedByMe, blockedByTarget, blockedByMe || blockedByTarget)));
|
new BlacklistCheckResult(targetUserId, blockedByMe, blockedByTarget, blockedByMe || blockedByTarget)));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,45 +37,45 @@ public class ConversationController {
|
|||||||
@GetMapping("/conversations")
|
@GetMapping("/conversations")
|
||||||
public ResponseEntity<ApiResponse<List<ConversationView>>> conversations(
|
public ResponseEntity<ApiResponse<List<ConversationView>>> conversations(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam(defaultValue = "0") int page,
|
@RequestParam(defaultValue = "0") int page,
|
||||||
@RequestParam(defaultValue = "20") int size) {
|
@RequestParam(defaultValue = "20") int size) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(messageService.conversationViews(appId, userId, size)));
|
return ResponseEntity.ok(ApiResponse.success(messageService.conversationViews(appKey, userId, size)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/conversations/{targetId}/pinned")
|
@PutMapping("/conversations/{targetId}/pinned")
|
||||||
public ResponseEntity<ApiResponse<Void>> setPinned(
|
public ResponseEntity<ApiResponse<Void>> setPinned(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String targetId,
|
@PathVariable String targetId,
|
||||||
@RequestParam String chatType,
|
@RequestParam String chatType,
|
||||||
@RequestParam boolean pinned) {
|
@RequestParam boolean pinned) {
|
||||||
conversationStateService.setPinned(appId, userId, targetId, chatType, pinned);
|
conversationStateService.setPinned(appKey, userId, targetId, chatType, pinned);
|
||||||
return ResponseEntity.ok(ApiResponse.ok());
|
return ResponseEntity.ok(ApiResponse.ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/conversations/{targetId}/muted")
|
@PutMapping("/conversations/{targetId}/muted")
|
||||||
public ResponseEntity<ApiResponse<Void>> setMuted(
|
public ResponseEntity<ApiResponse<Void>> setMuted(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String targetId,
|
@PathVariable String targetId,
|
||||||
@RequestParam String chatType,
|
@RequestParam String chatType,
|
||||||
@RequestParam boolean muted) {
|
@RequestParam boolean muted) {
|
||||||
conversationStateService.setMuted(appId, userId, targetId, chatType, muted);
|
conversationStateService.setMuted(appKey, userId, targetId, chatType, muted);
|
||||||
return ResponseEntity.ok(ApiResponse.ok());
|
return ResponseEntity.ok(ApiResponse.ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/conversations/{targetId}/read")
|
@PutMapping("/conversations/{targetId}/read")
|
||||||
public ResponseEntity<ApiResponse<Void>> markRead(
|
public ResponseEntity<ApiResponse<Void>> markRead(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String targetId,
|
@PathVariable String targetId,
|
||||||
@RequestParam String chatType) {
|
@RequestParam String chatType) {
|
||||||
var state = conversationStateService.markRead(appId, userId, targetId, chatType);
|
var state = conversationStateService.markRead(appKey, userId, targetId, chatType);
|
||||||
if ("GROUP".equalsIgnoreCase(chatType)) {
|
if ("GROUP".equalsIgnoreCase(chatType)) {
|
||||||
messageService.syncGroupReadReceipt(appId, userId, targetId, state.getLastReadAt());
|
messageService.syncGroupReadReceipt(appKey, userId, targetId, state.getLastReadAt());
|
||||||
} else {
|
} else {
|
||||||
messageService.syncReadReceipt(appId, userId, targetId, chatType, state.getLastReadAt());
|
messageService.syncReadReceipt(appKey, userId, targetId, chatType, state.getLastReadAt());
|
||||||
}
|
}
|
||||||
return ResponseEntity.ok(ApiResponse.ok());
|
return ResponseEntity.ok(ApiResponse.ok());
|
||||||
}
|
}
|
||||||
@ -83,50 +83,50 @@ public class ConversationController {
|
|||||||
@PutMapping("/conversations/{targetId}/draft")
|
@PutMapping("/conversations/{targetId}/draft")
|
||||||
public ResponseEntity<ApiResponse<Void>> setDraft(
|
public ResponseEntity<ApiResponse<Void>> setDraft(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String targetId,
|
@PathVariable String targetId,
|
||||||
@RequestParam String chatType,
|
@RequestParam String chatType,
|
||||||
@RequestParam(required = false) String draft) {
|
@RequestParam(required = false) String draft) {
|
||||||
conversationStateService.setDraft(appId, userId, targetId, chatType, draft);
|
conversationStateService.setDraft(appKey, userId, targetId, chatType, draft);
|
||||||
return ResponseEntity.ok(ApiResponse.ok());
|
return ResponseEntity.ok(ApiResponse.ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/conversations/{targetId}/hidden")
|
@PutMapping("/conversations/{targetId}/hidden")
|
||||||
public ResponseEntity<ApiResponse<Void>> setHidden(
|
public ResponseEntity<ApiResponse<Void>> setHidden(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String targetId,
|
@PathVariable String targetId,
|
||||||
@RequestParam String chatType,
|
@RequestParam String chatType,
|
||||||
@RequestParam boolean hidden) {
|
@RequestParam boolean hidden) {
|
||||||
conversationStateService.setHidden(appId, userId, targetId, chatType, hidden);
|
conversationStateService.setHidden(appKey, userId, targetId, chatType, hidden);
|
||||||
return ResponseEntity.ok(ApiResponse.ok());
|
return ResponseEntity.ok(ApiResponse.ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/conversations/{targetId}/group")
|
@PutMapping("/conversations/{targetId}/group")
|
||||||
public ResponseEntity<ApiResponse<Void>> setConversationGroup(
|
public ResponseEntity<ApiResponse<Void>> setConversationGroup(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String targetId,
|
@PathVariable String targetId,
|
||||||
@RequestParam String chatType,
|
@RequestParam String chatType,
|
||||||
@RequestParam(required = false) String groupName) {
|
@RequestParam(required = false) String groupName) {
|
||||||
conversationStateService.setConversationGroup(appId, userId, targetId, chatType, groupName);
|
conversationStateService.setConversationGroup(appKey, userId, targetId, chatType, groupName);
|
||||||
return ResponseEntity.ok(ApiResponse.ok());
|
return ResponseEntity.ok(ApiResponse.ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/conversation-groups")
|
@GetMapping("/conversation-groups")
|
||||||
public ResponseEntity<ApiResponse<List<String>>> listConversationGroups(
|
public ResponseEntity<ApiResponse<List<String>>> listConversationGroups(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId) {
|
@RequestParam String appKey) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(conversationStateService.listConversationGroups(appId, userId)));
|
return ResponseEntity.ok(ApiResponse.success(conversationStateService.listConversationGroups(appKey, userId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/conversation-groups/{groupName}")
|
@GetMapping("/conversation-groups/{groupName}")
|
||||||
public ResponseEntity<ApiResponse<List<Map<String, String>>>> listConversationGroupItems(
|
public ResponseEntity<ApiResponse<List<Map<String, String>>>> listConversationGroupItems(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String groupName) {
|
@PathVariable String groupName) {
|
||||||
List<Map<String, String>> items = conversationStateService
|
List<Map<String, String>> items = conversationStateService
|
||||||
.listByConversationGroup(appId, userId, groupName)
|
.listByConversationGroup(appKey, userId, groupName)
|
||||||
.stream()
|
.stream()
|
||||||
.map(state -> Map.of(
|
.map(state -> Map.of(
|
||||||
"targetId", state.getTargetId(),
|
"targetId", state.getTargetId(),
|
||||||
@ -140,11 +140,11 @@ public class ConversationController {
|
|||||||
@DeleteMapping("/conversations/{targetId}")
|
@DeleteMapping("/conversations/{targetId}")
|
||||||
public ResponseEntity<ApiResponse<Void>> deleteConversation(
|
public ResponseEntity<ApiResponse<Void>> deleteConversation(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String targetId,
|
@PathVariable String targetId,
|
||||||
@RequestParam String chatType) {
|
@RequestParam String chatType) {
|
||||||
boolean syncAcrossClients = featureConfigClient.multiClientConversationDeleteSync(appId);
|
boolean syncAcrossClients = featureConfigClient.multiClientConversationDeleteSync(appKey);
|
||||||
conversationStateService.deleteConversation(appId, userId, targetId, chatType, syncAcrossClients);
|
conversationStateService.deleteConversation(appKey, userId, targetId, chatType, syncAcrossClients);
|
||||||
return ResponseEntity.ok(ApiResponse.ok());
|
return ResponseEntity.ok(ApiResponse.ok());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,8 +32,8 @@ public class FriendController {
|
|||||||
@GetMapping
|
@GetMapping
|
||||||
public ResponseEntity<ApiResponse<List<String>>> listFriends(
|
public ResponseEntity<ApiResponse<List<String>>> listFriends(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId) {
|
@RequestParam String appKey) {
|
||||||
List<String> friendIds = friendRepository.findByAppIdAndUserId(appId, userId)
|
List<String> friendIds = friendRepository.findByAppIdAndUserId(appKey, userId)
|
||||||
.stream()
|
.stream()
|
||||||
.map(ImFriendEntity::getFriendId)
|
.map(ImFriendEntity::getFriendId)
|
||||||
.toList();
|
.toList();
|
||||||
@ -43,22 +43,22 @@ public class FriendController {
|
|||||||
@PostMapping
|
@PostMapping
|
||||||
public ResponseEntity<ApiResponse<ImFriendEntity>> addFriend(
|
public ResponseEntity<ApiResponse<ImFriendEntity>> addFriend(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam String friendId) {
|
@RequestParam String friendId) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(addFriendLink(appId, userId, friendId)));
|
return ResponseEntity.ok(ApiResponse.success(addFriendLink(appKey, userId, friendId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/batch")
|
@PostMapping("/batch")
|
||||||
public ResponseEntity<ApiResponse<List<ImFriendEntity>>> addFriends(
|
public ResponseEntity<ApiResponse<List<ImFriendEntity>>> addFriends(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestBody FriendBatchRequest req) {
|
@RequestBody FriendBatchRequest req) {
|
||||||
List<ImFriendEntity> links = new ArrayList<>();
|
List<ImFriendEntity> links = new ArrayList<>();
|
||||||
for (String friendId : unique(req.friendIds())) {
|
for (String friendId : unique(req.friendIds())) {
|
||||||
if (friendId == null || friendId.isBlank() || userId.equals(friendId)) {
|
if (friendId == null || friendId.isBlank() || userId.equals(friendId)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
links.add(addFriendLink(appId, userId, friendId));
|
links.add(addFriendLink(appKey, userId, friendId));
|
||||||
}
|
}
|
||||||
return ResponseEntity.ok(ApiResponse.success(links));
|
return ResponseEntity.ok(ApiResponse.success(links));
|
||||||
}
|
}
|
||||||
@ -67,32 +67,32 @@ public class FriendController {
|
|||||||
public ResponseEntity<ApiResponse<Void>> removeFriend(
|
public ResponseEntity<ApiResponse<Void>> removeFriend(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@PathVariable String friendId,
|
@PathVariable String friendId,
|
||||||
@RequestParam String appId) {
|
@RequestParam String appKey) {
|
||||||
friendRepository.deleteByAppIdAndUserIdAndFriendId(appId, userId, friendId);
|
friendRepository.deleteByAppIdAndUserIdAndFriendId(appKey, userId, friendId);
|
||||||
friendRepository.deleteByAppIdAndUserIdAndFriendId(appId, friendId, userId);
|
friendRepository.deleteByAppIdAndUserIdAndFriendId(appKey, friendId, userId);
|
||||||
return ResponseEntity.ok(ApiResponse.success(null));
|
return ResponseEntity.ok(ApiResponse.success(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping
|
@DeleteMapping
|
||||||
public ResponseEntity<ApiResponse<Void>> removeAllFriends(
|
public ResponseEntity<ApiResponse<Void>> removeAllFriends(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId) {
|
@RequestParam String appKey) {
|
||||||
friendRepository.deleteByAppIdAndUserId(appId, userId);
|
friendRepository.deleteByAppIdAndUserId(appKey, userId);
|
||||||
friendRepository.deleteByAppIdAndFriendId(appId, userId);
|
friendRepository.deleteByAppIdAndFriendId(appKey, userId);
|
||||||
return ResponseEntity.ok(ApiResponse.ok());
|
return ResponseEntity.ok(ApiResponse.ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/batch/remove")
|
@PostMapping("/batch/remove")
|
||||||
public ResponseEntity<ApiResponse<Void>> removeFriends(
|
public ResponseEntity<ApiResponse<Void>> removeFriends(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestBody FriendBatchRequest req) {
|
@RequestBody FriendBatchRequest req) {
|
||||||
for (String friendId : unique(req.friendIds())) {
|
for (String friendId : unique(req.friendIds())) {
|
||||||
if (friendId == null || friendId.isBlank() || userId.equals(friendId)) {
|
if (friendId == null || friendId.isBlank() || userId.equals(friendId)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
friendRepository.deleteByAppIdAndUserIdAndFriendId(appId, userId, friendId);
|
friendRepository.deleteByAppIdAndUserIdAndFriendId(appKey, userId, friendId);
|
||||||
friendRepository.deleteByAppIdAndUserIdAndFriendId(appId, friendId, userId);
|
friendRepository.deleteByAppIdAndUserIdAndFriendId(appKey, friendId, userId);
|
||||||
}
|
}
|
||||||
return ResponseEntity.ok(ApiResponse.success(null));
|
return ResponseEntity.ok(ApiResponse.success(null));
|
||||||
}
|
}
|
||||||
@ -101,10 +101,10 @@ public class FriendController {
|
|||||||
public ResponseEntity<ApiResponse<ImFriendEntity>> setFriendGroup(
|
public ResponseEntity<ApiResponse<ImFriendEntity>> setFriendGroup(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@PathVariable String friendId,
|
@PathVariable String friendId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam(required = false) String groupName) {
|
@RequestParam(required = false) String groupName) {
|
||||||
ImFriendEntity link = friendRepository.findByAppIdAndUserIdAndFriendId(appId, userId, friendId)
|
ImFriendEntity link = friendRepository.findByAppIdAndUserIdAndFriendId(appKey, userId, friendId)
|
||||||
.orElseGet(() -> addFriendLink(appId, userId, friendId));
|
.orElseGet(() -> addFriendLink(appKey, userId, friendId));
|
||||||
link.setFriendGroup(normalizeGroup(groupName));
|
link.setFriendGroup(normalizeGroup(groupName));
|
||||||
return ResponseEntity.ok(ApiResponse.success(friendRepository.save(link)));
|
return ResponseEntity.ok(ApiResponse.success(friendRepository.save(link)));
|
||||||
}
|
}
|
||||||
@ -112,8 +112,8 @@ public class FriendController {
|
|||||||
@GetMapping("/groups")
|
@GetMapping("/groups")
|
||||||
public ResponseEntity<ApiResponse<List<String>>> listFriendGroups(
|
public ResponseEntity<ApiResponse<List<String>>> listFriendGroups(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId) {
|
@RequestParam String appKey) {
|
||||||
List<String> groups = friendRepository.findByAppIdAndUserId(appId, userId).stream()
|
List<String> groups = friendRepository.findByAppIdAndUserId(appKey, userId).stream()
|
||||||
.map(ImFriendEntity::getFriendGroup)
|
.map(ImFriendEntity::getFriendGroup)
|
||||||
.filter(group -> group != null && !group.isBlank())
|
.filter(group -> group != null && !group.isBlank())
|
||||||
.distinct()
|
.distinct()
|
||||||
@ -125,31 +125,31 @@ public class FriendController {
|
|||||||
@GetMapping("/groups/{groupName}")
|
@GetMapping("/groups/{groupName}")
|
||||||
public ResponseEntity<ApiResponse<List<String>>> listFriendsByGroup(
|
public ResponseEntity<ApiResponse<List<String>>> listFriendsByGroup(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String groupName) {
|
@PathVariable String groupName) {
|
||||||
List<String> friendIds = friendRepository
|
List<String> friendIds = friendRepository
|
||||||
.findByAppIdAndUserIdAndFriendGroup(appId, userId, normalizeGroup(groupName))
|
.findByAppIdAndUserIdAndFriendGroup(appKey, userId, normalizeGroup(groupName))
|
||||||
.stream()
|
.stream()
|
||||||
.map(ImFriendEntity::getFriendId)
|
.map(ImFriendEntity::getFriendId)
|
||||||
.toList();
|
.toList();
|
||||||
return ResponseEntity.ok(ApiResponse.success(friendIds));
|
return ResponseEntity.ok(ApiResponse.success(friendIds));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImFriendEntity addFriendLink(String appId, String userId, String friendId) {
|
private ImFriendEntity addFriendLink(String appKey, String userId, String friendId) {
|
||||||
ImFriendEntity forward = friendRepository
|
ImFriendEntity forward = friendRepository
|
||||||
.findByAppIdAndUserIdAndFriendId(appId, userId, friendId)
|
.findByAppIdAndUserIdAndFriendId(appKey, userId, friendId)
|
||||||
.orElseGet(() -> {
|
.orElseGet(() -> {
|
||||||
ImFriendEntity e = new ImFriendEntity();
|
ImFriendEntity e = new ImFriendEntity();
|
||||||
e.setAppId(appId);
|
e.setAppId(appKey);
|
||||||
e.setUserId(userId);
|
e.setUserId(userId);
|
||||||
e.setFriendId(friendId);
|
e.setFriendId(friendId);
|
||||||
return friendRepository.save(e);
|
return friendRepository.save(e);
|
||||||
});
|
});
|
||||||
|
|
||||||
friendRepository.findByAppIdAndUserIdAndFriendId(appId, friendId, userId)
|
friendRepository.findByAppIdAndUserIdAndFriendId(appKey, friendId, userId)
|
||||||
.orElseGet(() -> {
|
.orElseGet(() -> {
|
||||||
ImFriendEntity e = new ImFriendEntity();
|
ImFriendEntity e = new ImFriendEntity();
|
||||||
e.setAppId(appId);
|
e.setAppId(appKey);
|
||||||
e.setUserId(friendId);
|
e.setUserId(friendId);
|
||||||
e.setFriendId(userId);
|
e.setFriendId(userId);
|
||||||
return friendRepository.save(e);
|
return friendRepository.save(e);
|
||||||
@ -168,12 +168,12 @@ public class FriendController {
|
|||||||
@PostMapping("/check")
|
@PostMapping("/check")
|
||||||
public ResponseEntity<ApiResponse<List<FriendCheckResult>>> checkFriends(
|
public ResponseEntity<ApiResponse<List<FriendCheckResult>>> checkFriends(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestBody FriendCheckRequest req) {
|
@RequestBody FriendCheckRequest req) {
|
||||||
List<FriendCheckResult> results = new ArrayList<>();
|
List<FriendCheckResult> results = new ArrayList<>();
|
||||||
for (String friendId : req.friendIds() == null ? List.<String>of() : req.friendIds()) {
|
for (String friendId : req.friendIds() == null ? List.<String>of() : req.friendIds()) {
|
||||||
boolean isFriend = friendRepository.existsByAppIdAndUserIdAndFriendId(appId, userId, friendId)
|
boolean isFriend = friendRepository.existsByAppIdAndUserIdAndFriendId(appKey, userId, friendId)
|
||||||
|| friendRepository.existsByAppIdAndUserIdAndFriendId(appId, friendId, userId);
|
|| friendRepository.existsByAppIdAndUserIdAndFriendId(appKey, friendId, userId);
|
||||||
results.add(new FriendCheckResult(friendId, isFriend));
|
results.add(new FriendCheckResult(friendId, isFriend));
|
||||||
}
|
}
|
||||||
return ResponseEntity.ok(ApiResponse.success(results));
|
return ResponseEntity.ok(ApiResponse.success(results));
|
||||||
|
|||||||
@ -28,53 +28,53 @@ public class FriendRequestController {
|
|||||||
@GetMapping
|
@GetMapping
|
||||||
public ResponseEntity<ApiResponse<List<ImFriendRequestEntity>>> list(
|
public ResponseEntity<ApiResponse<List<ImFriendRequestEntity>>> list(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam(defaultValue = "incoming") String direction) {
|
@RequestParam(defaultValue = "incoming") String direction) {
|
||||||
List<ImFriendRequestEntity> list = "outgoing".equalsIgnoreCase(direction)
|
List<ImFriendRequestEntity> list = "outgoing".equalsIgnoreCase(direction)
|
||||||
? friendRequestService.outgoing(appId, userId)
|
? friendRequestService.outgoing(appKey, userId)
|
||||||
: friendRequestService.incoming(appId, userId);
|
: friendRequestService.incoming(appKey, userId);
|
||||||
return ResponseEntity.ok(ApiResponse.success(list));
|
return ResponseEntity.ok(ApiResponse.success(list));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public ResponseEntity<ApiResponse<ImFriendRequestEntity>> send(
|
public ResponseEntity<ApiResponse<ImFriendRequestEntity>> send(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam String toUserId,
|
@RequestParam String toUserId,
|
||||||
@RequestParam(required = false) String remark) {
|
@RequestParam(required = false) String remark) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(friendRequestService.send(appId, userId, toUserId, remark)));
|
return ResponseEntity.ok(ApiResponse.success(friendRequestService.send(appKey, userId, toUserId, remark)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{requestId}/accept")
|
@PostMapping("/{requestId}/accept")
|
||||||
public ResponseEntity<ApiResponse<ImFriendRequestEntity>> accept(
|
public ResponseEntity<ApiResponse<ImFriendRequestEntity>> accept(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String requestId) {
|
@PathVariable String requestId) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(friendRequestService.accept(appId, requestId, userId)));
|
return ResponseEntity.ok(ApiResponse.success(friendRequestService.accept(appKey, requestId, userId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{requestId}/reject")
|
@PostMapping("/{requestId}/reject")
|
||||||
public ResponseEntity<ApiResponse<ImFriendRequestEntity>> reject(
|
public ResponseEntity<ApiResponse<ImFriendRequestEntity>> reject(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String requestId) {
|
@PathVariable String requestId) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(friendRequestService.reject(appId, requestId, userId)));
|
return ResponseEntity.ok(ApiResponse.success(friendRequestService.reject(appKey, requestId, userId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/batch/accept")
|
@PostMapping("/batch/accept")
|
||||||
public ResponseEntity<ApiResponse<List<ImFriendRequestEntity>>> acceptBatch(
|
public ResponseEntity<ApiResponse<List<ImFriendRequestEntity>>> acceptBatch(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestBody BatchRequest req) {
|
@RequestBody BatchRequest req) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(friendRequestService.acceptBatch(appId, req.requestIds(), userId)));
|
return ResponseEntity.ok(ApiResponse.success(friendRequestService.acceptBatch(appKey, req.requestIds(), userId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/batch/reject")
|
@PostMapping("/batch/reject")
|
||||||
public ResponseEntity<ApiResponse<List<ImFriendRequestEntity>>> rejectBatch(
|
public ResponseEntity<ApiResponse<List<ImFriendRequestEntity>>> rejectBatch(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestBody BatchRequest req) {
|
@RequestBody BatchRequest req) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(friendRequestService.rejectBatch(appId, req.requestIds(), userId)));
|
return ResponseEntity.ok(ApiResponse.success(friendRequestService.rejectBatch(appKey, req.requestIds(), userId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public record BatchRequest(List<String> requestIds) {}
|
public record BatchRequest(List<String> requestIds) {}
|
||||||
|
|||||||
@ -33,49 +33,49 @@ public class GroupController {
|
|||||||
public ResponseEntity<ApiResponse<ImGroupEntity>> create(
|
public ResponseEntity<ApiResponse<ImGroupEntity>> create(
|
||||||
@RequestBody CreateGroupRequest req,
|
@RequestBody CreateGroupRequest req,
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId) {
|
@RequestParam String appKey) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(
|
return ResponseEntity.ok(ApiResponse.success(
|
||||||
groupService.create(appId, req.name(), userId, req.memberIds(), req.groupType(), null)));
|
groupService.create(appKey, req.name(), userId, req.memberIds(), req.groupType(), null)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public ResponseEntity<ApiResponse<List<ImGroupEntity>>> list(
|
public ResponseEntity<ApiResponse<List<ImGroupEntity>>> list(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@AuthenticationPrincipal String userId) {
|
@AuthenticationPrincipal String userId) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(groupService.listUserGroups(appId, userId)));
|
return ResponseEntity.ok(ApiResponse.success(groupService.listUserGroups(appKey, userId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/public")
|
@GetMapping("/public")
|
||||||
public ResponseEntity<ApiResponse<List<ImGroupEntity>>> listPublic(
|
public ResponseEntity<ApiResponse<List<ImGroupEntity>>> listPublic(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam(required = false) String keyword) {
|
@RequestParam(required = false) String keyword) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(groupService.listPublicGroups(appId, keyword)));
|
return ResponseEntity.ok(ApiResponse.success(groupService.listPublicGroups(appKey, keyword)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/search")
|
@GetMapping("/search")
|
||||||
public ResponseEntity<ApiResponse<List<ImGroupEntity>>> search(
|
public ResponseEntity<ApiResponse<List<ImGroupEntity>>> search(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam String keyword,
|
@RequestParam String keyword,
|
||||||
@RequestParam(defaultValue = "20") int size) {
|
@RequestParam(defaultValue = "20") int size) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(groupService.searchGroups(appId, keyword, size)));
|
return ResponseEntity.ok(ApiResponse.success(groupService.searchGroups(appKey, keyword, size)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{groupId}/members")
|
@GetMapping("/{groupId}/members")
|
||||||
public ResponseEntity<ApiResponse<List<ImAccountEntity>>> listMembers(
|
public ResponseEntity<ApiResponse<List<ImAccountEntity>>> listMembers(
|
||||||
@PathVariable String groupId,
|
@PathVariable String groupId,
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId) {
|
@RequestParam String appKey) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(groupService.listMembers(appId, groupId, userId)));
|
return ResponseEntity.ok(ApiResponse.success(groupService.listMembers(appKey, groupId, userId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{groupId}/members/search")
|
@GetMapping("/{groupId}/members/search")
|
||||||
public ResponseEntity<ApiResponse<List<ImAccountEntity>>> searchMembers(
|
public ResponseEntity<ApiResponse<List<ImAccountEntity>>> searchMembers(
|
||||||
@PathVariable String groupId,
|
@PathVariable String groupId,
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam String keyword,
|
@RequestParam String keyword,
|
||||||
@RequestParam(defaultValue = "20") int size) {
|
@RequestParam(defaultValue = "20") int size) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(groupService.searchMembers(appId, groupId, userId, keyword, size)));
|
return ResponseEntity.ok(ApiResponse.success(groupService.searchMembers(appKey, groupId, userId, keyword, size)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/{groupId}")
|
@PutMapping("/{groupId}")
|
||||||
@ -176,17 +176,17 @@ public class GroupController {
|
|||||||
public ResponseEntity<ApiResponse<ImGroupJoinRequestEntity>> sendJoinRequest(
|
public ResponseEntity<ApiResponse<ImGroupJoinRequestEntity>> sendJoinRequest(
|
||||||
@PathVariable String groupId,
|
@PathVariable String groupId,
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam(required = false) String remark) {
|
@RequestParam(required = false) String remark) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(groupService.sendJoinRequest(appId, groupId, userId, remark)));
|
return ResponseEntity.ok(ApiResponse.success(groupService.sendJoinRequest(appKey, groupId, userId, remark)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{groupId}/join-requests")
|
@GetMapping("/{groupId}/join-requests")
|
||||||
public ResponseEntity<ApiResponse<List<ImGroupJoinRequestEntity>>> listJoinRequests(
|
public ResponseEntity<ApiResponse<List<ImGroupJoinRequestEntity>>> listJoinRequests(
|
||||||
@PathVariable String groupId,
|
@PathVariable String groupId,
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId) {
|
@RequestParam String appKey) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(groupService.listJoinRequests(appId, groupId, userId)));
|
return ResponseEntity.ok(ApiResponse.success(groupService.listJoinRequests(appKey, groupId, userId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{groupId}/join-requests/{requestId}/accept")
|
@PostMapping("/{groupId}/join-requests/{requestId}/accept")
|
||||||
@ -194,8 +194,8 @@ public class GroupController {
|
|||||||
@PathVariable String groupId,
|
@PathVariable String groupId,
|
||||||
@PathVariable String requestId,
|
@PathVariable String requestId,
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId) {
|
@RequestParam String appKey) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(groupService.acceptJoinRequest(appId, requestId, userId)));
|
return ResponseEntity.ok(ApiResponse.success(groupService.acceptJoinRequest(appKey, requestId, userId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{groupId}/join-requests/{requestId}/reject")
|
@PostMapping("/{groupId}/join-requests/{requestId}/reject")
|
||||||
@ -203,28 +203,28 @@ public class GroupController {
|
|||||||
@PathVariable String groupId,
|
@PathVariable String groupId,
|
||||||
@PathVariable String requestId,
|
@PathVariable String requestId,
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId) {
|
@RequestParam String appKey) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(groupService.rejectJoinRequest(appId, requestId, userId)));
|
return ResponseEntity.ok(ApiResponse.success(groupService.rejectJoinRequest(appKey, requestId, userId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{groupId}/join-requests/batch/accept")
|
@PostMapping("/{groupId}/join-requests/batch/accept")
|
||||||
public ResponseEntity<ApiResponse<List<ImGroupJoinRequestEntity>>> acceptJoinRequests(
|
public ResponseEntity<ApiResponse<List<ImGroupJoinRequestEntity>>> acceptJoinRequests(
|
||||||
@PathVariable String groupId,
|
@PathVariable String groupId,
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestBody RequestBatch req) {
|
@RequestBody RequestBatch req) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(
|
return ResponseEntity.ok(ApiResponse.success(
|
||||||
groupService.acceptJoinRequests(appId, groupId, req.requestIds(), userId)));
|
groupService.acceptJoinRequests(appKey, groupId, req.requestIds(), userId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{groupId}/join-requests/batch/reject")
|
@PostMapping("/{groupId}/join-requests/batch/reject")
|
||||||
public ResponseEntity<ApiResponse<List<ImGroupJoinRequestEntity>>> rejectJoinRequests(
|
public ResponseEntity<ApiResponse<List<ImGroupJoinRequestEntity>>> rejectJoinRequests(
|
||||||
@PathVariable String groupId,
|
@PathVariable String groupId,
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestBody RequestBatch req) {
|
@RequestBody RequestBatch req) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(
|
return ResponseEntity.ok(ApiResponse.success(
|
||||||
groupService.rejectJoinRequests(appId, groupId, req.requestIds(), userId)));
|
groupService.rejectJoinRequests(appKey, groupId, req.requestIds(), userId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/{groupId}/members/{userId}/info")
|
@PutMapping("/{groupId}/members/{userId}/info")
|
||||||
|
|||||||
@ -102,24 +102,24 @@ public class ImAdminController {
|
|||||||
this.userPresenceService = userPresenceService;
|
this.userPresenceService = userPresenceService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** List all registered IM users for the given appId. */
|
/** List all registered IM users for the given appKey. */
|
||||||
@GetMapping("/users")
|
@GetMapping("/users")
|
||||||
public ResponseEntity<ApiResponse<Page<ImAccountEntity>>> listUsers(
|
public ResponseEntity<ApiResponse<Page<ImAccountEntity>>> listUsers(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam(defaultValue = "0") int page,
|
@RequestParam(defaultValue = "0") int page,
|
||||||
@RequestParam(defaultValue = "20") int size) {
|
@RequestParam(defaultValue = "20") int size) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(
|
return ResponseEntity.ok(ApiResponse.success(
|
||||||
accountRepository.findByAppId(appId, PageRequest.of(page, size))));
|
accountRepository.findByAppId(appKey, PageRequest.of(page, size))));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Ban or unban a user. */
|
/** Ban or unban a user. */
|
||||||
@PutMapping("/users/{userId}/status")
|
@PutMapping("/users/{userId}/status")
|
||||||
public ResponseEntity<ApiResponse<ImAccountEntity>> updateUserStatus(
|
public ResponseEntity<ApiResponse<ImAccountEntity>> updateUserStatus(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String userId,
|
@PathVariable String userId,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@RequestBody Map<String, String> body) {
|
@RequestBody Map<String, String> body) {
|
||||||
ImAccountEntity account = accountRepository.findByAppIdAndUserId(appId, userId)
|
ImAccountEntity account = accountRepository.findByAppIdAndUserId(appKey, userId)
|
||||||
.orElseThrow(() -> new BusinessException(404, "账号不存在"));
|
.orElseThrow(() -> new BusinessException(404, "账号不存在"));
|
||||||
String status = body.get("status");
|
String status = body.get("status");
|
||||||
if (status == null || status.isBlank()) {
|
if (status == null || status.isBlank()) {
|
||||||
@ -127,72 +127,72 @@ public class ImAdminController {
|
|||||||
}
|
}
|
||||||
account.setStatus(ImAccountEntity.Status.valueOf(status.toUpperCase()));
|
account.setStatus(ImAccountEntity.Status.valueOf(status.toUpperCase()));
|
||||||
ImAccountEntity saved = accountRepository.save(account);
|
ImAccountEntity saved = accountRepository.save(account);
|
||||||
operationLogService.record(appId, operatorId, "UPDATE_USER_STATUS", "ACCOUNT", userId, status);
|
operationLogService.record(appKey, operatorId, "UPDATE_USER_STATUS", "ACCOUNT", userId, status);
|
||||||
return ResponseEntity.ok(ApiResponse.success(saved));
|
return ResponseEntity.ok(ApiResponse.success(saved));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Update a registered IM user profile without changing userId. */
|
/** Update a registered IM user profile without changing userId. */
|
||||||
@PutMapping("/users/{userId}")
|
@PutMapping("/users/{userId}")
|
||||||
public ResponseEntity<ApiResponse<ImAccountEntity>> updateUser(
|
public ResponseEntity<ApiResponse<ImAccountEntity>> updateUser(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String userId,
|
@PathVariable String userId,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@RequestBody UpdateUserRequest req) {
|
@RequestBody UpdateUserRequest req) {
|
||||||
ImAccountEntity saved = accountService.updateAccount(
|
ImAccountEntity saved = accountService.updateAccount(
|
||||||
appId,
|
appKey,
|
||||||
userId,
|
userId,
|
||||||
req.nickname(),
|
req.nickname(),
|
||||||
req.avatar(),
|
req.avatar(),
|
||||||
req.gender(),
|
req.gender(),
|
||||||
req.status());
|
req.status());
|
||||||
operationLogService.record(appId, operatorId, "UPDATE_USER", "ACCOUNT", userId, req.nickname());
|
operationLogService.record(appKey, operatorId, "UPDATE_USER", "ACCOUNT", userId, req.nickname());
|
||||||
return ResponseEntity.ok(ApiResponse.success(saved));
|
return ResponseEntity.ok(ApiResponse.success(saved));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** List all groups for the given appId. */
|
/** List all groups for the given appKey. */
|
||||||
@GetMapping("/groups")
|
@GetMapping("/groups")
|
||||||
public ResponseEntity<ApiResponse<List<ImGroupEntity>>> listGroups(@RequestParam String appId) {
|
public ResponseEntity<ApiResponse<List<ImGroupEntity>>> listGroups(@RequestParam String appKey) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(groupRepository.findByAppId(appId)));
|
return ResponseEntity.ok(ApiResponse.success(groupRepository.findByAppId(appKey)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Admin registers a new IM user (or returns existing). */
|
/** Admin registers a new IM user (or returns existing). */
|
||||||
@PostMapping("/users")
|
@PostMapping("/users")
|
||||||
public ResponseEntity<ApiResponse<ImAccountEntity>> registerUser(
|
public ResponseEntity<ApiResponse<ImAccountEntity>> registerUser(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@RequestBody RegisterUserRequest req) {
|
@RequestBody RegisterUserRequest req) {
|
||||||
ImAccountEntity account = accountService.importAccount(
|
ImAccountEntity account = accountService.importAccount(
|
||||||
appId,
|
appKey,
|
||||||
req.userId(),
|
req.userId(),
|
||||||
req.nickname(),
|
req.nickname(),
|
||||||
req.avatar(),
|
req.avatar(),
|
||||||
req.gender(),
|
req.gender(),
|
||||||
req.status());
|
req.status());
|
||||||
operationLogService.record(appId, operatorId, "REGISTER_USER", "ACCOUNT", req.userId(), req.nickname());
|
operationLogService.record(appKey, operatorId, "REGISTER_USER", "ACCOUNT", req.userId(), req.nickname());
|
||||||
return ResponseEntity.ok(ApiResponse.success(account));
|
return ResponseEntity.ok(ApiResponse.success(account));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Admin creates a group. */
|
/** Admin creates a group. */
|
||||||
@PostMapping("/groups")
|
@PostMapping("/groups")
|
||||||
public ResponseEntity<ApiResponse<ImGroupEntity>> createGroup(
|
public ResponseEntity<ApiResponse<ImGroupEntity>> createGroup(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@RequestBody CreateGroupRequest req) {
|
@RequestBody CreateGroupRequest req) {
|
||||||
ImGroupEntity group = groupService.create(
|
ImGroupEntity group = groupService.create(
|
||||||
appId,
|
appKey,
|
||||||
req.name(),
|
req.name(),
|
||||||
req.creatorId(),
|
req.creatorId(),
|
||||||
req.memberIds(),
|
req.memberIds(),
|
||||||
req.groupType(),
|
req.groupType(),
|
||||||
req.announcement());
|
req.announcement());
|
||||||
operationLogService.record(appId, operatorId, "CREATE_GROUP", "GROUP", group.getId(), group.getName());
|
operationLogService.record(appKey, operatorId, "CREATE_GROUP", "GROUP", group.getId(), group.getName());
|
||||||
return ResponseEntity.ok(ApiResponse.success(group));
|
return ResponseEntity.ok(ApiResponse.success(group));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Admin updates a group without changing its id. */
|
/** Admin updates a group without changing its id. */
|
||||||
@PutMapping("/groups/{groupId}")
|
@PutMapping("/groups/{groupId}")
|
||||||
public ResponseEntity<ApiResponse<ImGroupEntity>> updateGroup(
|
public ResponseEntity<ApiResponse<ImGroupEntity>> updateGroup(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String groupId,
|
@PathVariable String groupId,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@RequestBody UpdateGroupRequest req) {
|
@RequestBody UpdateGroupRequest req) {
|
||||||
@ -202,38 +202,38 @@ public class ImAdminController {
|
|||||||
req.name(),
|
req.name(),
|
||||||
req.groupType(),
|
req.groupType(),
|
||||||
req.announcement());
|
req.announcement());
|
||||||
operationLogService.record(appId, operatorId, "UPDATE_GROUP", "GROUP", groupId, req.name());
|
operationLogService.record(appKey, operatorId, "UPDATE_GROUP", "GROUP", groupId, req.name());
|
||||||
return ResponseEntity.ok(ApiResponse.success(group));
|
return ResponseEntity.ok(ApiResponse.success(group));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Fuzzy search users by userId or nickname. */
|
/** Fuzzy search users by userId or nickname. */
|
||||||
@GetMapping("/users/search")
|
@GetMapping("/users/search")
|
||||||
public ResponseEntity<ApiResponse<List<ImAccountEntity>>> searchUsers(
|
public ResponseEntity<ApiResponse<List<ImAccountEntity>>> searchUsers(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@RequestParam String keyword,
|
@RequestParam String keyword,
|
||||||
@RequestParam(defaultValue = "20") int size) {
|
@RequestParam(defaultValue = "20") int size) {
|
||||||
List<ImAccountEntity> results = accountRepository.searchByKeyword(appId, keyword, PageRequest.of(0, size));
|
List<ImAccountEntity> results = accountRepository.searchByKeyword(appKey, keyword, PageRequest.of(0, size));
|
||||||
operationLogService.record(appId, operatorId, "SEARCH_USERS", "ACCOUNT", null, keyword);
|
operationLogService.record(appKey, operatorId, "SEARCH_USERS", "ACCOUNT", null, keyword);
|
||||||
return ResponseEntity.ok(ApiResponse.success(results));
|
return ResponseEntity.ok(ApiResponse.success(results));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Fuzzy search groups by id, name, creator or announcement. */
|
/** Fuzzy search groups by id, name, creator or announcement. */
|
||||||
@GetMapping("/groups/search")
|
@GetMapping("/groups/search")
|
||||||
public ResponseEntity<ApiResponse<List<ImGroupEntity>>> searchGroups(
|
public ResponseEntity<ApiResponse<List<ImGroupEntity>>> searchGroups(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@RequestParam String keyword,
|
@RequestParam String keyword,
|
||||||
@RequestParam(defaultValue = "20") int size) {
|
@RequestParam(defaultValue = "20") int size) {
|
||||||
List<ImGroupEntity> results = groupRepository.searchByKeyword(appId, keyword, PageRequest.of(0, size));
|
List<ImGroupEntity> results = groupRepository.searchByKeyword(appKey, keyword, PageRequest.of(0, size));
|
||||||
operationLogService.record(appId, operatorId, "SEARCH_GROUPS", "GROUP", null, keyword);
|
operationLogService.record(appKey, operatorId, "SEARCH_GROUPS", "GROUP", null, keyword);
|
||||||
return ResponseEntity.ok(ApiResponse.success(results));
|
return ResponseEntity.ok(ApiResponse.success(results));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Search messages across the application. */
|
/** Search messages across the application. */
|
||||||
@GetMapping("/messages/search")
|
@GetMapping("/messages/search")
|
||||||
public ResponseEntity<ApiResponse<Page<ImMessageEntity>>> searchMessages(
|
public ResponseEntity<ApiResponse<Page<ImMessageEntity>>> searchMessages(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@RequestParam(required = false) ImMessageEntity.ChatType chatType,
|
@RequestParam(required = false) ImMessageEntity.ChatType chatType,
|
||||||
@RequestParam(required = false) ImMessageEntity.MsgType msgType,
|
@RequestParam(required = false) ImMessageEntity.MsgType msgType,
|
||||||
@ -242,22 +242,22 @@ public class ImAdminController {
|
|||||||
@RequestParam(required = false) LocalDateTime endTime,
|
@RequestParam(required = false) LocalDateTime endTime,
|
||||||
@RequestParam(defaultValue = "0") int page,
|
@RequestParam(defaultValue = "0") int page,
|
||||||
@RequestParam(defaultValue = "20") int size) {
|
@RequestParam(defaultValue = "20") int size) {
|
||||||
operationLogService.record(appId, operatorId, "SEARCH_MESSAGES", "MESSAGE", null, keyword);
|
operationLogService.record(appKey, operatorId, "SEARCH_MESSAGES", "MESSAGE", null, keyword);
|
||||||
return ResponseEntity.ok(ApiResponse.success(
|
return ResponseEntity.ok(ApiResponse.success(
|
||||||
messageRepository.searchByKeyword(
|
messageRepository.searchByKeyword(
|
||||||
appId, chatType, msgType, keyword, startTime, endTime, PageRequest.of(page, size))));
|
appKey, chatType, msgType, keyword, startTime, endTime, PageRequest.of(page, size))));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Message statistics for the given appId. */
|
/** Message statistics for the given appKey. */
|
||||||
@GetMapping("/stats")
|
@GetMapping("/stats")
|
||||||
public ResponseEntity<ApiResponse<Map<String, Object>>> stats(
|
public ResponseEntity<ApiResponse<Map<String, Object>>> stats(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@AuthenticationPrincipal String operatorId) {
|
@AuthenticationPrincipal String operatorId) {
|
||||||
long totalMessages = messageRepository.countByAppId(appId);
|
long totalMessages = messageRepository.countByAppId(appKey);
|
||||||
long totalUsers = accountRepository.countByAppId(appId);
|
long totalUsers = accountRepository.countByAppId(appKey);
|
||||||
long totalGroups = groupRepository.countByAppId(appId);
|
long totalGroups = groupRepository.countByAppId(appKey);
|
||||||
long todayMessages = messageRepository.countTodayByAppId(appId);
|
long todayMessages = messageRepository.countTodayByAppId(appKey);
|
||||||
operationLogService.record(appId, operatorId, "VIEW_STATS", "STATS", null, "summary");
|
operationLogService.record(appKey, operatorId, "VIEW_STATS", "STATS", null, "summary");
|
||||||
|
|
||||||
return ResponseEntity.ok(ApiResponse.success(Map.of(
|
return ResponseEntity.ok(ApiResponse.success(Map.of(
|
||||||
"totalMessages", totalMessages,
|
"totalMessages", totalMessages,
|
||||||
@ -270,7 +270,7 @@ public class ImAdminController {
|
|||||||
/** Admin queries conversation history between any two users. */
|
/** Admin queries conversation history between any two users. */
|
||||||
@GetMapping("/messages")
|
@GetMapping("/messages")
|
||||||
public ResponseEntity<ApiResponse<Page<com.xuqm.im.entity.ImMessageEntity>>> history(
|
public ResponseEntity<ApiResponse<Page<com.xuqm.im.entity.ImMessageEntity>>> history(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@RequestParam String userA,
|
@RequestParam String userA,
|
||||||
@RequestParam String userB,
|
@RequestParam String userB,
|
||||||
@ -280,270 +280,270 @@ public class ImAdminController {
|
|||||||
@RequestParam(required = false) LocalDateTime endTime,
|
@RequestParam(required = false) LocalDateTime endTime,
|
||||||
@RequestParam(defaultValue = "0") int page,
|
@RequestParam(defaultValue = "0") int page,
|
||||||
@RequestParam(defaultValue = "20") int size) {
|
@RequestParam(defaultValue = "20") int size) {
|
||||||
operationLogService.record(appId, operatorId, "VIEW_HISTORY", "MESSAGE", userA + "," + userB, keyword);
|
operationLogService.record(appKey, operatorId, "VIEW_HISTORY", "MESSAGE", userA + "," + userB, keyword);
|
||||||
return ResponseEntity.ok(ApiResponse.success(
|
return ResponseEntity.ok(ApiResponse.success(
|
||||||
messageRepository.findSingleConversationFiltered(
|
messageRepository.findSingleConversationFiltered(
|
||||||
appId, userA, userB, msgType, keyword, startTime, endTime, PageRequest.of(page, size))));
|
appKey, userA, userB, msgType, keyword, startTime, endTime, PageRequest.of(page, size))));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Admin revokes an arbitrary message. */
|
/** Admin revokes an arbitrary message. */
|
||||||
@PostMapping("/messages/{messageId}/revoke")
|
@PostMapping("/messages/{messageId}/revoke")
|
||||||
public ResponseEntity<ApiResponse<com.xuqm.im.entity.ImMessageEntity>> adminRevoke(
|
public ResponseEntity<ApiResponse<com.xuqm.im.entity.ImMessageEntity>> adminRevoke(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@PathVariable String messageId) {
|
@PathVariable String messageId) {
|
||||||
ImMessageEntity revoked = messageService.adminRevoke(appId, messageId);
|
ImMessageEntity revoked = messageService.adminRevoke(appKey, messageId);
|
||||||
operationLogService.record(appId, operatorId, "ADMIN_REVOKE_MESSAGE", "MESSAGE", messageId, null);
|
operationLogService.record(appKey, operatorId, "ADMIN_REVOKE_MESSAGE", "MESSAGE", messageId, null);
|
||||||
return ResponseEntity.ok(ApiResponse.success(revoked));
|
return ResponseEntity.ok(ApiResponse.success(revoked));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Admin force dismisses a group. */
|
/** Admin force dismisses a group. */
|
||||||
@DeleteMapping("/groups/{groupId}")
|
@DeleteMapping("/groups/{groupId}")
|
||||||
public ResponseEntity<ApiResponse<Void>> adminDismissGroup(
|
public ResponseEntity<ApiResponse<Void>> adminDismissGroup(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@PathVariable String groupId) {
|
@PathVariable String groupId) {
|
||||||
groupService.adminDismiss(groupId);
|
groupService.adminDismiss(groupId);
|
||||||
operationLogService.record(appId, operatorId, "ADMIN_DISMISS_GROUP", "GROUP", groupId, null);
|
operationLogService.record(appKey, operatorId, "ADMIN_DISMISS_GROUP", "GROUP", groupId, null);
|
||||||
return ResponseEntity.ok(ApiResponse.ok());
|
return ResponseEntity.ok(ApiResponse.ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/friend-requests")
|
@GetMapping("/friend-requests")
|
||||||
public ResponseEntity<ApiResponse<List<ImFriendRequestEntity>>> listFriendRequests(@RequestParam String appId) {
|
public ResponseEntity<ApiResponse<List<ImFriendRequestEntity>>> listFriendRequests(@RequestParam String appKey) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(friendRequestService.listByApp(appId)));
|
return ResponseEntity.ok(ApiResponse.success(friendRequestService.listByApp(appKey)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/friend-requests")
|
@PostMapping("/friend-requests")
|
||||||
public ResponseEntity<ApiResponse<ImFriendRequestEntity>> createFriendRequest(
|
public ResponseEntity<ApiResponse<ImFriendRequestEntity>> createFriendRequest(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@RequestBody FriendRequestCreateRequest req) {
|
@RequestBody FriendRequestCreateRequest req) {
|
||||||
ImFriendRequestEntity saved = friendRequestService.send(appId, req.fromUserId(), req.toUserId(), req.remark());
|
ImFriendRequestEntity saved = friendRequestService.send(appKey, req.fromUserId(), req.toUserId(), req.remark());
|
||||||
operationLogService.record(appId, operatorId, "CREATE_FRIEND_REQUEST", "FRIEND_REQUEST", saved.getId(), req.toUserId());
|
operationLogService.record(appKey, operatorId, "CREATE_FRIEND_REQUEST", "FRIEND_REQUEST", saved.getId(), req.toUserId());
|
||||||
return ResponseEntity.ok(ApiResponse.success(saved));
|
return ResponseEntity.ok(ApiResponse.success(saved));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/friend-requests/{requestId}/accept")
|
@PostMapping("/friend-requests/{requestId}/accept")
|
||||||
public ResponseEntity<ApiResponse<ImFriendRequestEntity>> acceptFriendRequest(
|
public ResponseEntity<ApiResponse<ImFriendRequestEntity>> acceptFriendRequest(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String requestId,
|
@PathVariable String requestId,
|
||||||
@AuthenticationPrincipal String operatorId) {
|
@AuthenticationPrincipal String operatorId) {
|
||||||
ImFriendRequestEntity saved = friendRequestService.adminAccept(appId, requestId);
|
ImFriendRequestEntity saved = friendRequestService.adminAccept(appKey, requestId);
|
||||||
operationLogService.record(appId, operatorId, "ACCEPT_FRIEND_REQUEST", "FRIEND_REQUEST", requestId, null);
|
operationLogService.record(appKey, operatorId, "ACCEPT_FRIEND_REQUEST", "FRIEND_REQUEST", requestId, null);
|
||||||
return ResponseEntity.ok(ApiResponse.success(saved));
|
return ResponseEntity.ok(ApiResponse.success(saved));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/friend-requests/{requestId}/reject")
|
@PostMapping("/friend-requests/{requestId}/reject")
|
||||||
public ResponseEntity<ApiResponse<ImFriendRequestEntity>> rejectFriendRequest(
|
public ResponseEntity<ApiResponse<ImFriendRequestEntity>> rejectFriendRequest(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String requestId,
|
@PathVariable String requestId,
|
||||||
@AuthenticationPrincipal String operatorId) {
|
@AuthenticationPrincipal String operatorId) {
|
||||||
ImFriendRequestEntity saved = friendRequestService.adminReject(appId, requestId);
|
ImFriendRequestEntity saved = friendRequestService.adminReject(appKey, requestId);
|
||||||
operationLogService.record(appId, operatorId, "REJECT_FRIEND_REQUEST", "FRIEND_REQUEST", requestId, null);
|
operationLogService.record(appKey, operatorId, "REJECT_FRIEND_REQUEST", "FRIEND_REQUEST", requestId, null);
|
||||||
return ResponseEntity.ok(ApiResponse.success(saved));
|
return ResponseEntity.ok(ApiResponse.success(saved));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/blacklist")
|
@GetMapping("/blacklist")
|
||||||
public ResponseEntity<ApiResponse<List<ImBlacklistEntity>>> listBlacklist(@RequestParam String appId) {
|
public ResponseEntity<ApiResponse<List<ImBlacklistEntity>>> listBlacklist(@RequestParam String appKey) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(blacklistService.listByApp(appId)));
|
return ResponseEntity.ok(ApiResponse.success(blacklistService.listByApp(appKey)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/blacklist")
|
@PostMapping("/blacklist")
|
||||||
public ResponseEntity<ApiResponse<ImBlacklistEntity>> addBlacklist(
|
public ResponseEntity<ApiResponse<ImBlacklistEntity>> addBlacklist(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@RequestBody BlacklistRequest req) {
|
@RequestBody BlacklistRequest req) {
|
||||||
ImBlacklistEntity saved = blacklistService.add(appId, req.userId(), req.blockedUserId());
|
ImBlacklistEntity saved = blacklistService.add(appKey, req.userId(), req.blockedUserId());
|
||||||
operationLogService.record(appId, operatorId, "ADD_BLACKLIST", "BLACKLIST", saved.getId(), req.blockedUserId());
|
operationLogService.record(appKey, operatorId, "ADD_BLACKLIST", "BLACKLIST", saved.getId(), req.blockedUserId());
|
||||||
return ResponseEntity.ok(ApiResponse.success(saved));
|
return ResponseEntity.ok(ApiResponse.success(saved));
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/blacklist")
|
@DeleteMapping("/blacklist")
|
||||||
public ResponseEntity<ApiResponse<Void>> removeBlacklist(
|
public ResponseEntity<ApiResponse<Void>> removeBlacklist(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam String userId,
|
@RequestParam String userId,
|
||||||
@RequestParam String blockedUserId,
|
@RequestParam String blockedUserId,
|
||||||
@AuthenticationPrincipal String operatorId) {
|
@AuthenticationPrincipal String operatorId) {
|
||||||
blacklistService.remove(appId, userId, blockedUserId);
|
blacklistService.remove(appKey, userId, blockedUserId);
|
||||||
operationLogService.record(appId, operatorId, "REMOVE_BLACKLIST", "BLACKLIST", userId, blockedUserId);
|
operationLogService.record(appKey, operatorId, "REMOVE_BLACKLIST", "BLACKLIST", userId, blockedUserId);
|
||||||
return ResponseEntity.ok(ApiResponse.ok());
|
return ResponseEntity.ok(ApiResponse.ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/groups/{groupId}/members")
|
@GetMapping("/groups/{groupId}/members")
|
||||||
public ResponseEntity<ApiResponse<List<ImAccountEntity>>> listGroupMembers(
|
public ResponseEntity<ApiResponse<List<ImAccountEntity>>> listGroupMembers(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String groupId) {
|
@PathVariable String groupId) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(groupService.adminListMembers(appId, groupId)));
|
return ResponseEntity.ok(ApiResponse.success(groupService.adminListMembers(appKey, groupId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/groups/{groupId}/members/search")
|
@GetMapping("/groups/{groupId}/members/search")
|
||||||
public ResponseEntity<ApiResponse<List<ImAccountEntity>>> searchGroupMembers(
|
public ResponseEntity<ApiResponse<List<ImAccountEntity>>> searchGroupMembers(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String groupId,
|
@PathVariable String groupId,
|
||||||
@RequestParam String keyword,
|
@RequestParam String keyword,
|
||||||
@RequestParam(defaultValue = "20") int size) {
|
@RequestParam(defaultValue = "20") int size) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(groupService.adminSearchMembers(appId, groupId, keyword, size)));
|
return ResponseEntity.ok(ApiResponse.success(groupService.adminSearchMembers(appKey, groupId, keyword, size)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/groups/{groupId}/members")
|
@PostMapping("/groups/{groupId}/members")
|
||||||
public ResponseEntity<ApiResponse<ImGroupEntity>> addGroupMember(
|
public ResponseEntity<ApiResponse<ImGroupEntity>> addGroupMember(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String groupId,
|
@PathVariable String groupId,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@RequestBody MemberRequest req) {
|
@RequestBody MemberRequest req) {
|
||||||
ImGroupEntity saved = groupService.adminAddMember(appId, groupId, req.userId());
|
ImGroupEntity saved = groupService.adminAddMember(appKey, groupId, req.userId());
|
||||||
operationLogService.record(appId, operatorId, "ADMIN_ADD_GROUP_MEMBER", "GROUP", groupId, req.userId());
|
operationLogService.record(appKey, operatorId, "ADMIN_ADD_GROUP_MEMBER", "GROUP", groupId, req.userId());
|
||||||
return ResponseEntity.ok(ApiResponse.success(saved));
|
return ResponseEntity.ok(ApiResponse.success(saved));
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/groups/{groupId}/members/{userId}")
|
@DeleteMapping("/groups/{groupId}/members/{userId}")
|
||||||
public ResponseEntity<ApiResponse<ImGroupEntity>> removeGroupMember(
|
public ResponseEntity<ApiResponse<ImGroupEntity>> removeGroupMember(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String groupId,
|
@PathVariable String groupId,
|
||||||
@PathVariable String userId,
|
@PathVariable String userId,
|
||||||
@AuthenticationPrincipal String operatorId) {
|
@AuthenticationPrincipal String operatorId) {
|
||||||
ImGroupEntity saved = groupService.adminRemoveMember(appId, groupId, userId);
|
ImGroupEntity saved = groupService.adminRemoveMember(appKey, groupId, userId);
|
||||||
operationLogService.record(appId, operatorId, "ADMIN_REMOVE_GROUP_MEMBER", "GROUP", groupId, userId);
|
operationLogService.record(appKey, operatorId, "ADMIN_REMOVE_GROUP_MEMBER", "GROUP", groupId, userId);
|
||||||
return ResponseEntity.ok(ApiResponse.success(saved));
|
return ResponseEntity.ok(ApiResponse.success(saved));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/groups/{groupId}/roles")
|
@PostMapping("/groups/{groupId}/roles")
|
||||||
public ResponseEntity<ApiResponse<ImGroupEntity>> setGroupRole(
|
public ResponseEntity<ApiResponse<ImGroupEntity>> setGroupRole(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String groupId,
|
@PathVariable String groupId,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@RequestBody SetRoleRequest req) {
|
@RequestBody SetRoleRequest req) {
|
||||||
ImGroupEntity saved = groupService.adminSetRole(appId, groupId, req.userId(), req.role());
|
ImGroupEntity saved = groupService.adminSetRole(appKey, groupId, req.userId(), req.role());
|
||||||
operationLogService.record(appId, operatorId, "ADMIN_SET_GROUP_ROLE", "GROUP", groupId, req.userId() + ":" + req.role());
|
operationLogService.record(appKey, operatorId, "ADMIN_SET_GROUP_ROLE", "GROUP", groupId, req.userId() + ":" + req.role());
|
||||||
return ResponseEntity.ok(ApiResponse.success(saved));
|
return ResponseEntity.ok(ApiResponse.success(saved));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/groups/{groupId}/owner")
|
@PostMapping("/groups/{groupId}/owner")
|
||||||
public ResponseEntity<ApiResponse<ImGroupEntity>> transferGroupOwner(
|
public ResponseEntity<ApiResponse<ImGroupEntity>> transferGroupOwner(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String groupId,
|
@PathVariable String groupId,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@RequestBody TransferOwnerRequest req) {
|
@RequestBody TransferOwnerRequest req) {
|
||||||
ImGroupEntity saved = groupService.adminTransferOwner(appId, groupId, req.newOwnerId());
|
ImGroupEntity saved = groupService.adminTransferOwner(appKey, groupId, req.newOwnerId());
|
||||||
operationLogService.record(appId, operatorId, "ADMIN_TRANSFER_GROUP_OWNER", "GROUP", groupId, req.newOwnerId());
|
operationLogService.record(appKey, operatorId, "ADMIN_TRANSFER_GROUP_OWNER", "GROUP", groupId, req.newOwnerId());
|
||||||
return ResponseEntity.ok(ApiResponse.success(saved));
|
return ResponseEntity.ok(ApiResponse.success(saved));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/groups/{groupId}/attributes")
|
@PutMapping("/groups/{groupId}/attributes")
|
||||||
public ResponseEntity<ApiResponse<ImGroupEntity>> updateGroupAttributes(
|
public ResponseEntity<ApiResponse<ImGroupEntity>> updateGroupAttributes(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String groupId,
|
@PathVariable String groupId,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@RequestBody Map<String, Object> attributes) {
|
@RequestBody Map<String, Object> attributes) {
|
||||||
ImGroupEntity saved = groupService.adminUpdateExtAttributes(appId, groupId, attributes);
|
ImGroupEntity saved = groupService.adminUpdateExtAttributes(appKey, groupId, attributes);
|
||||||
operationLogService.record(appId, operatorId, "ADMIN_UPDATE_GROUP_ATTRIBUTES", "GROUP", groupId, null);
|
operationLogService.record(appKey, operatorId, "ADMIN_UPDATE_GROUP_ATTRIBUTES", "GROUP", groupId, null);
|
||||||
return ResponseEntity.ok(ApiResponse.success(saved));
|
return ResponseEntity.ok(ApiResponse.success(saved));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/groups/{groupId}/attributes/delete")
|
@PostMapping("/groups/{groupId}/attributes/delete")
|
||||||
public ResponseEntity<ApiResponse<ImGroupEntity>> removeGroupAttributes(
|
public ResponseEntity<ApiResponse<ImGroupEntity>> removeGroupAttributes(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String groupId,
|
@PathVariable String groupId,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@RequestBody AttributeKeysRequest req) {
|
@RequestBody AttributeKeysRequest req) {
|
||||||
ImGroupEntity saved = groupService.adminRemoveExtAttributes(appId, groupId, req.keys());
|
ImGroupEntity saved = groupService.adminRemoveExtAttributes(appKey, groupId, req.keys());
|
||||||
operationLogService.record(appId, operatorId, "ADMIN_REMOVE_GROUP_ATTRIBUTES", "GROUP", groupId,
|
operationLogService.record(appKey, operatorId, "ADMIN_REMOVE_GROUP_ATTRIBUTES", "GROUP", groupId,
|
||||||
String.join(",", req.keys() == null ? List.of() : req.keys()));
|
String.join(",", req.keys() == null ? List.of() : req.keys()));
|
||||||
return ResponseEntity.ok(ApiResponse.success(saved));
|
return ResponseEntity.ok(ApiResponse.success(saved));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/groups/{groupId}/mute")
|
@PostMapping("/groups/{groupId}/mute")
|
||||||
public ResponseEntity<ApiResponse<ImGroupEntity>> muteGroupMember(
|
public ResponseEntity<ApiResponse<ImGroupEntity>> muteGroupMember(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String groupId,
|
@PathVariable String groupId,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@RequestBody MuteMemberRequest req) {
|
@RequestBody MuteMemberRequest req) {
|
||||||
ImGroupEntity saved = groupService.adminMuteMember(appId, groupId, req.userId(), req.minutes());
|
ImGroupEntity saved = groupService.adminMuteMember(appKey, groupId, req.userId(), req.minutes());
|
||||||
operationLogService.record(appId, operatorId, "ADMIN_MUTE_GROUP_MEMBER", "GROUP", groupId, req.userId() + ":" + req.minutes());
|
operationLogService.record(appKey, operatorId, "ADMIN_MUTE_GROUP_MEMBER", "GROUP", groupId, req.userId() + ":" + req.minutes());
|
||||||
return ResponseEntity.ok(ApiResponse.success(saved));
|
return ResponseEntity.ok(ApiResponse.success(saved));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/groups/{groupId}/read-receipts")
|
@PostMapping("/groups/{groupId}/read-receipts")
|
||||||
public ResponseEntity<ApiResponse<List<MessageService.GroupReadReceiptSummary>>> groupReadReceipts(
|
public ResponseEntity<ApiResponse<List<MessageService.GroupReadReceiptSummary>>> groupReadReceipts(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String groupId,
|
@PathVariable String groupId,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@RequestBody GroupReadReceiptRequest req) {
|
@RequestBody GroupReadReceiptRequest req) {
|
||||||
List<MessageService.GroupReadReceiptSummary> receipts =
|
List<MessageService.GroupReadReceiptSummary> receipts =
|
||||||
messageService.groupReadReceipts(appId, groupId, req.messageIds());
|
messageService.groupReadReceipts(appKey, groupId, req.messageIds());
|
||||||
operationLogService.record(appId, operatorId, "QUERY_GROUP_READ_RECEIPTS", "GROUP", groupId,
|
operationLogService.record(appKey, operatorId, "QUERY_GROUP_READ_RECEIPTS", "GROUP", groupId,
|
||||||
"count=" + receipts.size());
|
"count=" + receipts.size());
|
||||||
return ResponseEntity.ok(ApiResponse.success(receipts));
|
return ResponseEntity.ok(ApiResponse.success(receipts));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/groups/{groupId}/join-requests")
|
@GetMapping("/groups/{groupId}/join-requests")
|
||||||
public ResponseEntity<ApiResponse<List<ImGroupJoinRequestEntity>>> listGroupJoinRequests(
|
public ResponseEntity<ApiResponse<List<ImGroupJoinRequestEntity>>> listGroupJoinRequests(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String groupId) {
|
@PathVariable String groupId) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(groupService.adminListJoinRequests(appId, groupId)));
|
return ResponseEntity.ok(ApiResponse.success(groupService.adminListJoinRequests(appKey, groupId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/groups/{groupId}/join-requests/{requestId}/accept")
|
@PostMapping("/groups/{groupId}/join-requests/{requestId}/accept")
|
||||||
public ResponseEntity<ApiResponse<ImGroupJoinRequestEntity>> acceptGroupJoinRequest(
|
public ResponseEntity<ApiResponse<ImGroupJoinRequestEntity>> acceptGroupJoinRequest(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String groupId,
|
@PathVariable String groupId,
|
||||||
@PathVariable String requestId,
|
@PathVariable String requestId,
|
||||||
@AuthenticationPrincipal String operatorId) {
|
@AuthenticationPrincipal String operatorId) {
|
||||||
ImGroupJoinRequestEntity saved = groupService.adminAcceptJoinRequest(appId, groupId, requestId);
|
ImGroupJoinRequestEntity saved = groupService.adminAcceptJoinRequest(appKey, groupId, requestId);
|
||||||
operationLogService.record(appId, operatorId, "ADMIN_ACCEPT_GROUP_JOIN_REQUEST", "GROUP_JOIN_REQUEST", requestId, null);
|
operationLogService.record(appKey, operatorId, "ADMIN_ACCEPT_GROUP_JOIN_REQUEST", "GROUP_JOIN_REQUEST", requestId, null);
|
||||||
return ResponseEntity.ok(ApiResponse.success(saved));
|
return ResponseEntity.ok(ApiResponse.success(saved));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/groups/{groupId}/join-requests/{requestId}/reject")
|
@PostMapping("/groups/{groupId}/join-requests/{requestId}/reject")
|
||||||
public ResponseEntity<ApiResponse<ImGroupJoinRequestEntity>> rejectGroupJoinRequest(
|
public ResponseEntity<ApiResponse<ImGroupJoinRequestEntity>> rejectGroupJoinRequest(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String groupId,
|
@PathVariable String groupId,
|
||||||
@PathVariable String requestId,
|
@PathVariable String requestId,
|
||||||
@AuthenticationPrincipal String operatorId) {
|
@AuthenticationPrincipal String operatorId) {
|
||||||
ImGroupJoinRequestEntity saved = groupService.adminRejectJoinRequest(appId, groupId, requestId);
|
ImGroupJoinRequestEntity saved = groupService.adminRejectJoinRequest(appKey, groupId, requestId);
|
||||||
operationLogService.record(appId, operatorId, "ADMIN_REJECT_GROUP_JOIN_REQUEST", "GROUP_JOIN_REQUEST", requestId, null);
|
operationLogService.record(appKey, operatorId, "ADMIN_REJECT_GROUP_JOIN_REQUEST", "GROUP_JOIN_REQUEST", requestId, null);
|
||||||
return ResponseEntity.ok(ApiResponse.success(saved));
|
return ResponseEntity.ok(ApiResponse.success(saved));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/webhooks")
|
@GetMapping("/webhooks")
|
||||||
public ResponseEntity<ApiResponse<List<WebhookConfigEntity>>> listWebhooks(@RequestParam String appId) {
|
public ResponseEntity<ApiResponse<List<WebhookConfigEntity>>> listWebhooks(@RequestParam String appKey) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(webhookConfigService.list(appId)));
|
return ResponseEntity.ok(ApiResponse.success(webhookConfigService.list(appKey)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/webhooks")
|
@PostMapping("/webhooks")
|
||||||
public ResponseEntity<ApiResponse<WebhookConfigEntity>> createWebhook(
|
public ResponseEntity<ApiResponse<WebhookConfigEntity>> createWebhook(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@RequestBody WebhookConfigRequest req) {
|
@RequestBody WebhookConfigRequest req) {
|
||||||
WebhookConfigEntity saved = webhookConfigService.create(appId, req.url(), req.secret(), req.enabled());
|
WebhookConfigEntity saved = webhookConfigService.create(appKey, req.url(), req.secret(), req.enabled());
|
||||||
operationLogService.record(appId, operatorId, "CREATE_WEBHOOK", "WEBHOOK", saved.getId(), req.url());
|
operationLogService.record(appKey, operatorId, "CREATE_WEBHOOK", "WEBHOOK", saved.getId(), req.url());
|
||||||
return ResponseEntity.ok(ApiResponse.success(saved));
|
return ResponseEntity.ok(ApiResponse.success(saved));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/webhooks/{id}")
|
@PutMapping("/webhooks/{id}")
|
||||||
public ResponseEntity<ApiResponse<WebhookConfigEntity>> updateWebhook(
|
public ResponseEntity<ApiResponse<WebhookConfigEntity>> updateWebhook(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String id,
|
@PathVariable String id,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@RequestBody WebhookConfigRequest req) {
|
@RequestBody WebhookConfigRequest req) {
|
||||||
WebhookConfigEntity saved = webhookConfigService.update(appId, id, req.url(), req.secret(), req.enabled());
|
WebhookConfigEntity saved = webhookConfigService.update(appKey, id, req.url(), req.secret(), req.enabled());
|
||||||
operationLogService.record(appId, operatorId, "UPDATE_WEBHOOK", "WEBHOOK", id, req.url());
|
operationLogService.record(appKey, operatorId, "UPDATE_WEBHOOK", "WEBHOOK", id, req.url());
|
||||||
return ResponseEntity.ok(ApiResponse.success(saved));
|
return ResponseEntity.ok(ApiResponse.success(saved));
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/webhooks/{id}")
|
@DeleteMapping("/webhooks/{id}")
|
||||||
public ResponseEntity<ApiResponse<Void>> deleteWebhook(
|
public ResponseEntity<ApiResponse<Void>> deleteWebhook(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@PathVariable String id) {
|
@PathVariable String id) {
|
||||||
webhookConfigService.delete(appId, id);
|
webhookConfigService.delete(appKey, id);
|
||||||
operationLogService.record(appId, operatorId, "DELETE_WEBHOOK", "WEBHOOK", id, null);
|
operationLogService.record(appKey, operatorId, "DELETE_WEBHOOK", "WEBHOOK", id, null);
|
||||||
return ResponseEntity.ok(ApiResponse.ok());
|
return ResponseEntity.ok(ApiResponse.ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/webhook-deliveries")
|
@GetMapping("/webhook-deliveries")
|
||||||
public ResponseEntity<ApiResponse<Page<WebhookDeliveryEntity>>> listWebhookDeliveries(
|
public ResponseEntity<ApiResponse<Page<WebhookDeliveryEntity>>> listWebhookDeliveries(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam(required = false) String callbackEvent,
|
@RequestParam(required = false) String callbackEvent,
|
||||||
@RequestParam(required = false) Boolean success,
|
@RequestParam(required = false) Boolean success,
|
||||||
@RequestParam(defaultValue = "0") int page,
|
@RequestParam(defaultValue = "0") int page,
|
||||||
@ -551,122 +551,122 @@ public class ImAdminController {
|
|||||||
Page<WebhookDeliveryEntity> result;
|
Page<WebhookDeliveryEntity> result;
|
||||||
PageRequest pageable = PageRequest.of(page, size);
|
PageRequest pageable = PageRequest.of(page, size);
|
||||||
if (callbackEvent != null && !callbackEvent.isBlank()) {
|
if (callbackEvent != null && !callbackEvent.isBlank()) {
|
||||||
result = webhookDeliveryRepository.findByAppIdAndCallbackEvent(appId, callbackEvent, pageable);
|
result = webhookDeliveryRepository.findByAppIdAndCallbackEvent(appKey, callbackEvent, pageable);
|
||||||
} else if (success != null) {
|
} else if (success != null) {
|
||||||
result = webhookDeliveryRepository.findByAppIdAndSuccess(appId, success, pageable);
|
result = webhookDeliveryRepository.findByAppIdAndSuccess(appKey, success, pageable);
|
||||||
} else {
|
} else {
|
||||||
result = webhookDeliveryRepository.findByAppId(appId, pageable);
|
result = webhookDeliveryRepository.findByAppId(appKey, pageable);
|
||||||
}
|
}
|
||||||
return ResponseEntity.ok(ApiResponse.success(result));
|
return ResponseEntity.ok(ApiResponse.success(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/webhook-alerts")
|
@GetMapping("/webhook-alerts")
|
||||||
public ResponseEntity<ApiResponse<Page<WebhookAlertEntity>>> listWebhookAlerts(
|
public ResponseEntity<ApiResponse<Page<WebhookAlertEntity>>> listWebhookAlerts(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam(required = false) Boolean acknowledged,
|
@RequestParam(required = false) Boolean acknowledged,
|
||||||
@RequestParam(defaultValue = "0") int page,
|
@RequestParam(defaultValue = "0") int page,
|
||||||
@RequestParam(defaultValue = "20") int size) {
|
@RequestParam(defaultValue = "20") int size) {
|
||||||
Page<WebhookAlertEntity> result;
|
Page<WebhookAlertEntity> result;
|
||||||
PageRequest pageable = PageRequest.of(page, size);
|
PageRequest pageable = PageRequest.of(page, size);
|
||||||
if (acknowledged != null) {
|
if (acknowledged != null) {
|
||||||
result = webhookAlertRepository.findByAppIdAndAcknowledged(appId, acknowledged, pageable);
|
result = webhookAlertRepository.findByAppIdAndAcknowledged(appKey, acknowledged, pageable);
|
||||||
} else {
|
} else {
|
||||||
result = webhookAlertRepository.findByAppId(appId, pageable);
|
result = webhookAlertRepository.findByAppId(appKey, pageable);
|
||||||
}
|
}
|
||||||
return ResponseEntity.ok(ApiResponse.success(result));
|
return ResponseEntity.ok(ApiResponse.success(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/webhook-alerts/{id}/acknowledge")
|
@PostMapping("/webhook-alerts/{id}/acknowledge")
|
||||||
public ResponseEntity<ApiResponse<WebhookAlertEntity>> acknowledgeWebhookAlert(
|
public ResponseEntity<ApiResponse<WebhookAlertEntity>> acknowledgeWebhookAlert(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String id,
|
@PathVariable String id,
|
||||||
@AuthenticationPrincipal String operatorId) {
|
@AuthenticationPrincipal String operatorId) {
|
||||||
WebhookAlertEntity alert = webhookAlertRepository.findById(id)
|
WebhookAlertEntity alert = webhookAlertRepository.findById(id)
|
||||||
.orElseThrow(() -> new BusinessException(404, "告警不存在"));
|
.orElseThrow(() -> new BusinessException(404, "告警不存在"));
|
||||||
if (!alert.getAppId().equals(appId)) {
|
if (!alert.getAppId().equals(appKey)) {
|
||||||
throw new BusinessException(403, "无权操作");
|
throw new BusinessException(403, "无权操作");
|
||||||
}
|
}
|
||||||
alert.setAcknowledged(true);
|
alert.setAcknowledged(true);
|
||||||
alert.setAcknowledgedAt(LocalDateTime.now());
|
alert.setAcknowledgedAt(LocalDateTime.now());
|
||||||
WebhookAlertEntity saved = webhookAlertRepository.save(alert);
|
WebhookAlertEntity saved = webhookAlertRepository.save(alert);
|
||||||
operationLogService.record(appId, operatorId, "ACK_WEBHOOK_ALERT", "WEBHOOK_ALERT", id, null);
|
operationLogService.record(appKey, operatorId, "ACK_WEBHOOK_ALERT", "WEBHOOK_ALERT", id, null);
|
||||||
return ResponseEntity.ok(ApiResponse.success(saved));
|
return ResponseEntity.ok(ApiResponse.success(saved));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/webhooks/{id}/health")
|
@GetMapping("/webhooks/{id}/health")
|
||||||
public ResponseEntity<ApiResponse<Map<String, Object>>> getWebhookHealth(
|
public ResponseEntity<ApiResponse<Map<String, Object>>> getWebhookHealth(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String id) {
|
@PathVariable String id) {
|
||||||
WebhookConfigEntity webhook = webhookConfigService.get(appId, id);
|
WebhookConfigEntity webhook = webhookConfigService.get(appKey, id);
|
||||||
Map<String, Object> health = new LinkedHashMap<>();
|
Map<String, Object> health = new LinkedHashMap<>();
|
||||||
health.put("webhookId", webhook.getId());
|
health.put("webhookId", webhook.getId());
|
||||||
health.put("url", webhook.getUrl());
|
health.put("url", webhook.getUrl());
|
||||||
health.put("enabled", webhook.isEnabled());
|
health.put("enabled", webhook.isEnabled());
|
||||||
health.put("consecutiveFailures", webhook.getConsecutiveFailures());
|
health.put("consecutiveFailures", webhook.getConsecutiveFailures());
|
||||||
health.put("lastFailureAt", webhook.getLastFailureAt());
|
health.put("lastFailureAt", webhook.getLastFailureAt());
|
||||||
long unacknowledgedAlerts = webhookAlertRepository.countUnacknowledgedByAppId(appId);
|
long unacknowledgedAlerts = webhookAlertRepository.countUnacknowledgedByAppId(appKey);
|
||||||
health.put("unacknowledgedAlerts", unacknowledgedAlerts);
|
health.put("unacknowledgedAlerts", unacknowledgedAlerts);
|
||||||
return ResponseEntity.ok(ApiResponse.success(health));
|
return ResponseEntity.ok(ApiResponse.success(health));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/keyword-filters")
|
@GetMapping("/keyword-filters")
|
||||||
public ResponseEntity<ApiResponse<List<KeywordFilterEntity>>> listKeywordFilters(@RequestParam String appId) {
|
public ResponseEntity<ApiResponse<List<KeywordFilterEntity>>> listKeywordFilters(@RequestParam String appKey) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(keywordFilterService.list(appId)));
|
return ResponseEntity.ok(ApiResponse.success(keywordFilterService.list(appKey)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/keyword-filters")
|
@PostMapping("/keyword-filters")
|
||||||
public ResponseEntity<ApiResponse<KeywordFilterEntity>> createKeywordFilter(
|
public ResponseEntity<ApiResponse<KeywordFilterEntity>> createKeywordFilter(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@RequestBody KeywordFilterRequest req) {
|
@RequestBody KeywordFilterRequest req) {
|
||||||
KeywordFilterEntity saved = keywordFilterService.add(appId, req.pattern(), req.replacement(), req.action());
|
KeywordFilterEntity saved = keywordFilterService.add(appKey, req.pattern(), req.replacement(), req.action());
|
||||||
operationLogService.record(appId, operatorId, "CREATE_KEYWORD_FILTER", "KEYWORD_FILTER", saved.getId(), req.pattern());
|
operationLogService.record(appKey, operatorId, "CREATE_KEYWORD_FILTER", "KEYWORD_FILTER", saved.getId(), req.pattern());
|
||||||
return ResponseEntity.ok(ApiResponse.success(saved));
|
return ResponseEntity.ok(ApiResponse.success(saved));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/keyword-filters/{id}")
|
@PutMapping("/keyword-filters/{id}")
|
||||||
public ResponseEntity<ApiResponse<KeywordFilterEntity>> updateKeywordFilter(
|
public ResponseEntity<ApiResponse<KeywordFilterEntity>> updateKeywordFilter(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String id,
|
@PathVariable String id,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@RequestBody KeywordFilterRequest req) {
|
@RequestBody KeywordFilterRequest req) {
|
||||||
KeywordFilterEntity saved = keywordFilterService.update(appId, id, req.pattern(), req.replacement(), req.action(), req.enabled());
|
KeywordFilterEntity saved = keywordFilterService.update(appKey, id, req.pattern(), req.replacement(), req.action(), req.enabled());
|
||||||
operationLogService.record(appId, operatorId, "UPDATE_KEYWORD_FILTER", "KEYWORD_FILTER", id, req.pattern());
|
operationLogService.record(appKey, operatorId, "UPDATE_KEYWORD_FILTER", "KEYWORD_FILTER", id, req.pattern());
|
||||||
return ResponseEntity.ok(ApiResponse.success(saved));
|
return ResponseEntity.ok(ApiResponse.success(saved));
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/keyword-filters/{id}")
|
@DeleteMapping("/keyword-filters/{id}")
|
||||||
public ResponseEntity<ApiResponse<Void>> deleteKeywordFilter(
|
public ResponseEntity<ApiResponse<Void>> deleteKeywordFilter(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@PathVariable String id) {
|
@PathVariable String id) {
|
||||||
keywordFilterService.delete(appId, id);
|
keywordFilterService.delete(appKey, id);
|
||||||
operationLogService.record(appId, operatorId, "DELETE_KEYWORD_FILTER", "KEYWORD_FILTER", id, null);
|
operationLogService.record(appKey, operatorId, "DELETE_KEYWORD_FILTER", "KEYWORD_FILTER", id, null);
|
||||||
return ResponseEntity.ok(ApiResponse.ok());
|
return ResponseEntity.ok(ApiResponse.ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/global-mute")
|
@GetMapping("/global-mute")
|
||||||
public ResponseEntity<ApiResponse<ImGlobalMuteEntity>> getGlobalMute(@RequestParam String appId) {
|
public ResponseEntity<ApiResponse<ImGlobalMuteEntity>> getGlobalMute(@RequestParam String appKey) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(globalMuteService.get(appId)));
|
return ResponseEntity.ok(ApiResponse.success(globalMuteService.get(appKey)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/global-mute")
|
@PutMapping("/global-mute")
|
||||||
public ResponseEntity<ApiResponse<ImGlobalMuteEntity>> setGlobalMute(
|
public ResponseEntity<ApiResponse<ImGlobalMuteEntity>> setGlobalMute(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@RequestParam boolean enabled) {
|
@RequestParam boolean enabled) {
|
||||||
ImGlobalMuteEntity saved = globalMuteService.setEnabled(appId, enabled);
|
ImGlobalMuteEntity saved = globalMuteService.setEnabled(appKey, enabled);
|
||||||
operationLogService.record(appId, operatorId, "SET_GLOBAL_MUTE", "GLOBAL_MUTE", saved.getId(), String.valueOf(enabled));
|
operationLogService.record(appKey, operatorId, "SET_GLOBAL_MUTE", "GLOBAL_MUTE", saved.getId(), String.valueOf(enabled));
|
||||||
return ResponseEntity.ok(ApiResponse.success(saved));
|
return ResponseEntity.ok(ApiResponse.success(saved));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/operation-logs")
|
@GetMapping("/operation-logs")
|
||||||
public ResponseEntity<ApiResponse<Page<com.xuqm.im.entity.ImOperationLogEntity>>> operationLogs(
|
public ResponseEntity<ApiResponse<Page<com.xuqm.im.entity.ImOperationLogEntity>>> operationLogs(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam(defaultValue = "0") int page,
|
@RequestParam(defaultValue = "0") int page,
|
||||||
@RequestParam(defaultValue = "20") int size) {
|
@RequestParam(defaultValue = "20") int size) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(
|
return ResponseEntity.ok(ApiResponse.success(
|
||||||
operationLogService.list(appId, PageRequest.of(page, size))));
|
operationLogService.list(appKey, PageRequest.of(page, size))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/users/state")
|
@GetMapping("/users/state")
|
||||||
@ -689,15 +689,15 @@ public class ImAdminController {
|
|||||||
@PostMapping("/users/kick")
|
@PostMapping("/users/kick")
|
||||||
@PreAuthorize("hasAnyAuthority('ROLE_OPS', 'ROLE_TENANT')")
|
@PreAuthorize("hasAnyAuthority('ROLE_OPS', 'ROLE_TENANT')")
|
||||||
public ResponseEntity<ApiResponse<Void>> kickUsers(
|
public ResponseEntity<ApiResponse<Void>> kickUsers(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@RequestBody KickRequest req) {
|
@RequestBody KickRequest req) {
|
||||||
for (String userId : req.userIds()) {
|
for (String userId : req.userIds()) {
|
||||||
messagingTemplate.convertAndSendToUser(userId, "/queue/kick",
|
messagingTemplate.convertAndSendToUser(userId, "/queue/kick",
|
||||||
Map.of("type", "KICK", "reason", "管理员强制下线"));
|
Map.of("type", "KICK", "reason", "管理员强制下线"));
|
||||||
redisTemplate.opsForValue().set("im:kick:" + appId + ":" + userId, "1", Duration.ofMinutes(5));
|
redisTemplate.opsForValue().set("im:kick:" + appKey + ":" + userId, "1", Duration.ofMinutes(5));
|
||||||
}
|
}
|
||||||
operationLogService.record(appId, operatorId, "KICK_USERS", "ACCOUNT", null,
|
operationLogService.record(appKey, operatorId, "KICK_USERS", "ACCOUNT", null,
|
||||||
String.join(",", req.userIds()));
|
String.join(",", req.userIds()));
|
||||||
return ResponseEntity.ok(ApiResponse.ok());
|
return ResponseEntity.ok(ApiResponse.ok());
|
||||||
}
|
}
|
||||||
@ -705,15 +705,15 @@ public class ImAdminController {
|
|||||||
@PostMapping("/messages/batch-send")
|
@PostMapping("/messages/batch-send")
|
||||||
@PreAuthorize("hasAnyAuthority('ROLE_OPS', 'ROLE_TENANT')")
|
@PreAuthorize("hasAnyAuthority('ROLE_OPS', 'ROLE_TENANT')")
|
||||||
public ResponseEntity<ApiResponse<List<ImMessageEntity>>> batchSendMsg(
|
public ResponseEntity<ApiResponse<List<ImMessageEntity>>> batchSendMsg(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@RequestBody BatchSendRequest req) {
|
@RequestBody BatchSendRequest req) {
|
||||||
List<ImMessageEntity> result = new ArrayList<>();
|
List<ImMessageEntity> result = new ArrayList<>();
|
||||||
for (String toId : req.toIds()) {
|
for (String toId : req.toIds()) {
|
||||||
ImMessageEntity sent = messageService.adminSend(appId, operatorId, toId, req.msgType(), req.content());
|
ImMessageEntity sent = messageService.adminSend(appKey, operatorId, toId, req.msgType(), req.content());
|
||||||
result.add(sent);
|
result.add(sent);
|
||||||
}
|
}
|
||||||
operationLogService.record(appId, operatorId, "BATCH_SEND_MESSAGE", "MESSAGE", null,
|
operationLogService.record(appKey, operatorId, "BATCH_SEND_MESSAGE", "MESSAGE", null,
|
||||||
"count=" + result.size());
|
"count=" + result.size());
|
||||||
return ResponseEntity.ok(ApiResponse.success(result));
|
return ResponseEntity.ok(ApiResponse.success(result));
|
||||||
}
|
}
|
||||||
@ -721,22 +721,22 @@ public class ImAdminController {
|
|||||||
@PostMapping("/messages/read")
|
@PostMapping("/messages/read")
|
||||||
@PreAuthorize("hasAnyAuthority('ROLE_OPS', 'ROLE_TENANT')")
|
@PreAuthorize("hasAnyAuthority('ROLE_OPS', 'ROLE_TENANT')")
|
||||||
public ResponseEntity<ApiResponse<Void>> adminSetMsgRead(
|
public ResponseEntity<ApiResponse<Void>> adminSetMsgRead(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@RequestBody SetMsgReadRequest req) {
|
@RequestBody SetMsgReadRequest req) {
|
||||||
messageService.adminSetMsgRead(appId, req.userId());
|
messageService.adminSetMsgRead(appKey, req.userId());
|
||||||
operationLogService.record(appId, operatorId, "ADMIN_SET_MSG_READ", "MESSAGE", req.userId(), null);
|
operationLogService.record(appKey, operatorId, "ADMIN_SET_MSG_READ", "MESSAGE", req.userId(), null);
|
||||||
return ResponseEntity.ok(ApiResponse.ok());
|
return ResponseEntity.ok(ApiResponse.ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/messages/import")
|
@PostMapping("/messages/import")
|
||||||
@PreAuthorize("hasAnyAuthority('ROLE_OPS', 'ROLE_TENANT')")
|
@PreAuthorize("hasAnyAuthority('ROLE_OPS', 'ROLE_TENANT')")
|
||||||
public ResponseEntity<ApiResponse<List<ImMessageEntity>>> importMessages(
|
public ResponseEntity<ApiResponse<List<ImMessageEntity>>> importMessages(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@AuthenticationPrincipal String operatorId,
|
@AuthenticationPrincipal String operatorId,
|
||||||
@RequestBody List<MessageService.ImportMessageRequest> req) {
|
@RequestBody List<MessageService.ImportMessageRequest> req) {
|
||||||
List<ImMessageEntity> result = messageService.importMessages(appId, req);
|
List<ImMessageEntity> result = messageService.importMessages(appKey, req);
|
||||||
operationLogService.record(appId, operatorId, "IMPORT_MESSAGES", "MESSAGE", null,
|
operationLogService.record(appKey, operatorId, "IMPORT_MESSAGES", "MESSAGE", null,
|
||||||
"count=" + result.size());
|
"count=" + result.size());
|
||||||
return ResponseEntity.ok(ApiResponse.success(result));
|
return ResponseEntity.ok(ApiResponse.success(result));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,8 +40,8 @@ public class InternalPresenceController {
|
|||||||
try {
|
try {
|
||||||
Claims claims = jwtUtil.parse(request.token());
|
Claims claims = jwtUtil.parse(request.token());
|
||||||
String userId = claims.getSubject();
|
String userId = claims.getSubject();
|
||||||
String appId = claims.get("appId", String.class);
|
String appKey = claims.get("appKey", String.class);
|
||||||
return ResponseEntity.ok(ApiResponse.success(status(appId, userId)));
|
return ResponseEntity.ok(ApiResponse.success(status(appKey, userId)));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return ResponseEntity.badRequest().body(ApiResponse.error(400, "Invalid IM token"));
|
return ResponseEntity.badRequest().body(ApiResponse.error(400, "Invalid IM token"));
|
||||||
}
|
}
|
||||||
@ -50,17 +50,17 @@ public class InternalPresenceController {
|
|||||||
@GetMapping("/users/{userId}")
|
@GetMapping("/users/{userId}")
|
||||||
public ResponseEntity<ApiResponse<PresenceStatus>> userStatus(
|
public ResponseEntity<ApiResponse<PresenceStatus>> userStatus(
|
||||||
@RequestHeader(value = "X-Internal-Token", required = false) String token,
|
@RequestHeader(value = "X-Internal-Token", required = false) String token,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable String userId) {
|
@PathVariable String userId) {
|
||||||
if (!isAllowed(token)) {
|
if (!isAllowed(token)) {
|
||||||
return ResponseEntity.status(403).body(ApiResponse.error(403, "Forbidden"));
|
return ResponseEntity.status(403).body(ApiResponse.error(403, "Forbidden"));
|
||||||
}
|
}
|
||||||
return ResponseEntity.ok(ApiResponse.success(status(appId, userId)));
|
return ResponseEntity.ok(ApiResponse.success(status(appKey, userId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private PresenceStatus status(String appId, String userId) {
|
private PresenceStatus status(String appKey, String userId) {
|
||||||
boolean online = presenceService.isOnline(userId);
|
boolean online = presenceService.isOnline(userId);
|
||||||
return new PresenceStatus(appId, userId, online, presenceService.lastSeenAt(userId));
|
return new PresenceStatus(appKey, userId, online, presenceService.lastSeenAt(userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isAllowed(String token) {
|
private boolean isAllowed(String token) {
|
||||||
@ -69,5 +69,5 @@ public class InternalPresenceController {
|
|||||||
|
|
||||||
public record TokenRequest(String token) {}
|
public record TokenRequest(String token) {}
|
||||||
|
|
||||||
public record PresenceStatus(String appId, String userId, boolean online, long lastSeenAt) {}
|
public record PresenceStatus(String appKey, String userId, boolean online, long lastSeenAt) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,16 +45,16 @@ public class MessageController {
|
|||||||
public ResponseEntity<ApiResponse<ImMessageEntity>> send(
|
public ResponseEntity<ApiResponse<ImMessageEntity>> send(
|
||||||
@Valid @RequestBody SendMessageRequest req,
|
@Valid @RequestBody SendMessageRequest req,
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId) {
|
@RequestParam String appKey) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(messageService.send(appId, userId, req)));
|
return ResponseEntity.ok(ApiResponse.success(messageService.send(appKey, userId, req)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{id}/revoke")
|
@PostMapping("/{id}/revoke")
|
||||||
public ResponseEntity<ApiResponse<ImMessageEntity>> revoke(
|
public ResponseEntity<ApiResponse<ImMessageEntity>> revoke(
|
||||||
@PathVariable String id,
|
@PathVariable String id,
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId) {
|
@RequestParam String appKey) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(messageService.revoke(appId, id, userId)));
|
return ResponseEntity.ok(ApiResponse.success(messageService.revoke(appKey, id, userId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/{id}")
|
@PutMapping("/{id}")
|
||||||
@ -62,15 +62,15 @@ public class MessageController {
|
|||||||
@PathVariable String id,
|
@PathVariable String id,
|
||||||
@Valid @RequestBody EditMessageRequest req,
|
@Valid @RequestBody EditMessageRequest req,
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId) {
|
@RequestParam String appKey) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(messageService.edit(appId, id, userId, req)));
|
return ResponseEntity.ok(ApiResponse.success(messageService.edit(appKey, id, userId, req)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/history/{toId}")
|
@GetMapping("/history/{toId}")
|
||||||
public ResponseEntity<ApiResponse<?>> history(
|
public ResponseEntity<ApiResponse<?>> history(
|
||||||
@PathVariable String toId,
|
@PathVariable String toId,
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam(required = false) ImMessageEntity.MsgType msgType,
|
@RequestParam(required = false) ImMessageEntity.MsgType msgType,
|
||||||
@RequestParam(required = false) String keyword,
|
@RequestParam(required = false) String keyword,
|
||||||
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime startTime,
|
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime startTime,
|
||||||
@ -78,14 +78,14 @@ public class MessageController {
|
|||||||
@RequestParam(defaultValue = "0") int page,
|
@RequestParam(defaultValue = "0") int page,
|
||||||
@RequestParam(defaultValue = "20") int size) {
|
@RequestParam(defaultValue = "20") int size) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(
|
return ResponseEntity.ok(ApiResponse.success(
|
||||||
messageService.history(appId, userId, toId, msgType, keyword, startTime, endTime, page, size)));
|
messageService.history(appKey, userId, toId, msgType, keyword, startTime, endTime, page, size)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/group-history/{groupId}")
|
@GetMapping("/group-history/{groupId}")
|
||||||
public ResponseEntity<ApiResponse<?>> groupHistory(
|
public ResponseEntity<ApiResponse<?>> groupHistory(
|
||||||
@PathVariable String groupId,
|
@PathVariable String groupId,
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam(required = false) ImMessageEntity.MsgType msgType,
|
@RequestParam(required = false) ImMessageEntity.MsgType msgType,
|
||||||
@RequestParam(required = false) String keyword,
|
@RequestParam(required = false) String keyword,
|
||||||
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime startTime,
|
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime startTime,
|
||||||
@ -93,13 +93,13 @@ public class MessageController {
|
|||||||
@RequestParam(defaultValue = "0") int page,
|
@RequestParam(defaultValue = "0") int page,
|
||||||
@RequestParam(defaultValue = "20") int size) {
|
@RequestParam(defaultValue = "20") int size) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(
|
return ResponseEntity.ok(ApiResponse.success(
|
||||||
messageService.groupHistory(appId, groupId, userId, msgType, keyword, startTime, endTime, page, size)));
|
messageService.groupHistory(appKey, groupId, userId, msgType, keyword, startTime, endTime, page, size)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/search")
|
@GetMapping("/search")
|
||||||
public ResponseEntity<ApiResponse<Page<ImMessageEntity>>> search(
|
public ResponseEntity<ApiResponse<Page<ImMessageEntity>>> search(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam(required = false) ImMessageEntity.ChatType chatType,
|
@RequestParam(required = false) ImMessageEntity.ChatType chatType,
|
||||||
@RequestParam(required = false) ImMessageEntity.MsgType msgType,
|
@RequestParam(required = false) ImMessageEntity.MsgType msgType,
|
||||||
@RequestParam(required = false) String keyword,
|
@RequestParam(required = false) String keyword,
|
||||||
@ -109,21 +109,21 @@ public class MessageController {
|
|||||||
@RequestParam(defaultValue = "20") int size) {
|
@RequestParam(defaultValue = "20") int size) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(
|
return ResponseEntity.ok(ApiResponse.success(
|
||||||
messageRepository.searchByKeywordForUser(
|
messageRepository.searchByKeywordForUser(
|
||||||
appId, userId, chatType, msgType, keyword, startTime, endTime, PageRequest.of(page, size))));
|
appKey, userId, chatType, msgType, keyword, startTime, endTime, PageRequest.of(page, size))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/offline/count")
|
@GetMapping("/offline/count")
|
||||||
public ResponseEntity<ApiResponse<Map<String, Object>>> offlineMessageCount(
|
public ResponseEntity<ApiResponse<Map<String, Object>>> offlineMessageCount(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId) {
|
@RequestParam String appKey) {
|
||||||
long count = offlineMessageSyncService.countUndelivered(appId, userId);
|
long count = offlineMessageSyncService.countUndelivered(appKey, userId);
|
||||||
return ResponseEntity.ok(ApiResponse.success(Map.of("count", count)));
|
return ResponseEntity.ok(ApiResponse.success(Map.of("count", count)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/offline")
|
@PostMapping("/offline")
|
||||||
public ResponseEntity<ApiResponse<List<ImMessageEntity>>> syncOfflineMessages(
|
public ResponseEntity<ApiResponse<List<ImMessageEntity>>> syncOfflineMessages(
|
||||||
@AuthenticationPrincipal String userId,
|
@AuthenticationPrincipal String userId,
|
||||||
@RequestParam String appId) {
|
@RequestParam String appKey) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(offlineMessageSyncService.syncAndReturn(appId, userId)));
|
return ResponseEntity.ok(ApiResponse.success(offlineMessageSyncService.syncAndReturn(appKey, userId)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,15 +23,15 @@ public class StatisticsController {
|
|||||||
|
|
||||||
@GetMapping("/messages")
|
@GetMapping("/messages")
|
||||||
public ApiResponse<Map<String, Object>> messageStats(
|
public ApiResponse<Map<String, Object>> messageStats(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam(required = false) Integer days) {
|
@RequestParam(required = false) Integer days) {
|
||||||
int d = days == null ? 7 : days;
|
int d = days == null ? 7 : days;
|
||||||
LocalDateTime since = LocalDateTime.now().minusDays(d);
|
LocalDateTime since = LocalDateTime.now().minusDays(d);
|
||||||
long total = messageRepository.countByAppIdAndCreatedAtAfter(appId, since);
|
long total = messageRepository.countByAppIdAndCreatedAtAfter(appKey, since);
|
||||||
long single = messageRepository.countByAppIdAndChatTypeAndCreatedAtAfter(
|
long single = messageRepository.countByAppIdAndChatTypeAndCreatedAtAfter(
|
||||||
appId, com.xuqm.im.entity.ImMessageEntity.ChatType.SINGLE, since);
|
appKey, com.xuqm.im.entity.ImMessageEntity.ChatType.SINGLE, since);
|
||||||
long group = messageRepository.countByAppIdAndChatTypeAndCreatedAtAfter(
|
long group = messageRepository.countByAppIdAndChatTypeAndCreatedAtAfter(
|
||||||
appId, com.xuqm.im.entity.ImMessageEntity.ChatType.GROUP, since);
|
appKey, com.xuqm.im.entity.ImMessageEntity.ChatType.GROUP, since);
|
||||||
return ApiResponse.success(Map.of(
|
return ApiResponse.success(Map.of(
|
||||||
"totalMessages", total,
|
"totalMessages", total,
|
||||||
"singleMessages", single,
|
"singleMessages", single,
|
||||||
@ -42,13 +42,13 @@ public class StatisticsController {
|
|||||||
|
|
||||||
@GetMapping("/webhooks")
|
@GetMapping("/webhooks")
|
||||||
public ApiResponse<Map<String, Object>> webhookStats(
|
public ApiResponse<Map<String, Object>> webhookStats(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam(required = false) Integer days) {
|
@RequestParam(required = false) Integer days) {
|
||||||
int d = days == null ? 7 : days;
|
int d = days == null ? 7 : days;
|
||||||
LocalDateTime since = LocalDateTime.now().minusDays(d);
|
LocalDateTime since = LocalDateTime.now().minusDays(d);
|
||||||
long success = webhookDeliveryRepository.countSuccessfulByAppIdSince(appId, since);
|
long success = webhookDeliveryRepository.countSuccessfulByAppIdSince(appKey, since);
|
||||||
long failed = webhookDeliveryRepository.countFailedByAppIdSince(appId, since);
|
long failed = webhookDeliveryRepository.countFailedByAppIdSince(appKey, since);
|
||||||
List<Object[]> raw = webhookDeliveryRepository.statsByAppIdSince(appId, since);
|
List<Object[]> raw = webhookDeliveryRepository.statsByAppIdSince(appKey, since);
|
||||||
List<Map<String, Object>> eventStats = new ArrayList<>();
|
List<Map<String, Object>> eventStats = new ArrayList<>();
|
||||||
for (Object[] row : raw) {
|
for (Object[] row : raw) {
|
||||||
eventStats.add(Map.of(
|
eventStats.add(Map.of(
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
package com.xuqm.im.model;
|
package com.xuqm.im.model;
|
||||||
|
|
||||||
public record BlacklistCallbackPayload(
|
public record BlacklistCallbackPayload(
|
||||||
String appId,
|
String appKey,
|
||||||
String id,
|
String id,
|
||||||
String userId,
|
String userId,
|
||||||
String blockedUserId,
|
String blockedUserId,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
package com.xuqm.im.model;
|
package com.xuqm.im.model;
|
||||||
|
|
||||||
public record FriendRequestCallbackPayload(
|
public record FriendRequestCallbackPayload(
|
||||||
String appId,
|
String appKey,
|
||||||
String requestId,
|
String requestId,
|
||||||
String fromUserId,
|
String fromUserId,
|
||||||
String toUserId,
|
String toUserId,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
package com.xuqm.im.model;
|
package com.xuqm.im.model;
|
||||||
|
|
||||||
public record GroupJoinRequestCallbackPayload(
|
public record GroupJoinRequestCallbackPayload(
|
||||||
String appId,
|
String appKey,
|
||||||
String requestId,
|
String requestId,
|
||||||
String groupId,
|
String groupId,
|
||||||
String groupName,
|
String groupName,
|
||||||
|
|||||||
@ -3,7 +3,7 @@ package com.xuqm.im.model;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public record MessageReadCallbackPayload(
|
public record MessageReadCallbackPayload(
|
||||||
String appId,
|
String appKey,
|
||||||
String readerId,
|
String readerId,
|
||||||
String peerId,
|
String peerId,
|
||||||
String groupId,
|
String groupId,
|
||||||
|
|||||||
@ -9,5 +9,5 @@ public record WebhookCallbackEnvelope(
|
|||||||
long requestTime,
|
long requestTime,
|
||||||
JsonNode payload,
|
JsonNode payload,
|
||||||
String signature,
|
String signature,
|
||||||
String appId
|
String appKey
|
||||||
) {}
|
) {}
|
||||||
|
|||||||
@ -22,12 +22,12 @@ public class BlacklistService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImBlacklistEntity add(String appId, String userId, String blockedUserId) {
|
public ImBlacklistEntity add(String appKey, String userId, String blockedUserId) {
|
||||||
return repository.findByAppIdAndUserIdAndBlockedUserId(appId, userId, blockedUserId)
|
return repository.findByAppIdAndUserIdAndBlockedUserId(appKey, userId, blockedUserId)
|
||||||
.orElseGet(() -> {
|
.orElseGet(() -> {
|
||||||
ImBlacklistEntity entity = new ImBlacklistEntity();
|
ImBlacklistEntity entity = new ImBlacklistEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appId);
|
entity.setAppId(appKey);
|
||||||
entity.setUserId(userId);
|
entity.setUserId(userId);
|
||||||
entity.setBlockedUserId(blockedUserId);
|
entity.setBlockedUserId(blockedUserId);
|
||||||
entity.setCreatedAt(LocalDateTime.now());
|
entity.setCreatedAt(LocalDateTime.now());
|
||||||
@ -38,29 +38,29 @@ public class BlacklistService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void remove(String appId, String userId, String blockedUserId) {
|
public void remove(String appKey, String userId, String blockedUserId) {
|
||||||
ImBlacklistEntity entity = repository.findByAppIdAndUserIdAndBlockedUserId(appId, userId, blockedUserId)
|
ImBlacklistEntity entity = repository.findByAppIdAndUserIdAndBlockedUserId(appKey, userId, blockedUserId)
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
repository.deleteByAppIdAndUserIdAndBlockedUserId(appId, userId, blockedUserId);
|
repository.deleteByAppIdAndUserIdAndBlockedUserId(appKey, userId, blockedUserId);
|
||||||
if (entity != null) {
|
if (entity != null) {
|
||||||
dispatchWebhook(entity, "blacklist.removed");
|
dispatchWebhook(entity, "blacklist.removed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImBlacklistEntity> list(String appId, String userId) {
|
public List<ImBlacklistEntity> list(String appKey, String userId) {
|
||||||
return repository.findByAppIdAndUserId(appId, userId);
|
return repository.findByAppIdAndUserId(appKey, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImBlacklistEntity> listByApp(String appId) {
|
public List<ImBlacklistEntity> listByApp(String appKey) {
|
||||||
return repository.findByAppId(appId);
|
return repository.findByAppId(appKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isBlocked(String appId, String userId, String blockedUserId) {
|
public boolean isBlocked(String appKey, String userId, String blockedUserId) {
|
||||||
return repository.existsByAppIdAndUserIdAndBlockedUserId(appId, userId, blockedUserId);
|
return repository.existsByAppIdAndUserIdAndBlockedUserId(appKey, userId, blockedUserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEitherBlocked(String appId, String userId, String targetUserId) {
|
public boolean isEitherBlocked(String appKey, String userId, String targetUserId) {
|
||||||
return isBlocked(appId, userId, targetUserId) || isBlocked(appId, targetUserId, userId);
|
return isBlocked(appKey, userId, targetUserId) || isBlocked(appKey, targetUserId, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dispatchWebhook(ImBlacklistEntity entity, String callbackEvent) {
|
private void dispatchWebhook(ImBlacklistEntity entity, String callbackEvent) {
|
||||||
|
|||||||
@ -20,24 +20,24 @@ public class ConversationStateService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImConversationStateEntity setPinned(String appId, String userId, String targetId, String chatType, boolean pinned) {
|
public ImConversationStateEntity setPinned(String appKey, String userId, String targetId, String chatType, boolean pinned) {
|
||||||
ImConversationStateEntity state = getOrCreate(appId, userId, targetId, chatType);
|
ImConversationStateEntity state = getOrCreate(appKey, userId, targetId, chatType);
|
||||||
state.setPinned(pinned);
|
state.setPinned(pinned);
|
||||||
touch(state);
|
touch(state);
|
||||||
return repository.save(state);
|
return repository.save(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImConversationStateEntity setMuted(String appId, String userId, String targetId, String chatType, boolean muted) {
|
public ImConversationStateEntity setMuted(String appKey, String userId, String targetId, String chatType, boolean muted) {
|
||||||
ImConversationStateEntity state = getOrCreate(appId, userId, targetId, chatType);
|
ImConversationStateEntity state = getOrCreate(appKey, userId, targetId, chatType);
|
||||||
state.setMuted(muted);
|
state.setMuted(muted);
|
||||||
touch(state);
|
touch(state);
|
||||||
return repository.save(state);
|
return repository.save(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImConversationStateEntity markRead(String appId, String userId, String targetId, String chatType) {
|
public ImConversationStateEntity markRead(String appKey, String userId, String targetId, String chatType) {
|
||||||
ImConversationStateEntity state = getOrCreate(appId, userId, targetId, chatType);
|
ImConversationStateEntity state = getOrCreate(appKey, userId, targetId, chatType);
|
||||||
state.setLastReadAt(LocalDateTime.now());
|
state.setLastReadAt(LocalDateTime.now());
|
||||||
state.setHidden(false);
|
state.setHidden(false);
|
||||||
touch(state);
|
touch(state);
|
||||||
@ -45,16 +45,16 @@ public class ConversationStateService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImConversationStateEntity setDraft(String appId, String userId, String targetId, String chatType, String draft) {
|
public ImConversationStateEntity setDraft(String appKey, String userId, String targetId, String chatType, String draft) {
|
||||||
ImConversationStateEntity state = getOrCreate(appId, userId, targetId, chatType);
|
ImConversationStateEntity state = getOrCreate(appKey, userId, targetId, chatType);
|
||||||
state.setDraft(draft);
|
state.setDraft(draft);
|
||||||
touch(state);
|
touch(state);
|
||||||
return repository.save(state);
|
return repository.save(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImConversationStateEntity setHidden(String appId, String userId, String targetId, String chatType, boolean hidden) {
|
public ImConversationStateEntity setHidden(String appKey, String userId, String targetId, String chatType, boolean hidden) {
|
||||||
ImConversationStateEntity state = getOrCreate(appId, userId, targetId, chatType);
|
ImConversationStateEntity state = getOrCreate(appKey, userId, targetId, chatType);
|
||||||
state.setHidden(hidden);
|
state.setHidden(hidden);
|
||||||
if (hidden) {
|
if (hidden) {
|
||||||
state.setDraft(null);
|
state.setDraft(null);
|
||||||
@ -65,20 +65,20 @@ public class ConversationStateService {
|
|||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImConversationStateEntity setConversationGroup(
|
public ImConversationStateEntity setConversationGroup(
|
||||||
String appId,
|
String appKey,
|
||||||
String userId,
|
String userId,
|
||||||
String targetId,
|
String targetId,
|
||||||
String chatType,
|
String chatType,
|
||||||
String conversationGroup) {
|
String conversationGroup) {
|
||||||
ImConversationStateEntity state = getOrCreate(appId, userId, targetId, chatType);
|
ImConversationStateEntity state = getOrCreate(appKey, userId, targetId, chatType);
|
||||||
state.setConversationGroup(normalizeGroup(conversationGroup));
|
state.setConversationGroup(normalizeGroup(conversationGroup));
|
||||||
touch(state);
|
touch(state);
|
||||||
return repository.save(state);
|
return repository.save(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImConversationStateEntity hideConversation(String appId, String userId, String targetId, String chatType) {
|
public ImConversationStateEntity hideConversation(String appKey, String userId, String targetId, String chatType) {
|
||||||
ImConversationStateEntity state = getOrCreate(appId, userId, targetId, chatType);
|
ImConversationStateEntity state = getOrCreate(appKey, userId, targetId, chatType);
|
||||||
state.setHidden(true);
|
state.setHidden(true);
|
||||||
state.setDraft(null);
|
state.setDraft(null);
|
||||||
touch(state);
|
touch(state);
|
||||||
@ -86,28 +86,28 @@ public class ConversationStateService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void deleteConversationState(String appId, String userId, String targetId, String chatType) {
|
public void deleteConversationState(String appKey, String userId, String targetId, String chatType) {
|
||||||
repository.deleteByAppIdAndUserIdAndTargetIdAndChatType(appId, userId, targetId, chatType);
|
repository.deleteByAppIdAndUserIdAndTargetIdAndChatType(appKey, userId, targetId, chatType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void deleteConversation(String appId,
|
public void deleteConversation(String appKey,
|
||||||
String userId,
|
String userId,
|
||||||
String targetId,
|
String targetId,
|
||||||
String chatType,
|
String chatType,
|
||||||
boolean syncAcrossClients) {
|
boolean syncAcrossClients) {
|
||||||
if (syncAcrossClients) {
|
if (syncAcrossClients) {
|
||||||
deleteConversationState(appId, userId, targetId, chatType);
|
deleteConversationState(appKey, userId, targetId, chatType);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
hideConversation(appId, userId, targetId, chatType);
|
hideConversation(appKey, userId, targetId, chatType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void clearHiddenForUsers(String appId, String targetId, String chatType, Collection<String> userIds) {
|
public void clearHiddenForUsers(String appKey, String targetId, String chatType, Collection<String> userIds) {
|
||||||
for (String userId : userIds) {
|
for (String userId : userIds) {
|
||||||
ImConversationStateEntity state = repository
|
ImConversationStateEntity state = repository
|
||||||
.findByAppIdAndUserIdAndTargetIdAndChatType(appId, userId, targetId, chatType)
|
.findByAppIdAndUserIdAndTargetIdAndChatType(appKey, userId, targetId, chatType)
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
if (state != null && state.isHidden()) {
|
if (state != null && state.isHidden()) {
|
||||||
state.setHidden(false);
|
state.setHidden(false);
|
||||||
@ -117,12 +117,12 @@ public class ConversationStateService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImConversationStateEntity getOrCreate(String appId, String userId, String targetId, String chatType) {
|
public ImConversationStateEntity getOrCreate(String appKey, String userId, String targetId, String chatType) {
|
||||||
return repository.findByAppIdAndUserIdAndTargetIdAndChatType(appId, userId, targetId, chatType)
|
return repository.findByAppIdAndUserIdAndTargetIdAndChatType(appKey, userId, targetId, chatType)
|
||||||
.orElseGet(() -> {
|
.orElseGet(() -> {
|
||||||
ImConversationStateEntity entity = new ImConversationStateEntity();
|
ImConversationStateEntity entity = new ImConversationStateEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appId);
|
entity.setAppId(appKey);
|
||||||
entity.setUserId(userId);
|
entity.setUserId(userId);
|
||||||
entity.setTargetId(targetId);
|
entity.setTargetId(targetId);
|
||||||
entity.setChatType(chatType);
|
entity.setChatType(chatType);
|
||||||
@ -138,17 +138,17 @@ public class ConversationStateService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImConversationStateEntity find(String appId, String userId, String targetId, String chatType) {
|
public ImConversationStateEntity find(String appKey, String userId, String targetId, String chatType) {
|
||||||
return repository.findByAppIdAndUserIdAndTargetIdAndChatType(appId, userId, targetId, chatType)
|
return repository.findByAppIdAndUserIdAndTargetIdAndChatType(appKey, userId, targetId, chatType)
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImConversationStateEntity> listVisible(String appId, String userId) {
|
public List<ImConversationStateEntity> listVisible(String appKey, String userId) {
|
||||||
return repository.findByAppIdAndUserIdAndHiddenFalse(appId, userId);
|
return repository.findByAppIdAndUserIdAndHiddenFalse(appKey, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> listConversationGroups(String appId, String userId) {
|
public List<String> listConversationGroups(String appKey, String userId) {
|
||||||
return repository.findByAppIdAndUserId(appId, userId).stream()
|
return repository.findByAppIdAndUserId(appKey, userId).stream()
|
||||||
.map(ImConversationStateEntity::getConversationGroup)
|
.map(ImConversationStateEntity::getConversationGroup)
|
||||||
.filter(group -> group != null && !group.isBlank())
|
.filter(group -> group != null && !group.isBlank())
|
||||||
.distinct()
|
.distinct()
|
||||||
@ -156,8 +156,8 @@ public class ConversationStateService {
|
|||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImConversationStateEntity> listByConversationGroup(String appId, String userId, String conversationGroup) {
|
public List<ImConversationStateEntity> listByConversationGroup(String appKey, String userId, String conversationGroup) {
|
||||||
return repository.findByAppIdAndUserIdAndConversationGroup(appId, userId, normalizeGroup(conversationGroup));
|
return repository.findByAppIdAndUserIdAndConversationGroup(appKey, userId, normalizeGroup(conversationGroup));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void touch(ImConversationStateEntity entity) {
|
private void touch(ImConversationStateEntity entity) {
|
||||||
|
|||||||
@ -45,18 +45,18 @@ public class FriendRequestService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImFriendRequestEntity send(String appId, String fromUserId, String toUserId, String remark) {
|
public ImFriendRequestEntity send(String appKey, String fromUserId, String toUserId, String remark) {
|
||||||
String mode = featureConfigClient.friendRequestMode(appId);
|
String mode = featureConfigClient.friendRequestMode(appKey);
|
||||||
if ("DISALLOW".equals(mode)) {
|
if ("DISALLOW".equals(mode)) {
|
||||||
throw new BusinessException(403, "当前应用未开放好友申请");
|
throw new BusinessException(403, "当前应用未开放好友申请");
|
||||||
}
|
}
|
||||||
final boolean[] created = {false};
|
final boolean[] created = {false};
|
||||||
ImFriendRequestEntity saved = requestRepository.findByAppIdAndFromUserIdAndToUserId(appId, fromUserId, toUserId)
|
ImFriendRequestEntity saved = requestRepository.findByAppIdAndFromUserIdAndToUserId(appKey, fromUserId, toUserId)
|
||||||
.orElseGet(() -> {
|
.orElseGet(() -> {
|
||||||
created[0] = true;
|
created[0] = true;
|
||||||
ImFriendRequestEntity entity = new ImFriendRequestEntity();
|
ImFriendRequestEntity entity = new ImFriendRequestEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appId);
|
entity.setAppId(appKey);
|
||||||
entity.setFromUserId(fromUserId);
|
entity.setFromUserId(fromUserId);
|
||||||
entity.setToUserId(toUserId);
|
entity.setToUserId(toUserId);
|
||||||
entity.setRemark(remark);
|
entity.setRemark(remark);
|
||||||
@ -88,17 +88,17 @@ public class FriendRequestService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImFriendRequestEntity accept(String appId, String requestId, String operatorId) {
|
public ImFriendRequestEntity accept(String appKey, String requestId, String operatorId) {
|
||||||
ImFriendRequestEntity request = getRequest(appId, requestId, operatorId);
|
ImFriendRequestEntity request = getRequest(appKey, requestId, operatorId);
|
||||||
request.setStatus(ImFriendRequestEntity.Status.ACCEPTED.name());
|
request.setStatus(ImFriendRequestEntity.Status.ACCEPTED.name());
|
||||||
request.setReviewedAt(LocalDateTime.now());
|
request.setReviewedAt(LocalDateTime.now());
|
||||||
requestRepository.save(request);
|
requestRepository.save(request);
|
||||||
friendRepository
|
friendRepository
|
||||||
.findByAppIdAndUserIdAndFriendId(appId, request.getFromUserId(), request.getToUserId())
|
.findByAppIdAndUserIdAndFriendId(appKey, request.getFromUserId(), request.getToUserId())
|
||||||
.orElseGet(() -> friendEntity(appId, request.getFromUserId(), request.getToUserId()));
|
.orElseGet(() -> friendEntity(appKey, request.getFromUserId(), request.getToUserId()));
|
||||||
friendRepository
|
friendRepository
|
||||||
.findByAppIdAndUserIdAndFriendId(appId, request.getToUserId(), request.getFromUserId())
|
.findByAppIdAndUserIdAndFriendId(appKey, request.getToUserId(), request.getFromUserId())
|
||||||
.orElseGet(() -> friendEntity(appId, request.getToUserId(), request.getFromUserId()));
|
.orElseGet(() -> friendEntity(appKey, request.getToUserId(), request.getFromUserId()));
|
||||||
dispatchWebhook(request, "friend.request.accepted");
|
dispatchWebhook(request, "friend.request.accepted");
|
||||||
publishNotification(
|
publishNotification(
|
||||||
request,
|
request,
|
||||||
@ -112,8 +112,8 @@ public class FriendRequestService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImFriendRequestEntity reject(String appId, String requestId, String operatorId) {
|
public ImFriendRequestEntity reject(String appKey, String requestId, String operatorId) {
|
||||||
ImFriendRequestEntity request = getRequest(appId, requestId, operatorId);
|
ImFriendRequestEntity request = getRequest(appKey, requestId, operatorId);
|
||||||
request.setStatus(ImFriendRequestEntity.Status.REJECTED.name());
|
request.setStatus(ImFriendRequestEntity.Status.REJECTED.name());
|
||||||
request.setReviewedAt(LocalDateTime.now());
|
request.setReviewedAt(LocalDateTime.now());
|
||||||
ImFriendRequestEntity saved = requestRepository.save(request);
|
ImFriendRequestEntity saved = requestRepository.save(request);
|
||||||
@ -130,69 +130,69 @@ public class FriendRequestService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public List<ImFriendRequestEntity> acceptBatch(String appId, List<String> requestIds, String operatorId) {
|
public List<ImFriendRequestEntity> acceptBatch(String appKey, List<String> requestIds, String operatorId) {
|
||||||
List<ImFriendRequestEntity> result = new java.util.ArrayList<>();
|
List<ImFriendRequestEntity> result = new java.util.ArrayList<>();
|
||||||
for (String requestId : unique(requestIds)) {
|
for (String requestId : unique(requestIds)) {
|
||||||
result.add(acceptInternal(appId, requestId, operatorId));
|
result.add(acceptInternal(appKey, requestId, operatorId));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public List<ImFriendRequestEntity> rejectBatch(String appId, List<String> requestIds, String operatorId) {
|
public List<ImFriendRequestEntity> rejectBatch(String appKey, List<String> requestIds, String operatorId) {
|
||||||
List<ImFriendRequestEntity> result = new java.util.ArrayList<>();
|
List<ImFriendRequestEntity> result = new java.util.ArrayList<>();
|
||||||
for (String requestId : unique(requestIds)) {
|
for (String requestId : unique(requestIds)) {
|
||||||
result.add(rejectInternal(appId, requestId, operatorId));
|
result.add(rejectInternal(appKey, requestId, operatorId));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImFriendRequestEntity> incoming(String appId, String userId) {
|
public List<ImFriendRequestEntity> incoming(String appKey, String userId) {
|
||||||
return requestRepository.findByAppIdAndToUserId(appId, userId).stream()
|
return requestRepository.findByAppIdAndToUserId(appKey, userId).stream()
|
||||||
.filter(request -> ImFriendRequestEntity.Status.PENDING.name().equals(request.getStatus()))
|
.filter(request -> ImFriendRequestEntity.Status.PENDING.name().equals(request.getStatus()))
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImFriendRequestEntity> outgoing(String appId, String userId) {
|
public List<ImFriendRequestEntity> outgoing(String appKey, String userId) {
|
||||||
return requestRepository.findByAppIdAndFromUserId(appId, userId);
|
return requestRepository.findByAppIdAndFromUserId(appKey, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImFriendRequestEntity> listByApp(String appId) {
|
public List<ImFriendRequestEntity> listByApp(String appKey) {
|
||||||
return requestRepository.findByAppId(appId);
|
return requestRepository.findByAppId(appKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImFriendRequestEntity adminAccept(String appId, String requestId) {
|
public ImFriendRequestEntity adminAccept(String appKey, String requestId) {
|
||||||
ImFriendRequestEntity request = getRequest(appId, requestId);
|
ImFriendRequestEntity request = getRequest(appKey, requestId);
|
||||||
return acceptRequest(request);
|
return acceptRequest(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImFriendRequestEntity adminReject(String appId, String requestId) {
|
public ImFriendRequestEntity adminReject(String appKey, String requestId) {
|
||||||
ImFriendRequestEntity request = getRequest(appId, requestId);
|
ImFriendRequestEntity request = getRequest(appKey, requestId);
|
||||||
return rejectRequest(request);
|
return rejectRequest(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImFriendRequestEntity getRequest(String appId, String requestId, String operatorId) {
|
private ImFriendRequestEntity getRequest(String appKey, String requestId, String operatorId) {
|
||||||
ImFriendRequestEntity request = requestRepository.findById(requestId)
|
ImFriendRequestEntity request = requestRepository.findById(requestId)
|
||||||
.orElseThrow(() -> new BusinessException(404, "好友申请不存在"));
|
.orElseThrow(() -> new BusinessException(404, "好友申请不存在"));
|
||||||
if (!request.getAppId().equals(appId) || !request.getToUserId().equals(operatorId)) {
|
if (!request.getAppId().equals(appKey) || !request.getToUserId().equals(operatorId)) {
|
||||||
throw new BusinessException(403, "无权操作");
|
throw new BusinessException(403, "无权操作");
|
||||||
}
|
}
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImFriendRequestEntity getRequest(String appId, String requestId) {
|
private ImFriendRequestEntity getRequest(String appKey, String requestId) {
|
||||||
ImFriendRequestEntity request = requestRepository.findById(requestId)
|
ImFriendRequestEntity request = requestRepository.findById(requestId)
|
||||||
.orElseThrow(() -> new BusinessException(404, "好友申请不存在"));
|
.orElseThrow(() -> new BusinessException(404, "好友申请不存在"));
|
||||||
if (!request.getAppId().equals(appId)) {
|
if (!request.getAppId().equals(appKey)) {
|
||||||
throw new BusinessException(403, "无权操作");
|
throw new BusinessException(403, "无权操作");
|
||||||
}
|
}
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImFriendRequestEntity acceptInternal(String appId, String requestId, String operatorId) {
|
private ImFriendRequestEntity acceptInternal(String appKey, String requestId, String operatorId) {
|
||||||
ImFriendRequestEntity request = getRequest(appId, requestId, operatorId);
|
ImFriendRequestEntity request = getRequest(appKey, requestId, operatorId);
|
||||||
return acceptRequest(request);
|
return acceptRequest(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,14 +218,14 @@ public class FriendRequestService {
|
|||||||
return saved;
|
return saved;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImFriendRequestEntity rejectInternal(String appId, String requestId, String operatorId) {
|
private ImFriendRequestEntity rejectInternal(String appKey, String requestId, String operatorId) {
|
||||||
ImFriendRequestEntity request = getRequest(appId, requestId, operatorId);
|
ImFriendRequestEntity request = getRequest(appKey, requestId, operatorId);
|
||||||
return rejectRequest(request);
|
return rejectRequest(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
private com.xuqm.im.entity.ImFriendEntity friendEntity(String appId, String userId, String friendId) {
|
private com.xuqm.im.entity.ImFriendEntity friendEntity(String appKey, String userId, String friendId) {
|
||||||
com.xuqm.im.entity.ImFriendEntity entity = new com.xuqm.im.entity.ImFriendEntity();
|
com.xuqm.im.entity.ImFriendEntity entity = new com.xuqm.im.entity.ImFriendEntity();
|
||||||
entity.setAppId(appId);
|
entity.setAppId(appKey);
|
||||||
entity.setUserId(userId);
|
entity.setUserId(userId);
|
||||||
entity.setFriendId(friendId);
|
entity.setFriendId(friendId);
|
||||||
return friendRepository.save(entity);
|
return friendRepository.save(entity);
|
||||||
|
|||||||
@ -16,15 +16,15 @@ public class GlobalMuteService {
|
|||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEnabled(String appId) {
|
public boolean isEnabled(String appKey) {
|
||||||
return repository.findByAppId(appId).map(ImGlobalMuteEntity::isEnabled).orElse(false);
|
return repository.findByAppId(appKey).map(ImGlobalMuteEntity::isEnabled).orElse(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImGlobalMuteEntity get(String appId) {
|
public ImGlobalMuteEntity get(String appKey) {
|
||||||
return repository.findByAppId(appId).orElseGet(() -> {
|
return repository.findByAppId(appKey).orElseGet(() -> {
|
||||||
ImGlobalMuteEntity entity = new ImGlobalMuteEntity();
|
ImGlobalMuteEntity entity = new ImGlobalMuteEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appId);
|
entity.setAppId(appKey);
|
||||||
entity.setEnabled(false);
|
entity.setEnabled(false);
|
||||||
entity.setCreatedAt(LocalDateTime.now());
|
entity.setCreatedAt(LocalDateTime.now());
|
||||||
entity.setUpdatedAt(LocalDateTime.now());
|
entity.setUpdatedAt(LocalDateTime.now());
|
||||||
@ -32,11 +32,11 @@ public class GlobalMuteService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImGlobalMuteEntity setEnabled(String appId, boolean enabled) {
|
public ImGlobalMuteEntity setEnabled(String appKey, boolean enabled) {
|
||||||
ImGlobalMuteEntity entity = repository.findByAppId(appId).orElseGet(() -> {
|
ImGlobalMuteEntity entity = repository.findByAppId(appKey).orElseGet(() -> {
|
||||||
ImGlobalMuteEntity created = new ImGlobalMuteEntity();
|
ImGlobalMuteEntity created = new ImGlobalMuteEntity();
|
||||||
created.setId(UUID.randomUUID().toString());
|
created.setId(UUID.randomUUID().toString());
|
||||||
created.setAppId(appId);
|
created.setAppId(appKey);
|
||||||
created.setCreatedAt(LocalDateTime.now());
|
created.setCreatedAt(LocalDateTime.now());
|
||||||
return created;
|
return created;
|
||||||
});
|
});
|
||||||
|
|||||||
@ -28,7 +28,7 @@ public class ImAccountService {
|
|||||||
this.appSecretClient = appSecretClient;
|
this.appSecretClient = appSecretClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validateSignature(String appId,
|
public void validateSignature(String appKey,
|
||||||
String userId,
|
String userId,
|
||||||
String timestamp,
|
String timestamp,
|
||||||
String nonce,
|
String nonce,
|
||||||
@ -39,19 +39,19 @@ public class ImAccountService {
|
|||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
throw new BusinessException(401, "Invalid app signature");
|
throw new BusinessException(401, "Invalid app signature");
|
||||||
}
|
}
|
||||||
String secret = appSecretClient.getAppSecret(appId);
|
String secret = appSecretClient.getAppSecret(appKey);
|
||||||
String payload = AppRequestSignatureUtil.payload(appId, userId, ts, nonce);
|
String payload = AppRequestSignatureUtil.payload(appKey, userId, ts, nonce);
|
||||||
if (!AppRequestSignatureUtil.matches(secret, payload, signature)) {
|
if (!AppRequestSignatureUtil.matches(secret, payload, signature)) {
|
||||||
throw new BusinessException(401, "Invalid app signature");
|
throw new BusinessException(401, "Invalid app signature");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoginResult loginOrRegister(String appId, String userId) {
|
public LoginResult loginOrRegister(String appKey, String userId) {
|
||||||
ImAccountEntity account = accountRepository.findByAppIdAndUserId(appId, userId)
|
ImAccountEntity account = accountRepository.findByAppIdAndUserId(appKey, userId)
|
||||||
.orElseGet(() -> {
|
.orElseGet(() -> {
|
||||||
ImAccountEntity e = new ImAccountEntity();
|
ImAccountEntity e = new ImAccountEntity();
|
||||||
e.setId(UUID.randomUUID().toString());
|
e.setId(UUID.randomUUID().toString());
|
||||||
e.setAppId(appId);
|
e.setAppId(appKey);
|
||||||
e.setUserId(userId);
|
e.setUserId(userId);
|
||||||
e.setGender(ImAccountEntity.Gender.UNKNOWN);
|
e.setGender(ImAccountEntity.Gender.UNKNOWN);
|
||||||
e.setStatus(ImAccountEntity.Status.ACTIVE);
|
e.setStatus(ImAccountEntity.Status.ACTIVE);
|
||||||
@ -63,17 +63,17 @@ public class ImAccountService {
|
|||||||
throw new BusinessException(403, "账号已被封禁");
|
throw new BusinessException(403, "账号已被封禁");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new LoginResult(jwtUtil.generate(userId, Map.of("appId", appId, "role", "USER")));
|
return new LoginResult(jwtUtil.generate(userId, Map.of("appKey", appKey, "role", "USER")));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImAccountEntity getAccount(String appId, String userId) {
|
public ImAccountEntity getAccount(String appKey, String userId) {
|
||||||
return accountRepository.findByAppIdAndUserId(appId, userId)
|
return accountRepository.findByAppIdAndUserId(appKey, userId)
|
||||||
.orElseThrow(() -> new BusinessException(404, "账号不存在"));
|
.orElseThrow(() -> new BusinessException(404, "账号不存在"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImAccountEntity updateAccount(String appId, String userId, String nickname,
|
public ImAccountEntity updateAccount(String appKey, String userId, String nickname,
|
||||||
String avatar, ImAccountEntity.Gender gender) {
|
String avatar, ImAccountEntity.Gender gender) {
|
||||||
ImAccountEntity account = getAccount(appId, userId);
|
ImAccountEntity account = getAccount(appKey, userId);
|
||||||
if (nickname != null) account.setNickname(nickname);
|
if (nickname != null) account.setNickname(nickname);
|
||||||
if (avatar != null) account.setAvatar(avatar);
|
if (avatar != null) account.setAvatar(avatar);
|
||||||
if (gender != null) account.setGender(gender);
|
if (gender != null) account.setGender(gender);
|
||||||
@ -81,13 +81,13 @@ public class ImAccountService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ImAccountEntity updateAccount(
|
public ImAccountEntity updateAccount(
|
||||||
String appId,
|
String appKey,
|
||||||
String userId,
|
String userId,
|
||||||
String nickname,
|
String nickname,
|
||||||
String avatar,
|
String avatar,
|
||||||
ImAccountEntity.Gender gender,
|
ImAccountEntity.Gender gender,
|
||||||
ImAccountEntity.Status status) {
|
ImAccountEntity.Status status) {
|
||||||
ImAccountEntity account = getAccount(appId, userId);
|
ImAccountEntity account = getAccount(appKey, userId);
|
||||||
if (nickname != null) account.setNickname(nickname);
|
if (nickname != null) account.setNickname(nickname);
|
||||||
if (avatar != null) account.setAvatar(avatar);
|
if (avatar != null) account.setAvatar(avatar);
|
||||||
if (gender != null) account.setGender(gender);
|
if (gender != null) account.setGender(gender);
|
||||||
@ -95,14 +95,14 @@ public class ImAccountService {
|
|||||||
return accountRepository.save(account);
|
return accountRepository.save(account);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImAccountEntity importAccount(String appId, String userId, String nickname,
|
public ImAccountEntity importAccount(String appKey, String userId, String nickname,
|
||||||
String avatar, ImAccountEntity.Gender gender,
|
String avatar, ImAccountEntity.Gender gender,
|
||||||
ImAccountEntity.Status status) {
|
ImAccountEntity.Status status) {
|
||||||
ImAccountEntity account = accountRepository.findByAppIdAndUserId(appId, userId)
|
ImAccountEntity account = accountRepository.findByAppIdAndUserId(appKey, userId)
|
||||||
.orElseGet(() -> {
|
.orElseGet(() -> {
|
||||||
ImAccountEntity entity = new ImAccountEntity();
|
ImAccountEntity entity = new ImAccountEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appId);
|
entity.setAppId(appKey);
|
||||||
entity.setUserId(userId);
|
entity.setUserId(userId);
|
||||||
entity.setCreatedAt(LocalDateTime.now());
|
entity.setCreatedAt(LocalDateTime.now());
|
||||||
return entity;
|
return entity;
|
||||||
@ -114,24 +114,24 @@ public class ImAccountService {
|
|||||||
return accountRepository.save(account);
|
return accountRepository.save(account);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImAccountEntity> importAccounts(String appId, List<ImportAccountRequest> requests) {
|
public List<ImAccountEntity> importAccounts(String appKey, List<ImportAccountRequest> requests) {
|
||||||
return requests == null ? List.of() : requests.stream()
|
return requests == null ? List.of() : requests.stream()
|
||||||
.filter(req -> req != null && req.userId() != null && !req.userId().isBlank())
|
.filter(req -> req != null && req.userId() != null && !req.userId().isBlank())
|
||||||
.map(req -> importAccount(appId, req.userId(), req.nickname(), req.avatar(), req.gender(), req.status()))
|
.map(req -> importAccount(appKey, req.userId(), req.nickname(), req.avatar(), req.gender(), req.status()))
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteAccount(String appId, String userId) {
|
public void deleteAccount(String appKey, String userId) {
|
||||||
accountRepository.findByAppIdAndUserId(appId, userId)
|
accountRepository.findByAppIdAndUserId(appKey, userId)
|
||||||
.ifPresent(accountRepository::delete);
|
.ifPresent(accountRepository::delete);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean exists(String appId, String userId) {
|
public boolean exists(String appKey, String userId) {
|
||||||
return accountRepository.existsByAppIdAndUserId(appId, userId);
|
return accountRepository.existsByAppIdAndUserId(appKey, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImAccountEntity> searchAccounts(String appId, String keyword, int size) {
|
public List<ImAccountEntity> searchAccounts(String appKey, String keyword, int size) {
|
||||||
return accountRepository.searchByKeyword(appId, keyword, PageRequest.of(0, Math.max(size, 1)));
|
return accountRepository.searchByKeyword(appKey, keyword, PageRequest.of(0, Math.max(size, 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public record ImportAccountRequest(
|
public record ImportAccountRequest(
|
||||||
|
|||||||
@ -27,14 +27,14 @@ public class ImAppSecretClient {
|
|||||||
@Value("${im.internal-token:xuqm-internal-token}")
|
@Value("${im.internal-token:xuqm-internal-token}")
|
||||||
private String internalToken;
|
private String internalToken;
|
||||||
|
|
||||||
public String getAppSecret(String appId) {
|
public String getAppSecret(String appKey) {
|
||||||
return cache.computeIfAbsent(appId, this::fetchAppSecret);
|
return cache.computeIfAbsent(appKey, this::fetchAppSecret);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String fetchAppSecret(String appId) {
|
private String fetchAppSecret(String appKey) {
|
||||||
String url = UriComponentsBuilder.fromHttpUrl(tenantServiceUrl)
|
String url = UriComponentsBuilder.fromHttpUrl(tenantServiceUrl)
|
||||||
.path("/api/internal/sdk/apps/{appId}/secret")
|
.path("/api/internal/sdk/apps/{appKey}/secret")
|
||||||
.buildAndExpand(appId)
|
.buildAndExpand(appKey)
|
||||||
.toUriString();
|
.toUriString();
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
headers.set("X-Internal-Token", internalToken);
|
headers.set("X-Internal-Token", internalToken);
|
||||||
@ -54,6 +54,6 @@ public class ImAppSecretClient {
|
|||||||
} catch (RestClientException e) {
|
} catch (RestClientException e) {
|
||||||
throw new BusinessException(502, "Failed to resolve app secret: " + e.getMessage());
|
throw new BusinessException(502, "Failed to resolve app secret: " + e.getMessage());
|
||||||
}
|
}
|
||||||
throw new BusinessException(502, "Failed to resolve app secret for appId: " + appId);
|
throw new BusinessException(502, "Failed to resolve app secret for appKey: " + appKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,16 +31,16 @@ public class ImFeatureConfigClient {
|
|||||||
this.restTemplate = new RestTemplate();
|
this.restTemplate = new RestTemplate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean allowStrangerMessage(String appId) {
|
public boolean allowStrangerMessage(String appKey) {
|
||||||
return readConfig(appId).path("allowStrangerMessage").asBoolean(false);
|
return readConfig(appKey).path("allowStrangerMessage").asBoolean(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean allowFriendRequest(String appId) {
|
public boolean allowFriendRequest(String appKey) {
|
||||||
return readConfig(appId).path("allowFriendRequest").asBoolean(true);
|
return readConfig(appKey).path("allowFriendRequest").asBoolean(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String friendRequestMode(String appId) {
|
public String friendRequestMode(String appKey) {
|
||||||
JsonNode node = readConfig(appId);
|
JsonNode node = readConfig(appKey);
|
||||||
String mode = node.path("friendRequestMode").asText("");
|
String mode = node.path("friendRequestMode").asText("");
|
||||||
String normalized = mode == null ? "" : mode.trim().toUpperCase();
|
String normalized = mode == null ? "" : mode.trim().toUpperCase();
|
||||||
return switch (normalized) {
|
return switch (normalized) {
|
||||||
@ -49,46 +49,46 @@ public class ImFeatureConfigClient {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean allowGroupJoinRequest(String appId) {
|
public boolean allowGroupJoinRequest(String appKey) {
|
||||||
return readConfig(appId).path("allowGroupJoinRequest").asBoolean(true);
|
return readConfig(appKey).path("allowGroupJoinRequest").asBoolean(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean blacklistSendSuccess(String appId) {
|
public boolean blacklistSendSuccess(String appKey) {
|
||||||
return readConfig(appId).path("blacklistSendSuccess").asBoolean(true);
|
return readConfig(appKey).path("blacklistSendSuccess").asBoolean(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int messageRecallMinutes(String appId) {
|
public int messageRecallMinutes(String appKey) {
|
||||||
return Math.max(readConfig(appId).path("messageRecallMinutes").asInt(2), 0);
|
return Math.max(readConfig(appKey).path("messageRecallMinutes").asInt(2), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int historyRetentionDays(String appId) {
|
public int historyRetentionDays(String appKey) {
|
||||||
return Math.max(readConfig(appId).path("historyRetentionDays").asInt(7), 1);
|
return Math.max(readConfig(appKey).path("historyRetentionDays").asInt(7), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int conversationPullLimit(String appId) {
|
public int conversationPullLimit(String appKey) {
|
||||||
return Math.min(Math.max(readConfig(appId).path("conversationPullLimit").asInt(100), 1), 500);
|
return Math.min(Math.max(readConfig(appKey).path("conversationPullLimit").asInt(100), 1), 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean multiClientConversationDeleteSync(String appId) {
|
public boolean multiClientConversationDeleteSync(String appKey) {
|
||||||
return readConfig(appId).path("multiClientConversationDeleteSync").asBoolean(false);
|
return readConfig(appKey).path("multiClientConversationDeleteSync").asBoolean(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean allowMultiDeviceLogin(String appId) {
|
public boolean allowMultiDeviceLogin(String appKey) {
|
||||||
return readConfig(appId).path("allowMultiDeviceLogin").asBoolean(true);
|
return readConfig(appKey).path("allowMultiDeviceLogin").asBoolean(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String multiDeviceLoginMode(String appId) {
|
public String multiDeviceLoginMode(String appKey) {
|
||||||
String mode = readConfig(appId).path("multiDeviceLoginMode").asText("MULTI_DEVICE_FREE");
|
String mode = readConfig(appKey).path("multiDeviceLoginMode").asText("MULTI_DEVICE_FREE");
|
||||||
return switch (mode.toUpperCase()) {
|
return switch (mode.toUpperCase()) {
|
||||||
case "SAME_PLATFORM_ONE", "SINGLE_DEVICE" -> mode.toUpperCase();
|
case "SAME_PLATFORM_ONE", "SINGLE_DEVICE" -> mode.toUpperCase();
|
||||||
default -> "MULTI_DEVICE_FREE";
|
default -> "MULTI_DEVICE_FREE";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private JsonNode readConfig(String appId) {
|
private JsonNode readConfig(String appKey) {
|
||||||
String url = UriComponentsBuilder.fromHttpUrl(tenantServiceUrl)
|
String url = UriComponentsBuilder.fromHttpUrl(tenantServiceUrl)
|
||||||
.path("/api/internal/sdk/apps/{appId}/services/{platform}/{serviceType}")
|
.path("/api/internal/sdk/apps/{appKey}/services/{platform}/{serviceType}")
|
||||||
.buildAndExpand(appId, "ANDROID", "IM")
|
.buildAndExpand(appKey, "ANDROID", "IM")
|
||||||
.toUriString();
|
.toUriString();
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
headers.set("X-Internal-Token", internalToken);
|
headers.set("X-Internal-Token", internalToken);
|
||||||
@ -108,7 +108,7 @@ public class ImFeatureConfigClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("Failed to read IM feature config for appId={}: {}", appId, e.getMessage());
|
log.warn("Failed to read IM feature config for appKey={}: {}", appKey, e.getMessage());
|
||||||
}
|
}
|
||||||
return OBJECT_MAPPER.createObjectNode();
|
return OBJECT_MAPPER.createObjectNode();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -64,7 +64,7 @@ public class ImGroupService {
|
|||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImGroupEntity create(
|
public ImGroupEntity create(
|
||||||
String appId,
|
String appKey,
|
||||||
String name,
|
String name,
|
||||||
String creatorId,
|
String creatorId,
|
||||||
List<String> memberIds,
|
List<String> memberIds,
|
||||||
@ -75,7 +75,7 @@ public class ImGroupService {
|
|||||||
|
|
||||||
ImGroupEntity group = new ImGroupEntity();
|
ImGroupEntity group = new ImGroupEntity();
|
||||||
group.setId(UUID.randomUUID().toString());
|
group.setId(UUID.randomUUID().toString());
|
||||||
group.setAppId(appId);
|
group.setAppId(appKey);
|
||||||
group.setName(name);
|
group.setName(name);
|
||||||
group.setGroupType(normalizeGroupType(groupType));
|
group.setGroupType(normalizeGroupType(groupType));
|
||||||
group.setCreatorId(creatorId);
|
group.setCreatorId(creatorId);
|
||||||
@ -85,7 +85,7 @@ public class ImGroupService {
|
|||||||
group.setExtAttributes("{}");
|
group.setExtAttributes("{}");
|
||||||
group.setCreatedAt(LocalDateTime.now());
|
group.setCreatedAt(LocalDateTime.now());
|
||||||
ImGroupEntity saved = groupRepository.save(group);
|
ImGroupEntity saved = groupRepository.save(group);
|
||||||
webhookDispatchService.dispatch(appId, "group", "group.created",
|
webhookDispatchService.dispatch(appKey, "group", "group.created",
|
||||||
java.util.Map.of("groupId", saved.getId(), "name", saved.getName(),
|
java.util.Map.of("groupId", saved.getId(), "name", saved.getName(),
|
||||||
"creatorId", creatorId, "memberIds", members));
|
"creatorId", creatorId, "memberIds", members));
|
||||||
return saved;
|
return saved;
|
||||||
@ -267,17 +267,17 @@ public class ImGroupService {
|
|||||||
return fromJson(group.getAdminIds());
|
return fromJson(group.getAdminIds());
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImGroupEntity> listByApp(String appId) {
|
public List<ImGroupEntity> listByApp(String appKey) {
|
||||||
return groupRepository.findByAppId(appId);
|
return groupRepository.findByAppId(appKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImGroupEntity> listUserGroups(String appId, String userId) {
|
public List<ImGroupEntity> listUserGroups(String appKey, String userId) {
|
||||||
return groupRepository.findUserGroups(appId, userId);
|
return groupRepository.findUserGroups(appKey, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImGroupEntity> listPublicGroups(String appId, String keyword) {
|
public List<ImGroupEntity> listPublicGroups(String appKey, String keyword) {
|
||||||
String normalizedKeyword = keyword == null ? "" : keyword.trim().toLowerCase();
|
String normalizedKeyword = keyword == null ? "" : keyword.trim().toLowerCase();
|
||||||
return groupRepository.findByAppId(appId).stream()
|
return groupRepository.findByAppId(appKey).stream()
|
||||||
.filter(group -> "PUBLIC".equalsIgnoreCase(normalizeGroupType(group.getGroupType())))
|
.filter(group -> "PUBLIC".equalsIgnoreCase(normalizeGroupType(group.getGroupType())))
|
||||||
.filter(group -> normalizedKeyword.isBlank()
|
.filter(group -> normalizedKeyword.isBlank()
|
||||||
|| group.getName().toLowerCase().contains(normalizedKeyword)
|
|| group.getName().toLowerCase().contains(normalizedKeyword)
|
||||||
@ -285,35 +285,35 @@ public class ImGroupService {
|
|||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImGroupEntity> searchGroups(String appId, String keyword, int size) {
|
public List<ImGroupEntity> searchGroups(String appKey, String keyword, int size) {
|
||||||
return groupRepository.searchByKeyword(appId, keyword, PageRequest.of(0, Math.max(size, 1)));
|
return groupRepository.searchByKeyword(appKey, keyword, PageRequest.of(0, Math.max(size, 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImAccountEntity> listMembers(String appId, String groupId, String requesterId) {
|
public List<ImAccountEntity> listMembers(String appKey, String groupId, String requesterId) {
|
||||||
ImGroupEntity group = get(groupId, requesterId);
|
ImGroupEntity group = get(groupId, requesterId);
|
||||||
return resolveMembers(appId, memberIds(group));
|
return resolveMembers(appKey, memberIds(group));
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImAccountEntity> searchMembers(String appId, String groupId, String requesterId, String keyword, int size) {
|
public List<ImAccountEntity> searchMembers(String appKey, String groupId, String requesterId, String keyword, int size) {
|
||||||
ImGroupEntity group = get(groupId, requesterId);
|
ImGroupEntity group = get(groupId, requesterId);
|
||||||
List<String> ids = memberIds(group);
|
List<String> ids = memberIds(group);
|
||||||
if (keyword == null || keyword.isBlank()) {
|
if (keyword == null || keyword.isBlank()) {
|
||||||
return resolveMembers(appId, ids).stream().limit(Math.max(size, 1)).toList();
|
return resolveMembers(appKey, ids).stream().limit(Math.max(size, 1)).toList();
|
||||||
}
|
}
|
||||||
LinkedHashSet<String> memberIdSet = new LinkedHashSet<>(ids);
|
LinkedHashSet<String> memberIdSet = new LinkedHashSet<>(ids);
|
||||||
return accountRepository.searchByKeyword(appId, keyword, PageRequest.of(0, Math.max(size, 1)))
|
return accountRepository.searchByKeyword(appKey, keyword, PageRequest.of(0, Math.max(size, 1)))
|
||||||
.stream()
|
.stream()
|
||||||
.filter(account -> memberIdSet.contains(account.getUserId()))
|
.filter(account -> memberIdSet.contains(account.getUserId()))
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImGroupJoinRequestEntity sendJoinRequest(String appId, String groupId, String requesterId, String remark) {
|
public ImGroupJoinRequestEntity sendJoinRequest(String appKey, String groupId, String requesterId, String remark) {
|
||||||
if (!featureConfigClient.allowGroupJoinRequest(appId)) {
|
if (!featureConfigClient.allowGroupJoinRequest(appKey)) {
|
||||||
throw new BusinessException(403, "当前应用未开放群加入申请");
|
throw new BusinessException(403, "当前应用未开放群加入申请");
|
||||||
}
|
}
|
||||||
ImGroupEntity group = get(groupId);
|
ImGroupEntity group = get(groupId);
|
||||||
if (!group.getAppId().equals(appId)) {
|
if (!group.getAppId().equals(appKey)) {
|
||||||
throw new BusinessException(403, "无权操作");
|
throw new BusinessException(403, "无权操作");
|
||||||
}
|
}
|
||||||
if (!"PUBLIC".equalsIgnoreCase(normalizeGroupType(group.getGroupType()))) {
|
if (!"PUBLIC".equalsIgnoreCase(normalizeGroupType(group.getGroupType()))) {
|
||||||
@ -322,11 +322,11 @@ public class ImGroupService {
|
|||||||
if (memberIds(group).contains(requesterId)) {
|
if (memberIds(group).contains(requesterId)) {
|
||||||
throw new BusinessException(400, "已经在群内");
|
throw new BusinessException(400, "已经在群内");
|
||||||
}
|
}
|
||||||
return joinRequestRepository.findByAppIdAndGroupIdAndRequesterId(appId, groupId, requesterId)
|
return joinRequestRepository.findByAppIdAndGroupIdAndRequesterId(appKey, groupId, requesterId)
|
||||||
.orElseGet(() -> {
|
.orElseGet(() -> {
|
||||||
ImGroupJoinRequestEntity entity = new ImGroupJoinRequestEntity();
|
ImGroupJoinRequestEntity entity = new ImGroupJoinRequestEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appId);
|
entity.setAppId(appKey);
|
||||||
entity.setGroupId(groupId);
|
entity.setGroupId(groupId);
|
||||||
entity.setRequesterId(requesterId);
|
entity.setRequesterId(requesterId);
|
||||||
entity.setRemark(remark);
|
entity.setRemark(remark);
|
||||||
@ -347,15 +347,15 @@ public class ImGroupService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImGroupJoinRequestEntity> listJoinRequests(String appId, String groupId, String operatorId) {
|
public List<ImGroupJoinRequestEntity> listJoinRequests(String appKey, String groupId, String operatorId) {
|
||||||
ImGroupEntity group = get(groupId);
|
ImGroupEntity group = get(groupId);
|
||||||
ensureCanManage(group, operatorId);
|
ensureCanManage(group, operatorId);
|
||||||
return joinRequestRepository.findByAppIdAndGroupId(appId, groupId);
|
return joinRequestRepository.findByAppIdAndGroupId(appKey, groupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImGroupJoinRequestEntity acceptJoinRequest(String appId, String requestId, String operatorId) {
|
public ImGroupJoinRequestEntity acceptJoinRequest(String appKey, String requestId, String operatorId) {
|
||||||
ImGroupJoinRequestEntity request = getJoinRequest(appId, requestId);
|
ImGroupJoinRequestEntity request = getJoinRequest(appKey, requestId);
|
||||||
ImGroupEntity group = get(request.getGroupId());
|
ImGroupEntity group = get(request.getGroupId());
|
||||||
ensureCanManage(group, operatorId);
|
ensureCanManage(group, operatorId);
|
||||||
request.setStatus(ImGroupJoinRequestEntity.Status.ACCEPTED.name());
|
request.setStatus(ImGroupJoinRequestEntity.Status.ACCEPTED.name());
|
||||||
@ -376,8 +376,8 @@ public class ImGroupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImGroupJoinRequestEntity rejectJoinRequest(String appId, String requestId, String operatorId) {
|
public ImGroupJoinRequestEntity rejectJoinRequest(String appKey, String requestId, String operatorId) {
|
||||||
ImGroupJoinRequestEntity request = getJoinRequest(appId, requestId);
|
ImGroupJoinRequestEntity request = getJoinRequest(appKey, requestId);
|
||||||
ImGroupEntity group = get(request.getGroupId());
|
ImGroupEntity group = get(request.getGroupId());
|
||||||
ensureCanManage(group, operatorId);
|
ensureCanManage(group, operatorId);
|
||||||
request.setStatus(ImGroupJoinRequestEntity.Status.REJECTED.name());
|
request.setStatus(ImGroupJoinRequestEntity.Status.REJECTED.name());
|
||||||
@ -397,23 +397,23 @@ public class ImGroupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public List<ImGroupJoinRequestEntity> acceptJoinRequests(String appId, String groupId, List<String> requestIds, String operatorId) {
|
public List<ImGroupJoinRequestEntity> acceptJoinRequests(String appKey, String groupId, List<String> requestIds, String operatorId) {
|
||||||
ImGroupEntity group = get(groupId);
|
ImGroupEntity group = get(groupId);
|
||||||
ensureCanManage(group, operatorId);
|
ensureCanManage(group, operatorId);
|
||||||
List<ImGroupJoinRequestEntity> result = new ArrayList<>();
|
List<ImGroupJoinRequestEntity> result = new ArrayList<>();
|
||||||
for (String requestId : unique(requestIds)) {
|
for (String requestId : unique(requestIds)) {
|
||||||
result.add(acceptJoinRequestInternal(appId, group, requestId, operatorId));
|
result.add(acceptJoinRequestInternal(appKey, group, requestId, operatorId));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public List<ImGroupJoinRequestEntity> rejectJoinRequests(String appId, String groupId, List<String> requestIds, String operatorId) {
|
public List<ImGroupJoinRequestEntity> rejectJoinRequests(String appKey, String groupId, List<String> requestIds, String operatorId) {
|
||||||
ImGroupEntity group = get(groupId);
|
ImGroupEntity group = get(groupId);
|
||||||
ensureCanManage(group, operatorId);
|
ensureCanManage(group, operatorId);
|
||||||
List<ImGroupJoinRequestEntity> result = new ArrayList<>();
|
List<ImGroupJoinRequestEntity> result = new ArrayList<>();
|
||||||
for (String requestId : unique(requestIds)) {
|
for (String requestId : unique(requestIds)) {
|
||||||
result.add(rejectJoinRequestInternal(appId, group, requestId, operatorId));
|
result.add(rejectJoinRequestInternal(appKey, group, requestId, operatorId));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -441,27 +441,27 @@ public class ImGroupService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ensureAppMatches(ImGroupEntity group, String appId) {
|
private void ensureAppMatches(ImGroupEntity group, String appKey) {
|
||||||
if (!group.getAppId().equals(appId)) {
|
if (!group.getAppId().equals(appKey)) {
|
||||||
throw new BusinessException(403, "无权操作");
|
throw new BusinessException(403, "无权操作");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImAccountEntity> adminListMembers(String appId, String groupId) {
|
public List<ImAccountEntity> adminListMembers(String appKey, String groupId) {
|
||||||
ImGroupEntity group = get(groupId);
|
ImGroupEntity group = get(groupId);
|
||||||
ensureAppMatches(group, appId);
|
ensureAppMatches(group, appKey);
|
||||||
return resolveMembers(appId, memberIds(group));
|
return resolveMembers(appKey, memberIds(group));
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImAccountEntity> adminSearchMembers(String appId, String groupId, String keyword, int size) {
|
public List<ImAccountEntity> adminSearchMembers(String appKey, String groupId, String keyword, int size) {
|
||||||
ImGroupEntity group = get(groupId);
|
ImGroupEntity group = get(groupId);
|
||||||
ensureAppMatches(group, appId);
|
ensureAppMatches(group, appKey);
|
||||||
List<String> ids = memberIds(group);
|
List<String> ids = memberIds(group);
|
||||||
if (keyword == null || keyword.isBlank()) {
|
if (keyword == null || keyword.isBlank()) {
|
||||||
return resolveMembers(appId, ids).stream().limit(Math.max(size, 1)).toList();
|
return resolveMembers(appKey, ids).stream().limit(Math.max(size, 1)).toList();
|
||||||
}
|
}
|
||||||
LinkedHashSet<String> memberIdSet = new LinkedHashSet<>(ids);
|
LinkedHashSet<String> memberIdSet = new LinkedHashSet<>(ids);
|
||||||
return accountRepository.searchByKeyword(appId, keyword, PageRequest.of(0, Math.max(size, 1)))
|
return accountRepository.searchByKeyword(appKey, keyword, PageRequest.of(0, Math.max(size, 1)))
|
||||||
.stream()
|
.stream()
|
||||||
.filter(account -> memberIdSet.contains(account.getUserId()))
|
.filter(account -> memberIdSet.contains(account.getUserId()))
|
||||||
.toList();
|
.toList();
|
||||||
@ -477,9 +477,9 @@ public class ImGroupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImGroupEntity adminTransferOwner(String appId, String groupId, String newOwnerId) {
|
public ImGroupEntity adminTransferOwner(String appKey, String groupId, String newOwnerId) {
|
||||||
ImGroupEntity group = get(groupId);
|
ImGroupEntity group = get(groupId);
|
||||||
ensureAppMatches(group, appId);
|
ensureAppMatches(group, appKey);
|
||||||
return transferOwnerInternal(group, newOwnerId);
|
return transferOwnerInternal(group, newOwnerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -491,9 +491,9 @@ public class ImGroupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImGroupEntity adminUpdateExtAttributes(String appId, String groupId, Map<String, Object> attributes) {
|
public ImGroupEntity adminUpdateExtAttributes(String appKey, String groupId, Map<String, Object> attributes) {
|
||||||
ImGroupEntity group = get(groupId);
|
ImGroupEntity group = get(groupId);
|
||||||
ensureAppMatches(group, appId);
|
ensureAppMatches(group, appKey);
|
||||||
return updateExtAttributesInternal(group, attributes);
|
return updateExtAttributesInternal(group, attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -505,24 +505,24 @@ public class ImGroupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImGroupEntity adminRemoveExtAttributes(String appId, String groupId, List<String> keys) {
|
public ImGroupEntity adminRemoveExtAttributes(String appKey, String groupId, List<String> keys) {
|
||||||
ImGroupEntity group = get(groupId);
|
ImGroupEntity group = get(groupId);
|
||||||
ensureAppMatches(group, appId);
|
ensureAppMatches(group, appKey);
|
||||||
return removeExtAttributesInternal(group, keys);
|
return removeExtAttributesInternal(group, keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImGroupEntity adminAddMember(String appId, String groupId, String userId) {
|
public ImGroupEntity adminAddMember(String appKey, String groupId, String userId) {
|
||||||
ImGroupEntity group = get(groupId);
|
ImGroupEntity group = get(groupId);
|
||||||
ensureAppMatches(group, appId);
|
ensureAppMatches(group, appKey);
|
||||||
addMemberInternal(group, userId);
|
addMemberInternal(group, userId);
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImGroupEntity adminAddMembers(String appId, String groupId, List<String> userIds) {
|
public ImGroupEntity adminAddMembers(String appKey, String groupId, List<String> userIds) {
|
||||||
ImGroupEntity group = get(groupId);
|
ImGroupEntity group = get(groupId);
|
||||||
ensureAppMatches(group, appId);
|
ensureAppMatches(group, appKey);
|
||||||
List<String> members = new ArrayList<>(fromJson(group.getMemberIds()));
|
List<String> members = new ArrayList<>(fromJson(group.getMemberIds()));
|
||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
for (String userId : userIds == null ? List.<String>of() : userIds) {
|
for (String userId : userIds == null ? List.<String>of() : userIds) {
|
||||||
@ -542,9 +542,9 @@ public class ImGroupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImGroupEntity adminRemoveMember(String appId, String groupId, String userId) {
|
public ImGroupEntity adminRemoveMember(String appKey, String groupId, String userId) {
|
||||||
ImGroupEntity group = get(groupId);
|
ImGroupEntity group = get(groupId);
|
||||||
ensureAppMatches(group, appId);
|
ensureAppMatches(group, appKey);
|
||||||
List<String> members = new ArrayList<>(fromJson(group.getMemberIds()));
|
List<String> members = new ArrayList<>(fromJson(group.getMemberIds()));
|
||||||
if (members.remove(userId)) {
|
if (members.remove(userId)) {
|
||||||
group.setMemberIds(toJson(members));
|
group.setMemberIds(toJson(members));
|
||||||
@ -554,9 +554,9 @@ public class ImGroupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImGroupEntity adminRemoveMembers(String appId, String groupId, List<String> userIds) {
|
public ImGroupEntity adminRemoveMembers(String appKey, String groupId, List<String> userIds) {
|
||||||
ImGroupEntity group = get(groupId);
|
ImGroupEntity group = get(groupId);
|
||||||
ensureAppMatches(group, appId);
|
ensureAppMatches(group, appKey);
|
||||||
List<String> members = new ArrayList<>(fromJson(group.getMemberIds()));
|
List<String> members = new ArrayList<>(fromJson(group.getMemberIds()));
|
||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
for (String userId : userIds == null ? List.<String>of() : userIds) {
|
for (String userId : userIds == null ? List.<String>of() : userIds) {
|
||||||
@ -573,9 +573,9 @@ public class ImGroupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImGroupEntity adminSetRole(String appId, String groupId, String userId, String role) {
|
public ImGroupEntity adminSetRole(String appKey, String groupId, String userId, String role) {
|
||||||
ImGroupEntity group = get(groupId);
|
ImGroupEntity group = get(groupId);
|
||||||
ensureAppMatches(group, appId);
|
ensureAppMatches(group, appKey);
|
||||||
List<String> admins = new ArrayList<>(fromJson(group.getAdminIds()));
|
List<String> admins = new ArrayList<>(fromJson(group.getAdminIds()));
|
||||||
if ("ADMIN".equalsIgnoreCase(role)) {
|
if ("ADMIN".equalsIgnoreCase(role)) {
|
||||||
if (!admins.contains(userId)) {
|
if (!admins.contains(userId)) {
|
||||||
@ -592,9 +592,9 @@ public class ImGroupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImGroupEntity adminMuteMember(String appId, String groupId, String userId, long minutes) {
|
public ImGroupEntity adminMuteMember(String appKey, String groupId, String userId, long minutes) {
|
||||||
ImGroupEntity group = get(groupId);
|
ImGroupEntity group = get(groupId);
|
||||||
ensureAppMatches(group, appId);
|
ensureAppMatches(group, appKey);
|
||||||
ImGroupMuteEntity mute = muteRepository
|
ImGroupMuteEntity mute = muteRepository
|
||||||
.findByGroupIdAndUserIdAndMutedUntilAfter(groupId, userId, LocalDateTime.now())
|
.findByGroupIdAndUserIdAndMutedUntilAfter(groupId, userId, LocalDateTime.now())
|
||||||
.orElseGet(() -> {
|
.orElseGet(() -> {
|
||||||
@ -611,10 +611,10 @@ public class ImGroupService {
|
|||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImGroupJoinRequestEntity> adminListJoinRequests(String appId, String groupId) {
|
public List<ImGroupJoinRequestEntity> adminListJoinRequests(String appKey, String groupId) {
|
||||||
ImGroupEntity group = get(groupId);
|
ImGroupEntity group = get(groupId);
|
||||||
ensureAppMatches(group, appId);
|
ensureAppMatches(group, appKey);
|
||||||
return joinRequestRepository.findByAppIdAndGroupId(appId, groupId);
|
return joinRequestRepository.findByAppIdAndGroupId(appKey, groupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@ -634,9 +634,9 @@ public class ImGroupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImGroupEntity adminModifyMemberInfo(String appId, String groupId, String userId, String nickName) {
|
public ImGroupEntity adminModifyMemberInfo(String appKey, String groupId, String userId, String nickName) {
|
||||||
ImGroupEntity group = get(groupId);
|
ImGroupEntity group = get(groupId);
|
||||||
ensureAppMatches(group, appId);
|
ensureAppMatches(group, appKey);
|
||||||
return modifyMemberInfo(groupId, userId, nickName);
|
return modifyMemberInfo(groupId, userId, nickName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -711,10 +711,10 @@ public class ImGroupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImGroupJoinRequestEntity adminAcceptJoinRequest(String appId, String groupId, String requestId) {
|
public ImGroupJoinRequestEntity adminAcceptJoinRequest(String appKey, String groupId, String requestId) {
|
||||||
ImGroupEntity group = get(groupId);
|
ImGroupEntity group = get(groupId);
|
||||||
ensureAppMatches(group, appId);
|
ensureAppMatches(group, appKey);
|
||||||
ImGroupJoinRequestEntity request = getJoinRequest(appId, requestId);
|
ImGroupJoinRequestEntity request = getJoinRequest(appKey, requestId);
|
||||||
if (!group.getId().equals(request.getGroupId())) {
|
if (!group.getId().equals(request.getGroupId())) {
|
||||||
throw new BusinessException(400, "加群申请不属于当前群");
|
throw new BusinessException(400, "加群申请不属于当前群");
|
||||||
}
|
}
|
||||||
@ -727,10 +727,10 @@ public class ImGroupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ImGroupJoinRequestEntity adminRejectJoinRequest(String appId, String groupId, String requestId) {
|
public ImGroupJoinRequestEntity adminRejectJoinRequest(String appKey, String groupId, String requestId) {
|
||||||
ImGroupEntity group = get(groupId);
|
ImGroupEntity group = get(groupId);
|
||||||
ensureAppMatches(group, appId);
|
ensureAppMatches(group, appKey);
|
||||||
ImGroupJoinRequestEntity request = getJoinRequest(appId, requestId);
|
ImGroupJoinRequestEntity request = getJoinRequest(appKey, requestId);
|
||||||
if (!group.getId().equals(request.getGroupId())) {
|
if (!group.getId().equals(request.getGroupId())) {
|
||||||
throw new BusinessException(400, "加群申请不属于当前群");
|
throw new BusinessException(400, "加群申请不属于当前群");
|
||||||
}
|
}
|
||||||
@ -829,17 +829,17 @@ public class ImGroupService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImGroupJoinRequestEntity getJoinRequest(String appId, String requestId) {
|
private ImGroupJoinRequestEntity getJoinRequest(String appKey, String requestId) {
|
||||||
ImGroupJoinRequestEntity request = joinRequestRepository.findById(requestId)
|
ImGroupJoinRequestEntity request = joinRequestRepository.findById(requestId)
|
||||||
.orElseThrow(() -> new BusinessException(404, "加群申请不存在"));
|
.orElseThrow(() -> new BusinessException(404, "加群申请不存在"));
|
||||||
if (!request.getAppId().equals(appId)) {
|
if (!request.getAppId().equals(appKey)) {
|
||||||
throw new BusinessException(403, "无权操作");
|
throw new BusinessException(403, "无权操作");
|
||||||
}
|
}
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImGroupJoinRequestEntity acceptJoinRequestInternal(String appId, ImGroupEntity group, String requestId, String operatorId) {
|
private ImGroupJoinRequestEntity acceptJoinRequestInternal(String appKey, ImGroupEntity group, String requestId, String operatorId) {
|
||||||
ImGroupJoinRequestEntity request = getJoinRequest(appId, requestId);
|
ImGroupJoinRequestEntity request = getJoinRequest(appKey, requestId);
|
||||||
if (!group.getId().equals(request.getGroupId())) {
|
if (!group.getId().equals(request.getGroupId())) {
|
||||||
throw new BusinessException(400, "加群申请不属于当前群");
|
throw new BusinessException(400, "加群申请不属于当前群");
|
||||||
}
|
}
|
||||||
@ -861,8 +861,8 @@ public class ImGroupService {
|
|||||||
return saved;
|
return saved;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImGroupJoinRequestEntity rejectJoinRequestInternal(String appId, ImGroupEntity group, String requestId, String operatorId) {
|
private ImGroupJoinRequestEntity rejectJoinRequestInternal(String appKey, ImGroupEntity group, String requestId, String operatorId) {
|
||||||
ImGroupJoinRequestEntity request = getJoinRequest(appId, requestId);
|
ImGroupJoinRequestEntity request = getJoinRequest(appKey, requestId);
|
||||||
if (!group.getId().equals(request.getGroupId())) {
|
if (!group.getId().equals(request.getGroupId())) {
|
||||||
throw new BusinessException(400, "加群申请不属于当前群");
|
throw new BusinessException(400, "加群申请不属于当前群");
|
||||||
}
|
}
|
||||||
@ -891,10 +891,10 @@ public class ImGroupService {
|
|||||||
return values == null ? List.of() : new ArrayList<>(new LinkedHashSet<>(values));
|
return values == null ? List.of() : new ArrayList<>(new LinkedHashSet<>(values));
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ImAccountEntity> resolveMembers(String appId, List<String> ids) {
|
private List<ImAccountEntity> resolveMembers(String appKey, List<String> ids) {
|
||||||
List<ImAccountEntity> members = new ArrayList<>();
|
List<ImAccountEntity> members = new ArrayList<>();
|
||||||
for (String userId : ids == null ? List.<String>of() : ids) {
|
for (String userId : ids == null ? List.<String>of() : ids) {
|
||||||
accountRepository.findByAppIdAndUserId(appId, userId).ifPresent(members::add);
|
accountRepository.findByAppIdAndUserId(appKey, userId).ifPresent(members::add);
|
||||||
}
|
}
|
||||||
return members;
|
return members;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,14 +34,14 @@ public class ImPushBridge {
|
|||||||
this.objectMapper = objectMapper;
|
this.objectMapper = objectMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendOfflinePush(String appId, String userId, String title, String body, String payload) {
|
public void sendOfflinePush(String appKey, String userId, String title, String body, String payload) {
|
||||||
if (isOnline(userId)) {
|
if (isOnline(userId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sendOfflinePushToUsers(appId, List.of(userId), title, body, payload);
|
sendOfflinePushToUsers(appKey, List.of(userId), title, body, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendOfflinePushToUsers(String appId, List<String> userIds, String title, String body, String payload) {
|
public void sendOfflinePushToUsers(String appKey, List<String> userIds, String title, String body, String payload) {
|
||||||
if (userIds == null || userIds.isEmpty()) {
|
if (userIds == null || userIds.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -56,7 +56,7 @@ public class ImPushBridge {
|
|||||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
headers.set("X-Internal-Token", internalToken);
|
headers.set("X-Internal-Token", internalToken);
|
||||||
Map<String, Object> bodyMap = Map.of(
|
Map<String, Object> bodyMap = Map.of(
|
||||||
"appId", appId,
|
"appKey", appKey,
|
||||||
"userIds", offlineUserIds,
|
"userIds", offlineUserIds,
|
||||||
"title", title,
|
"title", title,
|
||||||
"body", body,
|
"body", body,
|
||||||
@ -64,9 +64,9 @@ public class ImPushBridge {
|
|||||||
);
|
);
|
||||||
HttpEntity<String> request = new HttpEntity<>(objectMapper.writeValueAsString(bodyMap), headers);
|
HttpEntity<String> request = new HttpEntity<>(objectMapper.writeValueAsString(bodyMap), headers);
|
||||||
restTemplate.postForEntity(pushServiceUrl + "/api/push/internal/notify", request, String.class);
|
restTemplate.postForEntity(pushServiceUrl + "/api/push/internal/notify", request, String.class);
|
||||||
log.debug("Sync offline push sent appId={} users={} title={}", appId, offlineUserIds.size(), title);
|
log.debug("Sync offline push sent appKey={} users={} title={}", appKey, offlineUserIds.size(), title);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("Failed to send offline push appId={} users={}: {}", appId, offlineUserIds.size(), e.getMessage());
|
log.warn("Failed to send offline push appKey={} users={}: {}", appKey, offlineUserIds.size(), e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -32,13 +32,13 @@ public class ImPushBridgeClient {
|
|||||||
this.objectMapper = objectMapper;
|
this.objectMapper = objectMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void notifyUsers(String appId, List<String> userIds, String title, String body, String payload) {
|
public void notifyUsers(String appKey, List<String> userIds, String title, String body, String payload) {
|
||||||
if (userIds == null || userIds.isEmpty()) {
|
if (userIds == null || userIds.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
Map<String, Object> bodyMap = new LinkedHashMap<>();
|
Map<String, Object> bodyMap = new LinkedHashMap<>();
|
||||||
bodyMap.put("appId", appId);
|
bodyMap.put("appKey", appKey);
|
||||||
bodyMap.put("userIds", userIds);
|
bodyMap.put("userIds", userIds);
|
||||||
bodyMap.put("title", title);
|
bodyMap.put("title", title);
|
||||||
bodyMap.put("body", body);
|
bodyMap.put("body", body);
|
||||||
@ -52,7 +52,7 @@ public class ImPushBridgeClient {
|
|||||||
.build();
|
.build();
|
||||||
httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("Failed to notify push-service for appId={}, userIds={}: {}", appId, userIds, e.getMessage());
|
log.warn("Failed to notify push-service for appKey={}, userIds={}: {}", appKey, userIds, e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,8 +19,8 @@ public class KeywordFilterService {
|
|||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String filter(String appId, String content) {
|
public String filter(String appKey, String content) {
|
||||||
List<KeywordFilterEntity> filters = repository.findByAppIdAndEnabledTrue(appId);
|
List<KeywordFilterEntity> filters = repository.findByAppIdAndEnabledTrue(appKey);
|
||||||
String result = content;
|
String result = content;
|
||||||
for (KeywordFilterEntity f : filters) {
|
for (KeywordFilterEntity f : filters) {
|
||||||
try {
|
try {
|
||||||
@ -39,10 +39,10 @@ public class KeywordFilterService {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeywordFilterEntity add(String appId, String pattern, String replacement, String action) {
|
public KeywordFilterEntity add(String appKey, String pattern, String replacement, String action) {
|
||||||
KeywordFilterEntity entity = new KeywordFilterEntity();
|
KeywordFilterEntity entity = new KeywordFilterEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appId);
|
entity.setAppId(appKey);
|
||||||
entity.setPattern(pattern);
|
entity.setPattern(pattern);
|
||||||
entity.setReplacement(replacement);
|
entity.setReplacement(replacement);
|
||||||
entity.setAction(action);
|
entity.setAction(action);
|
||||||
@ -51,12 +51,12 @@ public class KeywordFilterService {
|
|||||||
return repository.save(entity);
|
return repository.save(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<KeywordFilterEntity> list(String appId) {
|
public List<KeywordFilterEntity> list(String appKey) {
|
||||||
return repository.findByAppId(appId);
|
return repository.findByAppId(appKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeywordFilterEntity update(String appId, String id, String pattern, String replacement, String action, Boolean enabled) {
|
public KeywordFilterEntity update(String appKey, String id, String pattern, String replacement, String action, Boolean enabled) {
|
||||||
KeywordFilterEntity entity = repository.findByIdAndAppId(id, appId)
|
KeywordFilterEntity entity = repository.findByIdAndAppId(id, appKey)
|
||||||
.orElseThrow(() -> new BusinessException(404, "关键词过滤规则不存在"));
|
.orElseThrow(() -> new BusinessException(404, "关键词过滤规则不存在"));
|
||||||
if (pattern != null) {
|
if (pattern != null) {
|
||||||
entity.setPattern(pattern);
|
entity.setPattern(pattern);
|
||||||
@ -73,8 +73,8 @@ public class KeywordFilterService {
|
|||||||
return repository.save(entity);
|
return repository.save(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void delete(String appId, String id) {
|
public void delete(String appKey, String id) {
|
||||||
KeywordFilterEntity entity = repository.findByIdAndAppId(id, appId)
|
KeywordFilterEntity entity = repository.findByIdAndAppId(id, appKey)
|
||||||
.orElseThrow(() -> new BusinessException(404, "关键词过滤规则不存在"));
|
.orElseThrow(() -> new BusinessException(404, "关键词过滤规则不存在"));
|
||||||
repository.delete(entity);
|
repository.delete(entity);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -76,13 +76,13 @@ public class MessageService {
|
|||||||
this.objectMapper = objectMapper;
|
this.objectMapper = objectMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImMessageEntity send(String appId, String fromUserId, SendMessageRequest req) {
|
public ImMessageEntity send(String appKey, String fromUserId, SendMessageRequest req) {
|
||||||
if (globalMuteService.isEnabled(appId)) {
|
if (globalMuteService.isEnabled(appKey)) {
|
||||||
throw new BusinessException(403, "当前应用已开启全局禁言");
|
throw new BusinessException(403, "当前应用已开启全局禁言");
|
||||||
}
|
}
|
||||||
String content = req.content();
|
String content = req.content();
|
||||||
if (req.msgType() == ImMessageEntity.MsgType.TEXT) {
|
if (req.msgType() == ImMessageEntity.MsgType.TEXT) {
|
||||||
content = keywordFilterService.filter(appId, content);
|
content = keywordFilterService.filter(appKey, content);
|
||||||
if (content == null) {
|
if (content == null) {
|
||||||
throw new BusinessException("消息包含违禁内容");
|
throw new BusinessException("消息包含违禁内容");
|
||||||
}
|
}
|
||||||
@ -97,13 +97,13 @@ public class MessageService {
|
|||||||
if (groupService.isMemberMuted(req.toId(), fromUserId)) {
|
if (groupService.isMemberMuted(req.toId(), fromUserId)) {
|
||||||
throw new BusinessException(403, "当前用户已被禁言");
|
throw new BusinessException(403, "当前用户已被禁言");
|
||||||
}
|
}
|
||||||
} else if (!isFriend(appId, fromUserId, req.toId())
|
} else if (!isFriend(appKey, fromUserId, req.toId())
|
||||||
&& !featureConfigClient.allowStrangerMessage(appId)) {
|
&& !featureConfigClient.allowStrangerMessage(appKey)) {
|
||||||
throw new BusinessException(403, "仅允许好友之间发送消息");
|
throw new BusinessException(403, "仅允许好友之间发送消息");
|
||||||
} else {
|
} else {
|
||||||
boolean senderBlocksReceiver = blacklistService.isBlocked(appId, fromUserId, req.toId());
|
boolean senderBlocksReceiver = blacklistService.isBlocked(appKey, fromUserId, req.toId());
|
||||||
receiverBlocksSender = blacklistService.isBlocked(appId, req.toId(), fromUserId);
|
receiverBlocksSender = blacklistService.isBlocked(appKey, req.toId(), fromUserId);
|
||||||
boolean blacklistSendSuccess = featureConfigClient.blacklistSendSuccess(appId);
|
boolean blacklistSendSuccess = featureConfigClient.blacklistSendSuccess(appKey);
|
||||||
if (senderBlocksReceiver && receiverBlocksSender) {
|
if (senderBlocksReceiver && receiverBlocksSender) {
|
||||||
throw new BusinessException(403, "已被拉黑,无法发送消息");
|
throw new BusinessException(403, "已被拉黑,无法发送消息");
|
||||||
}
|
}
|
||||||
@ -116,7 +116,7 @@ public class MessageService {
|
|||||||
message.setId(req.messageId() != null && !req.messageId().isBlank()
|
message.setId(req.messageId() != null && !req.messageId().isBlank()
|
||||||
? req.messageId()
|
? req.messageId()
|
||||||
: UUID.randomUUID().toString());
|
: UUID.randomUUID().toString());
|
||||||
message.setAppId(appId);
|
message.setAppId(appKey);
|
||||||
message.setFromUserId(fromUserId);
|
message.setFromUserId(fromUserId);
|
||||||
message.setToId(req.toId());
|
message.setToId(req.toId());
|
||||||
message.setChatType(req.chatType());
|
message.setChatType(req.chatType());
|
||||||
@ -127,42 +127,42 @@ public class MessageService {
|
|||||||
message.setCreatedAt(LocalDateTime.now());
|
message.setCreatedAt(LocalDateTime.now());
|
||||||
ImMessageEntity saved = messageRepository.save(message);
|
ImMessageEntity saved = messageRepository.save(message);
|
||||||
if (req.chatType() == ImMessageEntity.ChatType.GROUP) {
|
if (req.chatType() == ImMessageEntity.ChatType.GROUP) {
|
||||||
saved.setGroupReadCount(groupReadCount(appId, req.toId(), saved.getCreatedAt(), saved.getFromUserId()));
|
saved.setGroupReadCount(groupReadCount(appKey, req.toId(), saved.getCreatedAt(), saved.getFromUserId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.chatType() == ImMessageEntity.ChatType.SINGLE && !fromUserId.equals(req.toId())) {
|
if (req.chatType() == ImMessageEntity.ChatType.SINGLE && !fromUserId.equals(req.toId())) {
|
||||||
log.debug("echo message back to sender appId={} from={} to={}",
|
log.debug("echo message back to sender appKey={} from={} to={}",
|
||||||
appId, fromUserId, req.toId());
|
appKey, fromUserId, req.toId());
|
||||||
clusterPublisher.publish("/user/" + fromUserId + "/queue/messages", saved);
|
clusterPublisher.publish("/user/" + fromUserId + "/queue/messages", saved);
|
||||||
if (!receiverBlocksSender) {
|
if (!receiverBlocksSender) {
|
||||||
log.debug("deliver message to receiver appId={} from={} to={}",
|
log.debug("deliver message to receiver appKey={} from={} to={}",
|
||||||
appId, fromUserId, req.toId());
|
appKey, fromUserId, req.toId());
|
||||||
boolean receiverOnline = userPresenceService.isOnline(req.toId());
|
boolean receiverOnline = userPresenceService.isOnline(req.toId());
|
||||||
if (receiverOnline) {
|
if (receiverOnline) {
|
||||||
clusterPublisher.publish("/user/" + req.toId() + "/queue/messages", saved);
|
clusterPublisher.publish("/user/" + req.toId() + "/queue/messages", saved);
|
||||||
} else {
|
} else {
|
||||||
offlineMessageSyncService.storeOfflineMessage(appId, req.toId(), saved.getId());
|
offlineMessageSyncService.storeOfflineMessage(appKey, req.toId(), saved.getId());
|
||||||
}
|
}
|
||||||
conversationStateService.clearHiddenForUsers(appId, req.toId(), req.chatType().name(), List.of(fromUserId, req.toId()));
|
conversationStateService.clearHiddenForUsers(appKey, req.toId(), req.chatType().name(), List.of(fromUserId, req.toId()));
|
||||||
imPushBridge.sendOfflinePushToUsers(
|
imPushBridge.sendOfflinePushToUsers(
|
||||||
appId,
|
appKey,
|
||||||
List.of(req.toId()),
|
List.of(req.toId()),
|
||||||
"新消息",
|
"新消息",
|
||||||
saved.getContent(),
|
saved.getContent(),
|
||||||
buildPushPayload(saved)
|
buildPushPayload(saved)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
conversationStateService.clearHiddenForUsers(appId, req.toId(), req.chatType().name(), List.of(fromUserId));
|
conversationStateService.clearHiddenForUsers(appKey, req.toId(), req.chatType().name(), List.of(fromUserId));
|
||||||
}
|
}
|
||||||
} else if (req.chatType() == ImMessageEntity.ChatType.GROUP) {
|
} else if (req.chatType() == ImMessageEntity.ChatType.GROUP) {
|
||||||
String destination = "/topic/group/" + req.toId();
|
String destination = "/topic/group/" + req.toId();
|
||||||
log.debug("send message appId={} from={} to={} chatType={} msgType={} destination={}",
|
log.debug("send message appKey={} from={} to={} chatType={} msgType={} destination={}",
|
||||||
appId, fromUserId, req.toId(), req.chatType(), req.msgType(), destination);
|
appKey, fromUserId, req.toId(), req.chatType(), req.msgType(), destination);
|
||||||
clusterPublisher.publish(destination, saved);
|
clusterPublisher.publish(destination, saved);
|
||||||
List<String> memberIds = groupService.memberIds(group);
|
List<String> memberIds = groupService.memberIds(group);
|
||||||
conversationStateService.clearHiddenForUsers(appId, req.toId(), req.chatType().name(), memberIds);
|
conversationStateService.clearHiddenForUsers(appKey, req.toId(), req.chatType().name(), memberIds);
|
||||||
imPushBridge.sendOfflinePushToUsers(
|
imPushBridge.sendOfflinePushToUsers(
|
||||||
appId,
|
appKey,
|
||||||
memberIds.stream()
|
memberIds.stream()
|
||||||
.filter(memberId -> !memberId.equals(fromUserId))
|
.filter(memberId -> !memberId.equals(fromUserId))
|
||||||
.toList(),
|
.toList(),
|
||||||
@ -172,32 +172,32 @@ public class MessageService {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
String destination = "/user/" + req.toId() + "/queue/messages";
|
String destination = "/user/" + req.toId() + "/queue/messages";
|
||||||
log.debug("send message appId={} from={} to={} chatType={} msgType={} destination={}",
|
log.debug("send message appKey={} from={} to={} chatType={} msgType={} destination={}",
|
||||||
appId, fromUserId, req.toId(), req.chatType(), req.msgType(), destination);
|
appKey, fromUserId, req.toId(), req.chatType(), req.msgType(), destination);
|
||||||
if (!receiverBlocksSender) {
|
if (!receiverBlocksSender) {
|
||||||
clusterPublisher.publish(destination, saved);
|
clusterPublisher.publish(destination, saved);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatchWebhooks(appId, "message.sent", saved);
|
dispatchWebhooks(appKey, "message.sent", saved);
|
||||||
return saved;
|
return saved;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isFriend(String appId, String userId, String friendId) {
|
private boolean isFriend(String appKey, String userId, String friendId) {
|
||||||
return friendRepository.existsByAppIdAndUserIdAndFriendId(appId, userId, friendId)
|
return friendRepository.existsByAppIdAndUserIdAndFriendId(appKey, userId, friendId)
|
||||||
|| friendRepository.existsByAppIdAndUserIdAndFriendId(appId, friendId, userId);
|
|| friendRepository.existsByAppIdAndUserIdAndFriendId(appKey, friendId, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImMessageEntity revoke(String appId, String messageId, String requestUserId) {
|
public ImMessageEntity revoke(String appKey, String messageId, String requestUserId) {
|
||||||
ImMessageEntity message = messageRepository.findById(messageId)
|
ImMessageEntity message = messageRepository.findById(messageId)
|
||||||
.orElseThrow(() -> new BusinessException(404, "消息不存在"));
|
.orElseThrow(() -> new BusinessException(404, "消息不存在"));
|
||||||
if (!message.getAppId().equals(appId)) {
|
if (!message.getAppId().equals(appKey)) {
|
||||||
throw new BusinessException(403, "无权操作");
|
throw new BusinessException(403, "无权操作");
|
||||||
}
|
}
|
||||||
if (!message.getFromUserId().equals(requestUserId)) {
|
if (!message.getFromUserId().equals(requestUserId)) {
|
||||||
throw new BusinessException(403, "只能撤回自己发送的消息");
|
throw new BusinessException(403, "只能撤回自己发送的消息");
|
||||||
}
|
}
|
||||||
int recallMinutes = featureConfigClient.messageRecallMinutes(appId);
|
int recallMinutes = featureConfigClient.messageRecallMinutes(appKey);
|
||||||
if (recallMinutes > 0 && message.getCreatedAt().plusMinutes(recallMinutes).isBefore(LocalDateTime.now())) {
|
if (recallMinutes > 0 && message.getCreatedAt().plusMinutes(recallMinutes).isBefore(LocalDateTime.now())) {
|
||||||
throw new BusinessException(403, "已超过可撤回时长");
|
throw new BusinessException(403, "已超过可撤回时长");
|
||||||
}
|
}
|
||||||
@ -216,14 +216,14 @@ public class MessageService {
|
|||||||
log.debug("revoke group messageId={} groupId={}", saved.getId(), saved.getToId());
|
log.debug("revoke group messageId={} groupId={}", saved.getId(), saved.getToId());
|
||||||
clusterPublisher.publish("/topic/group/" + saved.getToId(), saved);
|
clusterPublisher.publish("/topic/group/" + saved.getToId(), saved);
|
||||||
}
|
}
|
||||||
dispatchWebhooks(appId, "message.revoked", saved);
|
dispatchWebhooks(appKey, "message.revoked", saved);
|
||||||
return saved;
|
return saved;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImMessageEntity edit(String appId, String messageId, String requestUserId, EditMessageRequest req) {
|
public ImMessageEntity edit(String appKey, String messageId, String requestUserId, EditMessageRequest req) {
|
||||||
ImMessageEntity message = messageRepository.findById(messageId)
|
ImMessageEntity message = messageRepository.findById(messageId)
|
||||||
.orElseThrow(() -> new BusinessException(404, "消息不存在"));
|
.orElseThrow(() -> new BusinessException(404, "消息不存在"));
|
||||||
if (!message.getAppId().equals(appId)) {
|
if (!message.getAppId().equals(appKey)) {
|
||||||
throw new BusinessException(403, "无权操作");
|
throw new BusinessException(403, "无权操作");
|
||||||
}
|
}
|
||||||
if (!message.getFromUserId().equals(requestUserId)) {
|
if (!message.getFromUserId().equals(requestUserId)) {
|
||||||
@ -236,7 +236,7 @@ public class MessageService {
|
|||||||
throw new BusinessException(400, "仅支持编辑文本消息");
|
throw new BusinessException(400, "仅支持编辑文本消息");
|
||||||
}
|
}
|
||||||
|
|
||||||
String content = keywordFilterService.filter(appId, req.content());
|
String content = keywordFilterService.filter(appKey, req.content());
|
||||||
if (content == null) {
|
if (content == null) {
|
||||||
throw new BusinessException("消息包含违禁内容");
|
throw new BusinessException("消息包含违禁内容");
|
||||||
}
|
}
|
||||||
@ -251,7 +251,7 @@ public class MessageService {
|
|||||||
clusterPublisher.publish("/user/" + saved.getFromUserId() + "/queue/messages", saved);
|
clusterPublisher.publish("/user/" + saved.getFromUserId() + "/queue/messages", saved);
|
||||||
}
|
}
|
||||||
imPushBridge.sendOfflinePushToUsers(
|
imPushBridge.sendOfflinePushToUsers(
|
||||||
appId,
|
appKey,
|
||||||
List.of(saved.getToId()),
|
List.of(saved.getToId()),
|
||||||
"消息已编辑",
|
"消息已编辑",
|
||||||
saved.getContent(),
|
saved.getContent(),
|
||||||
@ -261,7 +261,7 @@ public class MessageService {
|
|||||||
clusterPublisher.publish("/topic/group/" + saved.getToId(), saved);
|
clusterPublisher.publish("/topic/group/" + saved.getToId(), saved);
|
||||||
List<String> memberIds = groupService.memberIds(groupService.get(saved.getToId()));
|
List<String> memberIds = groupService.memberIds(groupService.get(saved.getToId()));
|
||||||
imPushBridge.sendOfflinePushToUsers(
|
imPushBridge.sendOfflinePushToUsers(
|
||||||
appId,
|
appKey,
|
||||||
memberIds.stream()
|
memberIds.stream()
|
||||||
.filter(memberId -> !memberId.equals(saved.getFromUserId()))
|
.filter(memberId -> !memberId.equals(saved.getFromUserId()))
|
||||||
.toList(),
|
.toList(),
|
||||||
@ -271,14 +271,14 @@ public class MessageService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatchWebhooks(appId, "message.edited", saved);
|
dispatchWebhooks(appKey, "message.edited", saved);
|
||||||
return saved;
|
return saved;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImMessageEntity adminRevoke(String appId, String messageId) {
|
public ImMessageEntity adminRevoke(String appKey, String messageId) {
|
||||||
ImMessageEntity message = messageRepository.findById(messageId)
|
ImMessageEntity message = messageRepository.findById(messageId)
|
||||||
.orElseThrow(() -> new BusinessException(404, "消息不存在"));
|
.orElseThrow(() -> new BusinessException(404, "消息不存在"));
|
||||||
if (!message.getAppId().equals(appId)) {
|
if (!message.getAppId().equals(appKey)) {
|
||||||
throw new BusinessException(403, "无权操作");
|
throw new BusinessException(403, "无权操作");
|
||||||
}
|
}
|
||||||
message.setStatus(ImMessageEntity.MsgStatus.REVOKED);
|
message.setStatus(ImMessageEntity.MsgStatus.REVOKED);
|
||||||
@ -295,12 +295,12 @@ public class MessageService {
|
|||||||
log.debug("admin revoke group messageId={} groupId={}", saved.getId(), saved.getToId());
|
log.debug("admin revoke group messageId={} groupId={}", saved.getId(), saved.getToId());
|
||||||
clusterPublisher.publish("/topic/group/" + saved.getToId(), saved);
|
clusterPublisher.publish("/topic/group/" + saved.getToId(), saved);
|
||||||
}
|
}
|
||||||
dispatchWebhooks(appId, "message.revoked", saved);
|
dispatchWebhooks(appKey, "message.revoked", saved);
|
||||||
return saved;
|
return saved;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Page<ImMessageEntity> history(
|
public Page<ImMessageEntity> history(
|
||||||
String appId,
|
String appKey,
|
||||||
String userId,
|
String userId,
|
||||||
String toId,
|
String toId,
|
||||||
ImMessageEntity.MsgType msgType,
|
ImMessageEntity.MsgType msgType,
|
||||||
@ -309,13 +309,13 @@ public class MessageService {
|
|||||||
LocalDateTime endTime,
|
LocalDateTime endTime,
|
||||||
int page,
|
int page,
|
||||||
int size) {
|
int size) {
|
||||||
LocalDateTime effectiveStart = applyHistoryRetention(appId, startTime);
|
LocalDateTime effectiveStart = applyHistoryRetention(appKey, startTime);
|
||||||
return messageRepository.findSingleConversationFiltered(
|
return messageRepository.findSingleConversationFiltered(
|
||||||
appId, userId, toId, msgType, keyword, effectiveStart, endTime, PageRequest.of(page, size));
|
appKey, userId, toId, msgType, keyword, effectiveStart, endTime, PageRequest.of(page, size));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Page<ImMessageEntity> groupHistory(
|
public Page<ImMessageEntity> groupHistory(
|
||||||
String appId,
|
String appKey,
|
||||||
String groupId,
|
String groupId,
|
||||||
String userId,
|
String userId,
|
||||||
ImMessageEntity.MsgType msgType,
|
ImMessageEntity.MsgType msgType,
|
||||||
@ -324,25 +324,25 @@ public class MessageService {
|
|||||||
LocalDateTime endTime,
|
LocalDateTime endTime,
|
||||||
int page,
|
int page,
|
||||||
int size) {
|
int size) {
|
||||||
LocalDateTime effectiveStart = applyHistoryRetention(appId, startTime);
|
LocalDateTime effectiveStart = applyHistoryRetention(appKey, startTime);
|
||||||
ImGroupEntity group = groupService.get(groupId);
|
ImGroupEntity group = groupService.get(groupId);
|
||||||
if (!groupService.memberIds(group).contains(userId)) {
|
if (!groupService.memberIds(group).contains(userId)) {
|
||||||
throw new BusinessException(403, "不在群内");
|
throw new BusinessException(403, "不在群内");
|
||||||
}
|
}
|
||||||
Page<ImMessageEntity> pageResult = messageRepository.findGroupHistoryFiltered(
|
Page<ImMessageEntity> pageResult = messageRepository.findGroupHistoryFiltered(
|
||||||
appId, groupId, msgType, keyword, effectiveStart, endTime, PageRequest.of(page, size));
|
appKey, groupId, msgType, keyword, effectiveStart, endTime, PageRequest.of(page, size));
|
||||||
pageResult.forEach(message -> message.setGroupReadCount(
|
pageResult.forEach(message -> message.setGroupReadCount(
|
||||||
groupReadCount(appId, groupId, message.getCreatedAt(), message.getFromUserId())));
|
groupReadCount(appKey, groupId, message.getCreatedAt(), message.getFromUserId())));
|
||||||
return pageResult;
|
return pageResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void syncReadReceipt(String appId, String readerId, String peerId, String chatType, LocalDateTime readAt) {
|
public void syncReadReceipt(String appKey, String readerId, String peerId, String chatType, LocalDateTime readAt) {
|
||||||
if (!ImMessageEntity.ChatType.SINGLE.name().equals(chatType) || readerId.equals(peerId)) {
|
if (!ImMessageEntity.ChatType.SINGLE.name().equals(chatType) || readerId.equals(peerId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<ImMessageEntity> messages = messageRepository
|
List<ImMessageEntity> messages = messageRepository
|
||||||
.findByAppIdAndFromUserIdAndToIdAndCreatedAtLessThanEqualOrderByCreatedAtAsc(
|
.findByAppIdAndFromUserIdAndToIdAndCreatedAtLessThanEqualOrderByCreatedAtAsc(
|
||||||
appId, peerId, readerId, readAt);
|
appKey, peerId, readerId, readAt);
|
||||||
if (messages.isEmpty()) {
|
if (messages.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -357,8 +357,8 @@ public class MessageService {
|
|||||||
clusterPublisher.publish("/user/" + peerId + "/queue/messages", saved);
|
clusterPublisher.publish("/user/" + peerId + "/queue/messages", saved);
|
||||||
messageIds.add(saved.getId());
|
messageIds.add(saved.getId());
|
||||||
}
|
}
|
||||||
dispatchWebhooks(appId, "message.read", new MessageReadCallbackPayload(
|
dispatchWebhooks(appKey, "message.read", new MessageReadCallbackPayload(
|
||||||
appId,
|
appKey,
|
||||||
readerId,
|
readerId,
|
||||||
peerId,
|
peerId,
|
||||||
null,
|
null,
|
||||||
@ -368,25 +368,25 @@ public class MessageService {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void syncGroupReadReceipt(String appId, String readerId, String groupId, LocalDateTime readAt) {
|
public void syncGroupReadReceipt(String appKey, String readerId, String groupId, LocalDateTime readAt) {
|
||||||
ImGroupEntity group = groupService.get(groupId);
|
ImGroupEntity group = groupService.get(groupId);
|
||||||
if (!groupService.memberIds(group).contains(readerId)) {
|
if (!groupService.memberIds(group).contains(readerId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<ImMessageEntity> messages = messageRepository
|
List<ImMessageEntity> messages = messageRepository
|
||||||
.findByAppIdAndToIdAndChatTypeAndCreatedAtLessThanEqualOrderByCreatedAtAsc(
|
.findByAppIdAndToIdAndChatTypeAndCreatedAtLessThanEqualOrderByCreatedAtAsc(
|
||||||
appId, groupId, ImMessageEntity.ChatType.GROUP, readAt);
|
appKey, groupId, ImMessageEntity.ChatType.GROUP, readAt);
|
||||||
if (messages.isEmpty()) {
|
if (messages.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<String> messageIds = new java.util.ArrayList<>();
|
List<String> messageIds = new java.util.ArrayList<>();
|
||||||
for (ImMessageEntity message : messages) {
|
for (ImMessageEntity message : messages) {
|
||||||
message.setGroupReadCount(groupReadCount(appId, groupId, message.getCreatedAt(), message.getFromUserId()));
|
message.setGroupReadCount(groupReadCount(appKey, groupId, message.getCreatedAt(), message.getFromUserId()));
|
||||||
clusterPublisher.publish("/topic/group/" + groupId, message);
|
clusterPublisher.publish("/topic/group/" + groupId, message);
|
||||||
messageIds.add(message.getId());
|
messageIds.add(message.getId());
|
||||||
}
|
}
|
||||||
dispatchWebhooks(appId, "message.read", new MessageReadCallbackPayload(
|
dispatchWebhooks(appKey, "message.read", new MessageReadCallbackPayload(
|
||||||
appId,
|
appKey,
|
||||||
readerId,
|
readerId,
|
||||||
null,
|
null,
|
||||||
groupId,
|
groupId,
|
||||||
@ -397,7 +397,7 @@ public class MessageService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Page<ImMessageEntity> adminHistory(
|
public Page<ImMessageEntity> adminHistory(
|
||||||
String appId,
|
String appKey,
|
||||||
String userA,
|
String userA,
|
||||||
String userB,
|
String userB,
|
||||||
ImMessageEntity.MsgType msgType,
|
ImMessageEntity.MsgType msgType,
|
||||||
@ -406,13 +406,13 @@ public class MessageService {
|
|||||||
LocalDateTime endTime,
|
LocalDateTime endTime,
|
||||||
int page,
|
int page,
|
||||||
int size) {
|
int size) {
|
||||||
LocalDateTime effectiveStart = applyHistoryRetention(appId, startTime);
|
LocalDateTime effectiveStart = applyHistoryRetention(appKey, startTime);
|
||||||
return messageRepository.findSingleConversationFiltered(
|
return messageRepository.findSingleConversationFiltered(
|
||||||
appId, userA, userB, msgType, keyword, effectiveStart, endTime, PageRequest.of(page, size));
|
appKey, userA, userB, msgType, keyword, effectiveStart, endTime, PageRequest.of(page, size));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Page<ImMessageEntity> adminGroupHistory(
|
public Page<ImMessageEntity> adminGroupHistory(
|
||||||
String appId,
|
String appKey,
|
||||||
String groupId,
|
String groupId,
|
||||||
ImMessageEntity.MsgType msgType,
|
ImMessageEntity.MsgType msgType,
|
||||||
String keyword,
|
String keyword,
|
||||||
@ -420,30 +420,30 @@ public class MessageService {
|
|||||||
LocalDateTime endTime,
|
LocalDateTime endTime,
|
||||||
int page,
|
int page,
|
||||||
int size) {
|
int size) {
|
||||||
LocalDateTime effectiveStart = applyHistoryRetention(appId, startTime);
|
LocalDateTime effectiveStart = applyHistoryRetention(appKey, startTime);
|
||||||
Page<ImMessageEntity> pageResult = messageRepository.findGroupHistoryFiltered(
|
Page<ImMessageEntity> pageResult = messageRepository.findGroupHistoryFiltered(
|
||||||
appId, groupId, msgType, keyword, effectiveStart, endTime, PageRequest.of(page, size));
|
appKey, groupId, msgType, keyword, effectiveStart, endTime, PageRequest.of(page, size));
|
||||||
pageResult.forEach(message -> message.setGroupReadCount(
|
pageResult.forEach(message -> message.setGroupReadCount(
|
||||||
groupReadCount(appId, groupId, message.getCreatedAt(), message.getFromUserId())));
|
groupReadCount(appKey, groupId, message.getCreatedAt(), message.getFromUserId())));
|
||||||
return pageResult;
|
return pageResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImMessageRepository.ConversationSummary> conversations(String appId, String userId, int size) {
|
public List<ImMessageRepository.ConversationSummary> conversations(String appKey, String userId, int size) {
|
||||||
return messageRepository.findConversations(appId, userId, normalizeConversationSize(appId, size));
|
return messageRepository.findConversations(appKey, userId, normalizeConversationSize(appKey, size));
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ConversationView> conversationViews(String appId, String userId, int size) {
|
public List<ConversationView> conversationViews(String appKey, String userId, int size) {
|
||||||
int cappedSize = normalizeConversationSize(appId, size);
|
int cappedSize = normalizeConversationSize(appKey, size);
|
||||||
int fetchSize = Math.max(cappedSize * 3, cappedSize);
|
int fetchSize = Math.max(cappedSize * 3, cappedSize);
|
||||||
return messageRepository.findConversations(appId, userId, fetchSize).stream()
|
return messageRepository.findConversations(appKey, userId, fetchSize).stream()
|
||||||
.map(summary -> toConversationView(appId, userId, summary))
|
.map(summary -> toConversationView(appKey, userId, summary))
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.limit(cappedSize)
|
.limit(cappedSize)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private LocalDateTime applyHistoryRetention(String appId, LocalDateTime requestedStart) {
|
private LocalDateTime applyHistoryRetention(String appKey, LocalDateTime requestedStart) {
|
||||||
int retentionDays = featureConfigClient.historyRetentionDays(appId);
|
int retentionDays = featureConfigClient.historyRetentionDays(appKey);
|
||||||
LocalDateTime retentionStart = LocalDateTime.now().minusDays(retentionDays);
|
LocalDateTime retentionStart = LocalDateTime.now().minusDays(retentionDays);
|
||||||
if (requestedStart == null || requestedStart.isBefore(retentionStart)) {
|
if (requestedStart == null || requestedStart.isBefore(retentionStart)) {
|
||||||
return retentionStart;
|
return retentionStart;
|
||||||
@ -451,31 +451,31 @@ public class MessageService {
|
|||||||
return requestedStart;
|
return requestedStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int normalizeConversationSize(String appId, int requestedSize) {
|
private int normalizeConversationSize(String appKey, int requestedSize) {
|
||||||
int limit = featureConfigClient.conversationPullLimit(appId);
|
int limit = featureConfigClient.conversationPullLimit(appKey);
|
||||||
int safeRequested = Math.max(requestedSize, 1);
|
int safeRequested = Math.max(requestedSize, 1);
|
||||||
return Math.min(safeRequested, limit);
|
return Math.min(safeRequested, limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConversationView toConversationView(
|
private ConversationView toConversationView(
|
||||||
String appId,
|
String appKey,
|
||||||
String userId,
|
String userId,
|
||||||
ImMessageRepository.ConversationSummary summary
|
ImMessageRepository.ConversationSummary summary
|
||||||
) {
|
) {
|
||||||
String targetId = summary.getTargetId();
|
String targetId = summary.getTargetId();
|
||||||
String chatType = summary.getChatType();
|
String chatType = summary.getChatType();
|
||||||
var state = conversationStateService.find(appId, userId, targetId, chatType);
|
var state = conversationStateService.find(appKey, userId, targetId, chatType);
|
||||||
if (state != null && state.isHidden()) {
|
if (state != null && state.isHidden()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Page<ImMessageEntity> page = chatType.equals("GROUP")
|
Page<ImMessageEntity> page = chatType.equals("GROUP")
|
||||||
? messageRepository.findGroupHistory(appId, targetId, PageRequest.of(0, 1))
|
? messageRepository.findGroupHistory(appKey, targetId, PageRequest.of(0, 1))
|
||||||
: messageRepository.findSingleConversation(appId, userId, targetId, PageRequest.of(0, 1));
|
: messageRepository.findSingleConversation(appKey, userId, targetId, PageRequest.of(0, 1));
|
||||||
ImMessageEntity lastMessage = page.getContent().stream().findFirst().orElse(null);
|
ImMessageEntity lastMessage = page.getContent().stream().findFirst().orElse(null);
|
||||||
LocalDateTime lastReadAt = state == null ? null : state.getLastReadAt();
|
LocalDateTime lastReadAt = state == null ? null : state.getLastReadAt();
|
||||||
long unreadCount = chatType.equals("GROUP")
|
long unreadCount = chatType.equals("GROUP")
|
||||||
? messageRepository.countUnreadGroupConversation(appId, userId, targetId, lastReadAt)
|
? messageRepository.countUnreadGroupConversation(appKey, userId, targetId, lastReadAt)
|
||||||
: messageRepository.countUnreadSingleConversation(appId, userId, targetId, lastReadAt);
|
: messageRepository.countUnreadSingleConversation(appKey, userId, targetId, lastReadAt);
|
||||||
return new ConversationView(
|
return new ConversationView(
|
||||||
targetId,
|
targetId,
|
||||||
chatType,
|
chatType,
|
||||||
@ -489,18 +489,18 @@ public class MessageService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<GroupReadReceiptSummary> groupReadReceipts(String appId, String groupId, List<String> messageIds) {
|
public List<GroupReadReceiptSummary> groupReadReceipts(String appKey, String groupId, List<String> messageIds) {
|
||||||
ImGroupEntity group = groupService.get(groupId);
|
ImGroupEntity group = groupService.get(groupId);
|
||||||
if (!group.getAppId().equals(appId)) {
|
if (!group.getAppId().equals(appKey)) {
|
||||||
throw new BusinessException(403, "无权操作");
|
throw new BusinessException(403, "无权操作");
|
||||||
}
|
}
|
||||||
List<String> members = groupService.memberIds(group);
|
List<String> members = groupService.memberIds(group);
|
||||||
return messageRepository.findAllById(messageIds == null ? List.of() : messageIds).stream()
|
return messageRepository.findAllById(messageIds == null ? List.of() : messageIds).stream()
|
||||||
.filter(message -> appId.equals(message.getAppId()))
|
.filter(message -> appKey.equals(message.getAppId()))
|
||||||
.filter(message -> groupId.equals(message.getToId()))
|
.filter(message -> groupId.equals(message.getToId()))
|
||||||
.filter(message -> message.getChatType() == ImMessageEntity.ChatType.GROUP)
|
.filter(message -> message.getChatType() == ImMessageEntity.ChatType.GROUP)
|
||||||
.map(message -> {
|
.map(message -> {
|
||||||
int readCount = groupReadCount(appId, groupId, message.getCreatedAt(), message.getFromUserId());
|
int readCount = groupReadCount(appKey, groupId, message.getCreatedAt(), message.getFromUserId());
|
||||||
return new GroupReadReceiptSummary(
|
return new GroupReadReceiptSummary(
|
||||||
message.getId(),
|
message.getId(),
|
||||||
groupId,
|
groupId,
|
||||||
@ -520,7 +520,7 @@ public class MessageService {
|
|||||||
try {
|
try {
|
||||||
Map<String, Object> payload = new java.util.LinkedHashMap<>();
|
Map<String, Object> payload = new java.util.LinkedHashMap<>();
|
||||||
payload.put("messageId", message.getId());
|
payload.put("messageId", message.getId());
|
||||||
payload.put("appId", message.getAppId());
|
payload.put("appKey", message.getAppId());
|
||||||
payload.put("fromUserId", message.getFromUserId());
|
payload.put("fromUserId", message.getFromUserId());
|
||||||
payload.put("toId", message.getToId());
|
payload.put("toId", message.getToId());
|
||||||
payload.put("chatType", message.getChatType().name());
|
payload.put("chatType", message.getChatType().name());
|
||||||
@ -557,7 +557,7 @@ public class MessageService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private int groupReadCount(String appId, String groupId, LocalDateTime createdAt, String senderId) {
|
private int groupReadCount(String appKey, String groupId, LocalDateTime createdAt, String senderId) {
|
||||||
ImGroupEntity group = groupService.get(groupId);
|
ImGroupEntity group = groupService.get(groupId);
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (String memberId : groupService.memberIds(group)) {
|
for (String memberId : groupService.memberIds(group)) {
|
||||||
@ -565,7 +565,7 @@ public class MessageService {
|
|||||||
count += 1;
|
count += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var state = conversationStateService.find(appId, memberId, groupId, ImMessageEntity.ChatType.GROUP.name());
|
var state = conversationStateService.find(appKey, memberId, groupId, ImMessageEntity.ChatType.GROUP.name());
|
||||||
if (state != null && state.getLastReadAt() != null && !state.getLastReadAt().isBefore(createdAt)) {
|
if (state != null && state.getLastReadAt() != null && !state.getLastReadAt().isBefore(createdAt)) {
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
@ -587,18 +587,18 @@ public class MessageService {
|
|||||||
return java.util.Optional.empty();
|
return java.util.Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void dispatchWebhooks(String appId, String callbackEvent, ImMessageEntity message) {
|
protected void dispatchWebhooks(String appKey, String callbackEvent, ImMessageEntity message) {
|
||||||
webhookDispatchService.dispatch(appId, "message", callbackEvent, message);
|
webhookDispatchService.dispatch(appKey, "message", callbackEvent, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void dispatchWebhooks(String appId, String callbackEvent, Object payload) {
|
protected void dispatchWebhooks(String appKey, String callbackEvent, Object payload) {
|
||||||
webhookDispatchService.dispatch(appId, "message", callbackEvent, payload);
|
webhookDispatchService.dispatch(appKey, "message", callbackEvent, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImMessageEntity adminSend(String appId, String fromUserId, String toId, ImMessageEntity.MsgType msgType, String content) {
|
public ImMessageEntity adminSend(String appKey, String fromUserId, String toId, ImMessageEntity.MsgType msgType, String content) {
|
||||||
ImMessageEntity message = new ImMessageEntity();
|
ImMessageEntity message = new ImMessageEntity();
|
||||||
message.setId(UUID.randomUUID().toString());
|
message.setId(UUID.randomUUID().toString());
|
||||||
message.setAppId(appId);
|
message.setAppId(appKey);
|
||||||
message.setFromUserId(fromUserId);
|
message.setFromUserId(fromUserId);
|
||||||
message.setToId(toId);
|
message.setToId(toId);
|
||||||
message.setChatType(ImMessageEntity.ChatType.SINGLE);
|
message.setChatType(ImMessageEntity.ChatType.SINGLE);
|
||||||
@ -610,28 +610,28 @@ public class MessageService {
|
|||||||
|
|
||||||
clusterPublisher.publish("/user/" + fromUserId + "/queue/messages", saved);
|
clusterPublisher.publish("/user/" + fromUserId + "/queue/messages", saved);
|
||||||
clusterPublisher.publish("/user/" + toId + "/queue/messages", saved);
|
clusterPublisher.publish("/user/" + toId + "/queue/messages", saved);
|
||||||
conversationStateService.clearHiddenForUsers(appId, toId, ImMessageEntity.ChatType.SINGLE.name(), List.of(fromUserId, toId));
|
conversationStateService.clearHiddenForUsers(appKey, toId, ImMessageEntity.ChatType.SINGLE.name(), List.of(fromUserId, toId));
|
||||||
imPushBridge.sendOfflinePushToUsers(
|
imPushBridge.sendOfflinePushToUsers(
|
||||||
appId,
|
appKey,
|
||||||
List.of(toId),
|
List.of(toId),
|
||||||
"新消息",
|
"新消息",
|
||||||
saved.getContent(),
|
saved.getContent(),
|
||||||
buildPushPayload(saved)
|
buildPushPayload(saved)
|
||||||
);
|
);
|
||||||
|
|
||||||
dispatchWebhooks(appId, "message.sent", saved);
|
dispatchWebhooks(appKey, "message.sent", saved);
|
||||||
return saved;
|
return saved;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void adminSetMsgRead(String appId, String userId) {
|
public void adminSetMsgRead(String appKey, String userId) {
|
||||||
List<ImMessageEntity> messages = messageRepository.findUnreadByAppIdAndToId(appId, userId);
|
List<ImMessageEntity> messages = messageRepository.findUnreadByAppIdAndToId(appKey, userId);
|
||||||
for (ImMessageEntity message : messages) {
|
for (ImMessageEntity message : messages) {
|
||||||
message.setStatus(ImMessageEntity.MsgStatus.READ);
|
message.setStatus(ImMessageEntity.MsgStatus.READ);
|
||||||
messageRepository.save(message);
|
messageRepository.save(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImMessageEntity> importMessages(String appId, List<ImportMessageRequest> requests) {
|
public List<ImMessageEntity> importMessages(String appKey, List<ImportMessageRequest> requests) {
|
||||||
List<ImMessageEntity> result = new ArrayList<>();
|
List<ImMessageEntity> result = new ArrayList<>();
|
||||||
for (ImportMessageRequest req : requests == null ? List.<ImportMessageRequest>of() : requests) {
|
for (ImportMessageRequest req : requests == null ? List.<ImportMessageRequest>of() : requests) {
|
||||||
if (req == null || req.fromUserId() == null || req.fromUserId().isBlank()
|
if (req == null || req.fromUserId() == null || req.fromUserId().isBlank()
|
||||||
@ -642,7 +642,7 @@ public class MessageService {
|
|||||||
message.setId(req.messageId() != null && !req.messageId().isBlank()
|
message.setId(req.messageId() != null && !req.messageId().isBlank()
|
||||||
? req.messageId()
|
? req.messageId()
|
||||||
: UUID.randomUUID().toString());
|
: UUID.randomUUID().toString());
|
||||||
message.setAppId(appId);
|
message.setAppId(appKey);
|
||||||
message.setFromUserId(req.fromUserId());
|
message.setFromUserId(req.fromUserId());
|
||||||
message.setToId(req.toId());
|
message.setToId(req.toId());
|
||||||
message.setChatType(req.chatType() != null ? req.chatType() : ImMessageEntity.ChatType.SINGLE);
|
message.setChatType(req.chatType() != null ? req.chatType() : ImMessageEntity.ChatType.SINGLE);
|
||||||
|
|||||||
@ -33,22 +33,22 @@ public class OfflineMessageSyncService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void storeOfflineMessage(String appId, String userId, String messageId) {
|
public void storeOfflineMessage(String appKey, String userId, String messageId) {
|
||||||
ImOfflineMessageEntity entity = new ImOfflineMessageEntity();
|
ImOfflineMessageEntity entity = new ImOfflineMessageEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appId);
|
entity.setAppId(appKey);
|
||||||
entity.setUserId(userId);
|
entity.setUserId(userId);
|
||||||
entity.setMessageId(messageId);
|
entity.setMessageId(messageId);
|
||||||
entity.setDelivered(false);
|
entity.setDelivered(false);
|
||||||
entity.setCreatedAt(LocalDateTime.now());
|
entity.setCreatedAt(LocalDateTime.now());
|
||||||
offlineMessageRepository.save(entity);
|
offlineMessageRepository.save(entity);
|
||||||
log.debug("Stored offline message appId={} userId={} messageId={}", appId, userId, messageId);
|
log.debug("Stored offline message appKey={} userId={} messageId={}", appKey, userId, messageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void syncAndDeliver(String appId, String userId) {
|
public void syncAndDeliver(String appKey, String userId) {
|
||||||
List<ImOfflineMessageEntity> offlineMessages = offlineMessageRepository
|
List<ImOfflineMessageEntity> offlineMessages = offlineMessageRepository
|
||||||
.findByAppIdAndUserIdAndDeliveredFalse(appId, userId);
|
.findByAppIdAndUserIdAndDeliveredFalse(appKey, userId);
|
||||||
if (offlineMessages.isEmpty()) {
|
if (offlineMessages.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -59,26 +59,26 @@ public class OfflineMessageSyncService {
|
|||||||
if (message != null) {
|
if (message != null) {
|
||||||
clusterPublisher.publish("/user/" + userId + "/queue/messages", message);
|
clusterPublisher.publish("/user/" + userId + "/queue/messages", message);
|
||||||
deliveredIds.add(offline.getId());
|
deliveredIds.add(offline.getId());
|
||||||
log.debug("Delivered offline message appId={} userId={} messageId={}", appId, userId, message.getId());
|
log.debug("Delivered offline message appKey={} userId={} messageId={}", appKey, userId, message.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!deliveredIds.isEmpty()) {
|
if (!deliveredIds.isEmpty()) {
|
||||||
offlineMessageRepository.markDeliveredByIds(deliveredIds);
|
offlineMessageRepository.markDeliveredByIds(deliveredIds);
|
||||||
log.info("Synced {} offline messages for appId={} userId={}", deliveredIds.size(), appId, userId);
|
log.info("Synced {} offline messages for appKey={} userId={}", deliveredIds.size(), appKey, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
offlineMessageRepository.deleteByAppIdAndUserIdAndDeliveredTrue(appId, userId);
|
offlineMessageRepository.deleteByAppIdAndUserIdAndDeliveredTrue(appKey, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long countUndelivered(String appId, String userId) {
|
public long countUndelivered(String appKey, String userId) {
|
||||||
return offlineMessageRepository.countUndeliveredByAppIdAndUserId(appId, userId);
|
return offlineMessageRepository.countUndeliveredByAppIdAndUserId(appKey, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public List<ImMessageEntity> syncAndReturn(String appId, String userId) {
|
public List<ImMessageEntity> syncAndReturn(String appKey, String userId) {
|
||||||
List<ImOfflineMessageEntity> offlineMessages = offlineMessageRepository
|
List<ImOfflineMessageEntity> offlineMessages = offlineMessageRepository
|
||||||
.findByAppIdAndUserIdAndDeliveredFalse(appId, userId);
|
.findByAppIdAndUserIdAndDeliveredFalse(appKey, userId);
|
||||||
List<ImMessageEntity> result = new ArrayList<>();
|
List<ImMessageEntity> result = new ArrayList<>();
|
||||||
List<String> deliveredIds = new ArrayList<>();
|
List<String> deliveredIds = new ArrayList<>();
|
||||||
for (ImOfflineMessageEntity offline : offlineMessages) {
|
for (ImOfflineMessageEntity offline : offlineMessages) {
|
||||||
@ -91,7 +91,7 @@ public class OfflineMessageSyncService {
|
|||||||
if (!deliveredIds.isEmpty()) {
|
if (!deliveredIds.isEmpty()) {
|
||||||
offlineMessageRepository.markDeliveredByIds(deliveredIds);
|
offlineMessageRepository.markDeliveredByIds(deliveredIds);
|
||||||
}
|
}
|
||||||
offlineMessageRepository.deleteByAppIdAndUserIdAndDeliveredTrue(appId, userId);
|
offlineMessageRepository.deleteByAppIdAndUserIdAndDeliveredTrue(appKey, userId);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,7 +19,7 @@ public class OperationLogService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ImOperationLogEntity record(
|
public ImOperationLogEntity record(
|
||||||
String appId,
|
String appKey,
|
||||||
String operatorId,
|
String operatorId,
|
||||||
String action,
|
String action,
|
||||||
String resourceType,
|
String resourceType,
|
||||||
@ -27,7 +27,7 @@ public class OperationLogService {
|
|||||||
String detail) {
|
String detail) {
|
||||||
ImOperationLogEntity entity = new ImOperationLogEntity();
|
ImOperationLogEntity entity = new ImOperationLogEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appId);
|
entity.setAppId(appKey);
|
||||||
entity.setOperatorId(operatorId == null || operatorId.isBlank() ? "system" : operatorId);
|
entity.setOperatorId(operatorId == null || operatorId.isBlank() ? "system" : operatorId);
|
||||||
entity.setAction(action);
|
entity.setAction(action);
|
||||||
entity.setResourceType(resourceType);
|
entity.setResourceType(resourceType);
|
||||||
@ -37,7 +37,7 @@ public class OperationLogService {
|
|||||||
return repository.save(entity);
|
return repository.save(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Page<ImOperationLogEntity> list(String appId, Pageable pageable) {
|
public Page<ImOperationLogEntity> list(String appKey, Pageable pageable) {
|
||||||
return repository.findByAppIdOrderByCreatedAtDesc(appId, pageable);
|
return repository.findByAppIdOrderByCreatedAtDesc(appKey, pageable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,19 +18,19 @@ public class WebhookConfigService {
|
|||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<WebhookConfigEntity> list(String appId) {
|
public List<WebhookConfigEntity> list(String appKey) {
|
||||||
return repository.findByAppId(appId);
|
return repository.findByAppId(appKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public WebhookConfigEntity get(String appId, String id) {
|
public WebhookConfigEntity get(String appKey, String id) {
|
||||||
return repository.findByIdAndAppId(id, appId)
|
return repository.findByIdAndAppId(id, appKey)
|
||||||
.orElseThrow(() -> new BusinessException(404, "回调配置不存在"));
|
.orElseThrow(() -> new BusinessException(404, "回调配置不存在"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public WebhookConfigEntity create(String appId, String url, String secret, Boolean enabled) {
|
public WebhookConfigEntity create(String appKey, String url, String secret, Boolean enabled) {
|
||||||
WebhookConfigEntity entity = new WebhookConfigEntity();
|
WebhookConfigEntity entity = new WebhookConfigEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appId);
|
entity.setAppId(appKey);
|
||||||
entity.setUrl(url);
|
entity.setUrl(url);
|
||||||
entity.setSecret(secret);
|
entity.setSecret(secret);
|
||||||
entity.setEnabled(enabled == null || enabled);
|
entity.setEnabled(enabled == null || enabled);
|
||||||
@ -38,8 +38,8 @@ public class WebhookConfigService {
|
|||||||
return repository.save(entity);
|
return repository.save(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public WebhookConfigEntity update(String appId, String id, String url, String secret, Boolean enabled) {
|
public WebhookConfigEntity update(String appKey, String id, String url, String secret, Boolean enabled) {
|
||||||
WebhookConfigEntity entity = repository.findByIdAndAppId(id, appId)
|
WebhookConfigEntity entity = repository.findByIdAndAppId(id, appKey)
|
||||||
.orElseThrow(() -> new BusinessException(404, "回调配置不存在"));
|
.orElseThrow(() -> new BusinessException(404, "回调配置不存在"));
|
||||||
if (url != null) {
|
if (url != null) {
|
||||||
entity.setUrl(url);
|
entity.setUrl(url);
|
||||||
@ -53,8 +53,8 @@ public class WebhookConfigService {
|
|||||||
return repository.save(entity);
|
return repository.save(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void delete(String appId, String id) {
|
public void delete(String appKey, String id) {
|
||||||
WebhookConfigEntity entity = repository.findByIdAndAppId(id, appId)
|
WebhookConfigEntity entity = repository.findByIdAndAppId(id, appKey)
|
||||||
.orElseThrow(() -> new BusinessException(404, "回调配置不存在"));
|
.orElseThrow(() -> new BusinessException(404, "回调配置不存在"));
|
||||||
repository.delete(entity);
|
repository.delete(entity);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -60,13 +60,13 @@ public class WebhookDispatchService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Async
|
@Async
|
||||||
public void dispatch(String appId, String callbackType, String callbackEvent, Object payload) {
|
public void dispatch(String appKey, String callbackType, String callbackEvent, Object payload) {
|
||||||
List<WebhookConfigEntity> webhooks = webhookRepository.findByAppIdAndEnabledTrue(appId);
|
List<WebhookConfigEntity> webhooks = webhookRepository.findByAppIdAndEnabledTrue(appKey);
|
||||||
if (webhooks.isEmpty()) {
|
if (webhooks.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
String appSecret = appSecretClient.getAppSecret(appId);
|
String appSecret = appSecretClient.getAppSecret(appKey);
|
||||||
long requestTime = System.currentTimeMillis();
|
long requestTime = System.currentTimeMillis();
|
||||||
String nonce = UUID.randomUUID().toString().replace("-", "");
|
String nonce = UUID.randomUUID().toString().replace("-", "");
|
||||||
String callbackId = UUID.randomUUID().toString();
|
String callbackId = UUID.randomUUID().toString();
|
||||||
@ -77,20 +77,20 @@ public class WebhookDispatchService {
|
|||||||
requestTime,
|
requestTime,
|
||||||
objectMapper.valueToTree(payload),
|
objectMapper.valueToTree(payload),
|
||||||
null,
|
null,
|
||||||
appId
|
appKey
|
||||||
);
|
);
|
||||||
String body = objectMapper.writeValueAsString(envelope);
|
String body = objectMapper.writeValueAsString(envelope);
|
||||||
String signature = signWebhook(appId, appSecret, requestTime, nonce, body);
|
String signature = signWebhook(appKey, appSecret, requestTime, nonce, body);
|
||||||
|
|
||||||
for (WebhookConfigEntity webhook : webhooks) {
|
for (WebhookConfigEntity webhook : webhooks) {
|
||||||
deliverWithRetry(appId, callbackId, callbackEvent, webhook, body, signature, requestTime, nonce);
|
deliverWithRetry(appKey, callbackId, callbackEvent, webhook, body, signature, requestTime, nonce);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("Webhook dispatch prepare failed appId={} event={}: {}", appId, callbackEvent, e.getMessage());
|
log.warn("Webhook dispatch prepare failed appKey={} event={}: {}", appKey, callbackEvent, e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deliverWithRetry(String appId, String callbackId, String callbackEvent,
|
private void deliverWithRetry(String appKey, String callbackId, String callbackEvent,
|
||||||
WebhookConfigEntity webhook, String body, String signature,
|
WebhookConfigEntity webhook, String body, String signature,
|
||||||
long requestTime, String nonce) {
|
long requestTime, String nonce) {
|
||||||
HttpClient client = HttpClient.newBuilder()
|
HttpClient client = HttpClient.newBuilder()
|
||||||
@ -100,7 +100,7 @@ public class WebhookDispatchService {
|
|||||||
for (int attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
for (int attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
||||||
WebhookDeliveryEntity delivery = new WebhookDeliveryEntity();
|
WebhookDeliveryEntity delivery = new WebhookDeliveryEntity();
|
||||||
delivery.setId(UUID.randomUUID().toString());
|
delivery.setId(UUID.randomUUID().toString());
|
||||||
delivery.setAppId(appId);
|
delivery.setAppId(appKey);
|
||||||
delivery.setCallbackId(callbackId);
|
delivery.setCallbackId(callbackId);
|
||||||
delivery.setCallbackEvent(callbackEvent);
|
delivery.setCallbackEvent(callbackEvent);
|
||||||
delivery.setUrl(webhook.getUrl());
|
delivery.setUrl(webhook.getUrl());
|
||||||
@ -112,7 +112,7 @@ public class WebhookDispatchService {
|
|||||||
.uri(URI.create(webhook.getUrl()))
|
.uri(URI.create(webhook.getUrl()))
|
||||||
.timeout(Duration.ofMillis(webhookTimeoutMs))
|
.timeout(Duration.ofMillis(webhookTimeoutMs))
|
||||||
.header("Content-Type", "application/json")
|
.header("Content-Type", "application/json")
|
||||||
.header("X-App-Id", appId)
|
.header("X-App-Id", appKey)
|
||||||
.header("X-App-Timestamp", String.valueOf(requestTime))
|
.header("X-App-Timestamp", String.valueOf(requestTime))
|
||||||
.header("X-App-Nonce", nonce)
|
.header("X-App-Nonce", nonce)
|
||||||
.header("X-App-Signature", signature)
|
.header("X-App-Signature", signature)
|
||||||
@ -131,28 +131,28 @@ public class WebhookDispatchService {
|
|||||||
webhook.setLastFailureAt(null);
|
webhook.setLastFailureAt(null);
|
||||||
webhookRepository.save(webhook);
|
webhookRepository.save(webhook);
|
||||||
}
|
}
|
||||||
log.info("Webhook delivered appId={} event={} url={} attempt={} status={}",
|
log.info("Webhook delivered appKey={} event={} url={} attempt={} status={}",
|
||||||
appId, callbackEvent, webhook.getUrl(), attempt, response.statusCode());
|
appKey, callbackEvent, webhook.getUrl(), attempt, response.statusCode());
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
delivery.setSuccess(false);
|
delivery.setSuccess(false);
|
||||||
delivery.setErrorMessage("HTTP " + response.statusCode());
|
delivery.setErrorMessage("HTTP " + response.statusCode());
|
||||||
deliveryRepository.save(delivery);
|
deliveryRepository.save(delivery);
|
||||||
log.warn("Webhook returned non-2xx appId={} event={} url={} attempt={} status={}",
|
log.warn("Webhook returned non-2xx appKey={} event={} url={} attempt={} status={}",
|
||||||
appId, callbackEvent, webhook.getUrl(), attempt, response.statusCode());
|
appKey, callbackEvent, webhook.getUrl(), attempt, response.statusCode());
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
delivery.setSuccess(false);
|
delivery.setSuccess(false);
|
||||||
delivery.setErrorMessage(truncate(e.getMessage(), 4000));
|
delivery.setErrorMessage(truncate(e.getMessage(), 4000));
|
||||||
deliveryRepository.save(delivery);
|
deliveryRepository.save(delivery);
|
||||||
log.warn("Webhook delivery failed appId={} event={} url={} attempt={}: {}",
|
log.warn("Webhook delivery failed appKey={} event={} url={} attempt={}: {}",
|
||||||
appId, callbackEvent, webhook.getUrl(), attempt, e.getMessage());
|
appKey, callbackEvent, webhook.getUrl(), attempt, e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attempt < MAX_RETRIES) {
|
if (attempt < MAX_RETRIES) {
|
||||||
long delay = RETRY_DELAYS_MS[attempt - 1];
|
long delay = RETRY_DELAYS_MS[attempt - 1];
|
||||||
log.info("Webhook retry scheduled appId={} event={} url={} delayMs={}",
|
log.info("Webhook retry scheduled appKey={} event={} url={} delayMs={}",
|
||||||
appId, callbackEvent, webhook.getUrl(), delay);
|
appKey, callbackEvent, webhook.getUrl(), delay);
|
||||||
try {
|
try {
|
||||||
Thread.sleep(delay);
|
Thread.sleep(delay);
|
||||||
} catch (InterruptedException ie) {
|
} catch (InterruptedException ie) {
|
||||||
@ -160,29 +160,29 @@ public class WebhookDispatchService {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
handleMaxRetriesExceeded(appId, callbackEvent, webhook);
|
handleMaxRetriesExceeded(appKey, callbackEvent, webhook);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleMaxRetriesExceeded(String appId, String callbackEvent, WebhookConfigEntity webhook) {
|
private void handleMaxRetriesExceeded(String appKey, String callbackEvent, WebhookConfigEntity webhook) {
|
||||||
int failures = webhook.getConsecutiveFailures() + 1;
|
int failures = webhook.getConsecutiveFailures() + 1;
|
||||||
webhook.setConsecutiveFailures(failures);
|
webhook.setConsecutiveFailures(failures);
|
||||||
webhook.setLastFailureAt(LocalDateTime.now());
|
webhook.setLastFailureAt(LocalDateTime.now());
|
||||||
webhookRepository.save(webhook);
|
webhookRepository.save(webhook);
|
||||||
|
|
||||||
log.error("Webhook max retries exceeded appId={} event={} url={} consecutiveFailures={}",
|
log.error("Webhook max retries exceeded appKey={} event={} url={} consecutiveFailures={}",
|
||||||
appId, callbackEvent, webhook.getUrl(), failures);
|
appKey, callbackEvent, webhook.getUrl(), failures);
|
||||||
|
|
||||||
if (failures >= ALERT_THRESHOLD && webhook.isEnabled()) {
|
if (failures >= ALERT_THRESHOLD && webhook.isEnabled()) {
|
||||||
webhook.setEnabled(false);
|
webhook.setEnabled(false);
|
||||||
webhookRepository.save(webhook);
|
webhookRepository.save(webhook);
|
||||||
log.warn("Webhook auto-disabled after {} consecutive failures appId={} url={}",
|
log.warn("Webhook auto-disabled after {} consecutive failures appKey={} url={}",
|
||||||
ALERT_THRESHOLD, appId, webhook.getUrl());
|
ALERT_THRESHOLD, appKey, webhook.getUrl());
|
||||||
|
|
||||||
WebhookAlertEntity alert = new WebhookAlertEntity();
|
WebhookAlertEntity alert = new WebhookAlertEntity();
|
||||||
alert.setId(UUID.randomUUID().toString());
|
alert.setId(UUID.randomUUID().toString());
|
||||||
alert.setAppId(appId);
|
alert.setAppId(appKey);
|
||||||
alert.setWebhookId(webhook.getId());
|
alert.setWebhookId(webhook.getId());
|
||||||
alert.setWebhookUrl(webhook.getUrl());
|
alert.setWebhookUrl(webhook.getUrl());
|
||||||
alert.setAlertType("AUTO_DISABLED");
|
alert.setAlertType("AUTO_DISABLED");
|
||||||
@ -193,8 +193,8 @@ public class WebhookDispatchService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String signWebhook(String appId, String appSecret, long requestTime, String nonce, String body) {
|
private String signWebhook(String appKey, String appSecret, long requestTime, String nonce, String body) {
|
||||||
String payload = appId + "\n" + requestTime + "\n" + nonce + "\n" + sha256Hex(body);
|
String payload = appKey + "\n" + requestTime + "\n" + nonce + "\n" + sha256Hex(body);
|
||||||
return hmacSha256Hex(appSecret, payload);
|
return hmacSha256Hex(appSecret, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -34,13 +34,13 @@ public class ChatController {
|
|||||||
request.messageId(), request.toId(), request.chatType(), request.msgType(),
|
request.messageId(), request.toId(), request.chatType(), request.msgType(),
|
||||||
request.content(), request.mentionedUserIds()
|
request.content(), request.mentionedUserIds()
|
||||||
);
|
);
|
||||||
messageService.send(request.appId(), userId, req);
|
messageService.send(request.appKey(), userId, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
@MessageMapping("/chat.revoke")
|
@MessageMapping("/chat.revoke")
|
||||||
public void revoke(@Payload WsRevokeRequest request, Principal principal) {
|
public void revoke(@Payload WsRevokeRequest request, Principal principal) {
|
||||||
if (principal == null) return;
|
if (principal == null) return;
|
||||||
messageService.revoke(request.appId(), request.messageId(), principal.getName());
|
messageService.revoke(request.appKey(), request.messageId(), principal.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@MessageMapping("/chat.sync")
|
@MessageMapping("/chat.sync")
|
||||||
@ -48,17 +48,17 @@ public class ChatController {
|
|||||||
if (principal == null) return;
|
if (principal == null) return;
|
||||||
String userId = principal.getName();
|
String userId = principal.getName();
|
||||||
userPresenceService.heartbeat(userId);
|
userPresenceService.heartbeat(userId);
|
||||||
offlineMessageSyncService.syncAndDeliver(request.appId(), userId);
|
offlineMessageSyncService.syncAndDeliver(request.appKey(), userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public record WsMessageRequest(
|
public record WsMessageRequest(
|
||||||
String appId, String messageId, String toId,
|
String appKey, String messageId, String toId,
|
||||||
ImMessageEntity.ChatType chatType,
|
ImMessageEntity.ChatType chatType,
|
||||||
ImMessageEntity.MsgType msgType,
|
ImMessageEntity.MsgType msgType,
|
||||||
String content, String mentionedUserIds
|
String content, String mentionedUserIds
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public record WsRevokeRequest(String appId, String messageId) {}
|
public record WsRevokeRequest(String appKey, String messageId) {}
|
||||||
|
|
||||||
public record WsSyncRequest(String appId) {}
|
public record WsSyncRequest(String appKey) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,17 +41,17 @@ public class ImSessionKickListener {
|
|||||||
String userId = auth.getName();
|
String userId = auth.getName();
|
||||||
String sessionId = accessor.getSessionId();
|
String sessionId = accessor.getSessionId();
|
||||||
|
|
||||||
String appId = null;
|
String appKey = null;
|
||||||
Object details = auth.getDetails();
|
Object details = auth.getDetails();
|
||||||
if (details instanceof Map<?, ?> detailsMap) {
|
if (details instanceof Map<?, ?> detailsMap) {
|
||||||
Object v = detailsMap.get("appId");
|
Object v = detailsMap.get("appKey");
|
||||||
if (v != null) appId = v.toString();
|
if (v != null) appKey = v.toString();
|
||||||
}
|
}
|
||||||
if (appId == null || appId.isBlank()) {
|
if (appKey == null || appKey.isBlank()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String mode = featureConfigClient.multiDeviceLoginMode(appId);
|
String mode = featureConfigClient.multiDeviceLoginMode(appKey);
|
||||||
if ("MULTI_DEVICE_FREE".equals(mode)) {
|
if ("MULTI_DEVICE_FREE".equals(mode)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -61,7 +61,7 @@ public class ImSessionKickListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("Sending kick to userId={} appId={} mode={} newSessionId={}", userId, appId, mode, sessionId);
|
log.info("Sending kick to userId={} appKey={} mode={} newSessionId={}", userId, appKey, mode, sessionId);
|
||||||
Map<String, String> payload = Map.of("type", "KICKED", "sessionId", sessionId, "reason", mode.toLowerCase());
|
Map<String, String> payload = Map.of("type", "KICKED", "sessionId", sessionId, "reason", mode.toLowerCase());
|
||||||
messagingTemplate.convertAndSendToUser(userId, "/queue/system", payload);
|
messagingTemplate.convertAndSendToUser(userId, "/queue/system", payload);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,7 +44,7 @@ jwt:
|
|||||||
expiration: 0
|
expiration: 0
|
||||||
|
|
||||||
im:
|
im:
|
||||||
tenant-service-url: ${TENANT_SERVICE_URL:http://127.0.0.1:8081}
|
tenant-service-url: ${TENANT_SERVICE_URL:http://127.0.0.1:9001}
|
||||||
internal-token: ${SDK_INTERNAL_TOKEN:xuqm-internal-token}
|
internal-token: ${SDK_INTERNAL_TOKEN:xuqm-internal-token}
|
||||||
push-service-url: ${PUSH_SERVICE_URL:http://127.0.0.1:8083}
|
push-service-url: ${PUSH_SERVICE_URL:http://127.0.0.1:8083}
|
||||||
multi-login: true
|
multi-login: true
|
||||||
|
|||||||
@ -39,7 +39,7 @@ public class InternalPushController {
|
|||||||
if (token == null || !internalToken.equals(token)) {
|
if (token == null || !internalToken.equals(token)) {
|
||||||
return ResponseEntity.status(403).body(ApiResponse.error(403, "Forbidden"));
|
return ResponseEntity.status(403).body(ApiResponse.error(403, "Forbidden"));
|
||||||
}
|
}
|
||||||
pushDispatcher.pushToUsers(request.appId(), request.userIds(), request.title(), request.body(), request.payload());
|
pushDispatcher.pushToUsers(request.appKey(), request.userIds(), request.title(), request.body(), request.payload());
|
||||||
return ResponseEntity.ok(ApiResponse.ok());
|
return ResponseEntity.ok(ApiResponse.ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,24 +47,24 @@ public class InternalPushController {
|
|||||||
public ResponseEntity<ApiResponse<PushDiagnosticsService.PushTokenDiagnostics>> searchByToken(
|
public ResponseEntity<ApiResponse<PushDiagnosticsService.PushTokenDiagnostics>> searchByToken(
|
||||||
@RequestHeader(value = "X-Internal-Token", required = false) String token,
|
@RequestHeader(value = "X-Internal-Token", required = false) String token,
|
||||||
@org.springframework.web.bind.annotation.RequestParam String queryToken,
|
@org.springframework.web.bind.annotation.RequestParam String queryToken,
|
||||||
@org.springframework.web.bind.annotation.RequestParam(required = false) String appId) {
|
@org.springframework.web.bind.annotation.RequestParam(required = false) String appKey) {
|
||||||
if (!isAllowed(token)) {
|
if (!isAllowed(token)) {
|
||||||
return ResponseEntity.status(403).body(ApiResponse.error(403, "Forbidden"));
|
return ResponseEntity.status(403).body(ApiResponse.error(403, "Forbidden"));
|
||||||
}
|
}
|
||||||
return ResponseEntity.ok(ApiResponse.success(diagnosticsService.searchByToken(queryToken, appId)));
|
return ResponseEntity.ok(ApiResponse.success(diagnosticsService.searchByToken(queryToken, appKey)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/device-logs")
|
@GetMapping("/device-logs")
|
||||||
public ResponseEntity<ApiResponse<java.util.Map<String, Object>>> deviceLogs(
|
public ResponseEntity<ApiResponse<java.util.Map<String, Object>>> deviceLogs(
|
||||||
@RequestHeader(value = "X-Internal-Token", required = false) String token,
|
@RequestHeader(value = "X-Internal-Token", required = false) String token,
|
||||||
@org.springframework.web.bind.annotation.RequestParam String appId,
|
@org.springframework.web.bind.annotation.RequestParam String appKey,
|
||||||
@org.springframework.web.bind.annotation.RequestParam String userId,
|
@org.springframework.web.bind.annotation.RequestParam String userId,
|
||||||
@org.springframework.web.bind.annotation.RequestParam(defaultValue = "0") int page,
|
@org.springframework.web.bind.annotation.RequestParam(defaultValue = "0") int page,
|
||||||
@org.springframework.web.bind.annotation.RequestParam(defaultValue = "20") int size) {
|
@org.springframework.web.bind.annotation.RequestParam(defaultValue = "20") int size) {
|
||||||
if (!isAllowed(token)) {
|
if (!isAllowed(token)) {
|
||||||
return ResponseEntity.status(403).body(ApiResponse.error(403, "Forbidden"));
|
return ResponseEntity.status(403).body(ApiResponse.error(403, "Forbidden"));
|
||||||
}
|
}
|
||||||
Page<DeviceLoginLogEntity> result = diagnosticsService.deviceLogs(appId, userId, page, size);
|
Page<DeviceLoginLogEntity> result = diagnosticsService.deviceLogs(appKey, userId, page, size);
|
||||||
return ResponseEntity.ok(ApiResponse.success(java.util.Map.of(
|
return ResponseEntity.ok(ApiResponse.success(java.util.Map.of(
|
||||||
"content", result.getContent(),
|
"content", result.getContent(),
|
||||||
"total", result.getTotalElements(),
|
"total", result.getTotalElements(),
|
||||||
@ -80,7 +80,7 @@ public class InternalPushController {
|
|||||||
return ResponseEntity.status(403).body(ApiResponse.error(403, "Forbidden"));
|
return ResponseEntity.status(403).body(ApiResponse.error(403, "Forbidden"));
|
||||||
}
|
}
|
||||||
PushDiagnosticsService.TestPushResult result = diagnosticsService.sendTestOfflineMessage(
|
PushDiagnosticsService.TestPushResult result = diagnosticsService.sendTestOfflineMessage(
|
||||||
request.appId(),
|
request.appKey(),
|
||||||
request.userId(),
|
request.userId(),
|
||||||
request.title(),
|
request.title(),
|
||||||
request.body(),
|
request.body(),
|
||||||
@ -93,7 +93,7 @@ public class InternalPushController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public record NotifyRequest(
|
public record NotifyRequest(
|
||||||
@NotBlank String appId,
|
@NotBlank String appKey,
|
||||||
List<@NotBlank String> userIds,
|
List<@NotBlank String> userIds,
|
||||||
@NotBlank String title,
|
@NotBlank String title,
|
||||||
@NotBlank String body,
|
@NotBlank String body,
|
||||||
@ -101,7 +101,7 @@ public class InternalPushController {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
public record TestOfflineRequest(
|
public record TestOfflineRequest(
|
||||||
@NotBlank String appId,
|
@NotBlank String appKey,
|
||||||
@NotBlank String userId,
|
@NotBlank String userId,
|
||||||
@NotBlank String title,
|
@NotBlank String title,
|
||||||
@NotBlank String body,
|
@NotBlank String body,
|
||||||
|
|||||||
@ -24,7 +24,7 @@ public class PushController {
|
|||||||
|
|
||||||
@PostMapping("/register")
|
@PostMapping("/register")
|
||||||
public ResponseEntity<ApiResponse<Void>> register(
|
public ResponseEntity<ApiResponse<Void>> register(
|
||||||
@RequestParam @NotBlank String appId,
|
@RequestParam @NotBlank String appKey,
|
||||||
@RequestParam @NotBlank String userId,
|
@RequestParam @NotBlank String userId,
|
||||||
@RequestParam @NotNull DeviceTokenEntity.Vendor vendor,
|
@RequestParam @NotNull DeviceTokenEntity.Vendor vendor,
|
||||||
@RequestParam @NotBlank String token,
|
@RequestParam @NotBlank String token,
|
||||||
@ -34,38 +34,38 @@ public class PushController {
|
|||||||
@RequestParam(required = false) String model,
|
@RequestParam(required = false) String model,
|
||||||
@RequestParam(required = false) String osVersion,
|
@RequestParam(required = false) String osVersion,
|
||||||
@RequestParam(required = false) String appVersion) {
|
@RequestParam(required = false) String appVersion) {
|
||||||
pushDispatcher.registerToken(appId, userId, vendor, token, platform, deviceId, brand, model, osVersion, appVersion);
|
pushDispatcher.registerToken(appKey, userId, vendor, token, platform, deviceId, brand, model, osVersion, appVersion);
|
||||||
return ResponseEntity.ok(ApiResponse.ok());
|
return ResponseEntity.ok(ApiResponse.ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/receive-push")
|
@PostMapping("/receive-push")
|
||||||
public ResponseEntity<ApiResponse<Void>> receivePush(
|
public ResponseEntity<ApiResponse<Void>> receivePush(
|
||||||
@RequestParam @NotBlank String appId,
|
@RequestParam @NotBlank String appKey,
|
||||||
@RequestParam @NotBlank String userId,
|
@RequestParam @NotBlank String userId,
|
||||||
@RequestParam(required = false) String deviceId,
|
@RequestParam(required = false) String deviceId,
|
||||||
@RequestParam boolean enabled) {
|
@RequestParam boolean enabled) {
|
||||||
pushDispatcher.setReceivePush(appId, userId, deviceId, enabled);
|
pushDispatcher.setReceivePush(appKey, userId, deviceId, enabled);
|
||||||
return ResponseEntity.ok(ApiResponse.ok());
|
return ResponseEntity.ok(ApiResponse.ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/send")
|
@PostMapping("/send")
|
||||||
public ResponseEntity<ApiResponse<Void>> send(
|
public ResponseEntity<ApiResponse<Void>> send(
|
||||||
@RequestParam @NotBlank String appId,
|
@RequestParam @NotBlank String appKey,
|
||||||
@RequestParam @NotBlank String userId,
|
@RequestParam @NotBlank String userId,
|
||||||
@RequestParam @NotBlank String title,
|
@RequestParam @NotBlank String title,
|
||||||
@RequestParam @NotBlank String body,
|
@RequestParam @NotBlank String body,
|
||||||
@RequestParam(required = false) String payload) {
|
@RequestParam(required = false) String payload) {
|
||||||
pushDispatcher.pushToUser(appId, userId, title, body, payload);
|
pushDispatcher.pushToUser(appKey, userId, title, body, payload);
|
||||||
return ResponseEntity.ok(ApiResponse.ok());
|
return ResponseEntity.ok(ApiResponse.ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/unregister")
|
@DeleteMapping("/unregister")
|
||||||
public ResponseEntity<ApiResponse<Void>> unregister(
|
public ResponseEntity<ApiResponse<Void>> unregister(
|
||||||
@RequestParam @NotBlank String appId,
|
@RequestParam @NotBlank String appKey,
|
||||||
@RequestParam @NotBlank String userId,
|
@RequestParam @NotBlank String userId,
|
||||||
@RequestParam @NotNull DeviceTokenEntity.Vendor vendor,
|
@RequestParam @NotNull DeviceTokenEntity.Vendor vendor,
|
||||||
@RequestParam(required = false) String deviceId) {
|
@RequestParam(required = false) String deviceId) {
|
||||||
pushDispatcher.unregisterToken(appId, userId, vendor, deviceId);
|
pushDispatcher.unregisterToken(appKey, userId, vendor, deviceId);
|
||||||
return ResponseEntity.ok(ApiResponse.ok());
|
return ResponseEntity.ok(ApiResponse.ok());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,18 +26,18 @@ public class PushManagementController {
|
|||||||
|
|
||||||
@GetMapping("/user-status")
|
@GetMapping("/user-status")
|
||||||
public ResponseEntity<ApiResponse<PushDiagnosticsService.PushTokenDiagnostics>> userStatus(
|
public ResponseEntity<ApiResponse<PushDiagnosticsService.PushTokenDiagnostics>> userStatus(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam String userId) {
|
@RequestParam String userId) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(diagnosticsService.searchByUserId(appId, userId)));
|
return ResponseEntity.ok(ApiResponse.success(diagnosticsService.searchByUserId(appKey, userId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/device-logs")
|
@GetMapping("/device-logs")
|
||||||
public ResponseEntity<ApiResponse<Map<String, Object>>> deviceLogs(
|
public ResponseEntity<ApiResponse<Map<String, Object>>> deviceLogs(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam String userId,
|
@RequestParam String userId,
|
||||||
@RequestParam(defaultValue = "0") int page,
|
@RequestParam(defaultValue = "0") int page,
|
||||||
@RequestParam(defaultValue = "20") int size) {
|
@RequestParam(defaultValue = "20") int size) {
|
||||||
Page<DeviceLoginLogEntity> result = diagnosticsService.deviceLogs(appId, userId, page, size);
|
Page<DeviceLoginLogEntity> result = diagnosticsService.deviceLogs(appKey, userId, page, size);
|
||||||
return ResponseEntity.ok(ApiResponse.success(Map.of(
|
return ResponseEntity.ok(ApiResponse.success(Map.of(
|
||||||
"content", result.getContent(),
|
"content", result.getContent(),
|
||||||
"total", result.getTotalElements(),
|
"total", result.getTotalElements(),
|
||||||
@ -49,7 +49,7 @@ public class PushManagementController {
|
|||||||
public ResponseEntity<ApiResponse<PushDiagnosticsService.TestPushResult>> testOffline(
|
public ResponseEntity<ApiResponse<PushDiagnosticsService.TestPushResult>> testOffline(
|
||||||
@RequestBody TestOfflineRequest request) {
|
@RequestBody TestOfflineRequest request) {
|
||||||
PushDiagnosticsService.TestPushResult result = diagnosticsService.sendTestOfflineMessage(
|
PushDiagnosticsService.TestPushResult result = diagnosticsService.sendTestOfflineMessage(
|
||||||
request.appId(),
|
request.appKey(),
|
||||||
request.userId(),
|
request.userId(),
|
||||||
request.title(),
|
request.title(),
|
||||||
request.body(),
|
request.body(),
|
||||||
@ -58,7 +58,7 @@ public class PushManagementController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public record TestOfflineRequest(
|
public record TestOfflineRequest(
|
||||||
String appId,
|
String appKey,
|
||||||
String userId,
|
String userId,
|
||||||
String title,
|
String title,
|
||||||
String body,
|
String body,
|
||||||
|
|||||||
@ -45,12 +45,12 @@ public class ImPresenceClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<PresenceStatus> userStatus(String appId, String userId) {
|
public Optional<PresenceStatus> userStatus(String appKey, String userId) {
|
||||||
try {
|
try {
|
||||||
HttpHeaders headers = internalHeaders();
|
HttpHeaders headers = internalHeaders();
|
||||||
HttpEntity<Void> request = new HttpEntity<>(headers);
|
HttpEntity<Void> request = new HttpEntity<>(headers);
|
||||||
String uri = UriComponentsBuilder.fromHttpUrl(imServiceBaseUrl + "/api/im/internal/presence/users/{userId}")
|
String uri = UriComponentsBuilder.fromHttpUrl(imServiceBaseUrl + "/api/im/internal/presence/users/{userId}")
|
||||||
.queryParam("appId", appId)
|
.queryParam("appKey", appKey)
|
||||||
.build(userId)
|
.build(userId)
|
||||||
.toString();
|
.toString();
|
||||||
String body = restTemplate.exchange(
|
String body = restTemplate.exchange(
|
||||||
@ -80,11 +80,11 @@ public class ImPresenceClient {
|
|||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
return Optional.of(new PresenceStatus(
|
return Optional.of(new PresenceStatus(
|
||||||
data.path("appId").asText(null),
|
data.path("appKey").asText(null),
|
||||||
data.path("userId").asText(null),
|
data.path("userId").asText(null),
|
||||||
data.path("online").asBoolean(false),
|
data.path("online").asBoolean(false),
|
||||||
data.path("lastSeenAt").asLong(0L)));
|
data.path("lastSeenAt").asLong(0L)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public record PresenceStatus(String appId, String userId, boolean online, long lastSeenAt) {}
|
public record PresenceStatus(String appKey, String userId, boolean online, long lastSeenAt) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,27 +37,27 @@ public class PushDiagnosticsService {
|
|||||||
|
|
||||||
public PushTokenDiagnostics searchByToken(String token, String appIdHint) {
|
public PushTokenDiagnostics searchByToken(String token, String appIdHint) {
|
||||||
Optional<DeviceTokenEntity> tokenMatch = tokenRepository.findFirstByToken(token);
|
Optional<DeviceTokenEntity> tokenMatch = tokenRepository.findFirstByToken(token);
|
||||||
String appId = appIdHint;
|
String appKey = appIdHint;
|
||||||
String userId = null;
|
String userId = null;
|
||||||
String tokenType = "UNKNOWN";
|
String tokenType = "UNKNOWN";
|
||||||
|
|
||||||
if (tokenMatch.isPresent()) {
|
if (tokenMatch.isPresent()) {
|
||||||
DeviceTokenEntity device = tokenMatch.get();
|
DeviceTokenEntity device = tokenMatch.get();
|
||||||
appId = device.getAppId();
|
appKey = device.getAppId();
|
||||||
userId = device.getUserId();
|
userId = device.getUserId();
|
||||||
tokenType = "PUSH";
|
tokenType = "PUSH";
|
||||||
} else {
|
} else {
|
||||||
Optional<ImPresenceClient.PresenceStatus> resolved = presenceClient.resolveToken(token);
|
Optional<ImPresenceClient.PresenceStatus> resolved = presenceClient.resolveToken(token);
|
||||||
if (resolved.isPresent()) {
|
if (resolved.isPresent()) {
|
||||||
appId = resolved.get().appId();
|
appKey = resolved.get().appKey();
|
||||||
userId = resolved.get().userId();
|
userId = resolved.get().userId();
|
||||||
tokenType = "IM";
|
tokenType = "IM";
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
Claims claims = jwtUtil.parse(token);
|
Claims claims = jwtUtil.parse(token);
|
||||||
userId = claims.getSubject();
|
userId = claims.getSubject();
|
||||||
String claimAppId = claims.get("appId", String.class);
|
String claimAppId = claims.get("appKey", String.class);
|
||||||
appId = claimAppId == null || claimAppId.isBlank() ? appIdHint : claimAppId;
|
appKey = claimAppId == null || claimAppId.isBlank() ? appIdHint : claimAppId;
|
||||||
tokenType = "IM";
|
tokenType = "IM";
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
// Keep UNKNOWN and return an empty diagnostic.
|
// Keep UNKNOWN and return an empty diagnostic.
|
||||||
@ -65,14 +65,14 @@ public class PushDiagnosticsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appId == null || appId.isBlank() || userId == null || userId.isBlank()) {
|
if (appKey == null || appKey.isBlank() || userId == null || userId.isBlank()) {
|
||||||
return new PushTokenDiagnostics(tokenType, appId, userId, false, 0L, false, null, List.of(), List.of());
|
return new PushTokenDiagnostics(tokenType, appKey, userId, false, 0L, false, null, List.of(), List.of());
|
||||||
}
|
}
|
||||||
|
|
||||||
ImPresenceClient.PresenceStatus presence = presenceClient.userStatus(appId, userId)
|
ImPresenceClient.PresenceStatus presence = presenceClient.userStatus(appKey, userId)
|
||||||
.orElse(new ImPresenceClient.PresenceStatus(appId, userId, false, 0L));
|
.orElse(new ImPresenceClient.PresenceStatus(appKey, userId, false, 0L));
|
||||||
List<DeviceTokenEntity> devices = tokenRepository.findByAppIdAndUserIdOrderByLastLoginAtDescUpdatedAtDesc(appId, userId);
|
List<DeviceTokenEntity> devices = tokenRepository.findByAppIdAndUserIdOrderByLastLoginAtDescUpdatedAtDesc(appKey, userId);
|
||||||
List<DeviceInfo> deliverableDevices = pushDispatcher.selectedPushTargets(appId, userId)
|
List<DeviceInfo> deliverableDevices = pushDispatcher.selectedPushTargets(appKey, userId)
|
||||||
.stream()
|
.stream()
|
||||||
.map(DeviceInfo::from)
|
.map(DeviceInfo::from)
|
||||||
.toList();
|
.toList();
|
||||||
@ -80,7 +80,7 @@ public class PushDiagnosticsService {
|
|||||||
boolean canSendOffline = !presence.online() && !deliverableDevices.isEmpty();
|
boolean canSendOffline = !presence.online() && !deliverableDevices.isEmpty();
|
||||||
return new PushTokenDiagnostics(
|
return new PushTokenDiagnostics(
|
||||||
tokenType,
|
tokenType,
|
||||||
appId,
|
appKey,
|
||||||
userId,
|
userId,
|
||||||
presence.online(),
|
presence.online(),
|
||||||
presence.lastSeenAt(),
|
presence.lastSeenAt(),
|
||||||
@ -90,11 +90,11 @@ public class PushDiagnosticsService {
|
|||||||
devices.stream().map(DeviceInfo::from).toList());
|
devices.stream().map(DeviceInfo::from).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public PushTokenDiagnostics searchByUserId(String appId, String userId) {
|
public PushTokenDiagnostics searchByUserId(String appKey, String userId) {
|
||||||
ImPresenceClient.PresenceStatus presence = presenceClient.userStatus(appId, userId)
|
ImPresenceClient.PresenceStatus presence = presenceClient.userStatus(appKey, userId)
|
||||||
.orElse(new ImPresenceClient.PresenceStatus(appId, userId, false, 0L));
|
.orElse(new ImPresenceClient.PresenceStatus(appKey, userId, false, 0L));
|
||||||
List<DeviceTokenEntity> devices = tokenRepository.findByAppIdAndUserIdOrderByLastLoginAtDescUpdatedAtDesc(appId, userId);
|
List<DeviceTokenEntity> devices = tokenRepository.findByAppIdAndUserIdOrderByLastLoginAtDescUpdatedAtDesc(appKey, userId);
|
||||||
List<DeviceInfo> deliverableDevices = pushDispatcher.selectedPushTargets(appId, userId)
|
List<DeviceInfo> deliverableDevices = pushDispatcher.selectedPushTargets(appKey, userId)
|
||||||
.stream()
|
.stream()
|
||||||
.map(DeviceInfo::from)
|
.map(DeviceInfo::from)
|
||||||
.toList();
|
.toList();
|
||||||
@ -102,7 +102,7 @@ public class PushDiagnosticsService {
|
|||||||
boolean canSendOffline = !presence.online() && !deliverableDevices.isEmpty();
|
boolean canSendOffline = !presence.online() && !deliverableDevices.isEmpty();
|
||||||
return new PushTokenDiagnostics(
|
return new PushTokenDiagnostics(
|
||||||
"USER",
|
"USER",
|
||||||
appId,
|
appKey,
|
||||||
userId,
|
userId,
|
||||||
presence.online(),
|
presence.online(),
|
||||||
presence.lastSeenAt(),
|
presence.lastSeenAt(),
|
||||||
@ -112,26 +112,26 @@ public class PushDiagnosticsService {
|
|||||||
devices.stream().map(DeviceInfo::from).toList());
|
devices.stream().map(DeviceInfo::from).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public TestPushResult sendTestOfflineMessage(String appId, String userId, String title, String body, String payload) {
|
public TestPushResult sendTestOfflineMessage(String appKey, String userId, String title, String body, String payload) {
|
||||||
List<DeviceInfo> targets = pushDispatcher.selectedPushTargets(appId, userId)
|
List<DeviceInfo> targets = pushDispatcher.selectedPushTargets(appKey, userId)
|
||||||
.stream()
|
.stream()
|
||||||
.map(DeviceInfo::from)
|
.map(DeviceInfo::from)
|
||||||
.toList();
|
.toList();
|
||||||
if (!targets.isEmpty()) {
|
if (!targets.isEmpty()) {
|
||||||
pushDispatcher.pushToUser(appId, userId, title, body, payload);
|
pushDispatcher.pushToUser(appKey, userId, title, body, payload);
|
||||||
}
|
}
|
||||||
return new TestPushResult(appId, userId, !targets.isEmpty(), targets.size(), targets);
|
return new TestPushResult(appKey, userId, !targets.isEmpty(), targets.size(), targets);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Page<DeviceLoginLogEntity> deviceLogs(String appId, String userId, int page, int size) {
|
public Page<DeviceLoginLogEntity> deviceLogs(String appKey, String userId, int page, int size) {
|
||||||
int safePage = Math.max(page, 0);
|
int safePage = Math.max(page, 0);
|
||||||
int safeSize = Math.min(Math.max(size, 1), 200);
|
int safeSize = Math.min(Math.max(size, 1), 200);
|
||||||
return logRepository.findByAppIdAndUserIdOrderByCreatedAtDesc(appId, userId, PageRequest.of(safePage, safeSize));
|
return logRepository.findByAppIdAndUserIdOrderByCreatedAtDesc(appKey, userId, PageRequest.of(safePage, safeSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
public record PushTokenDiagnostics(
|
public record PushTokenDiagnostics(
|
||||||
String tokenType,
|
String tokenType,
|
||||||
String appId,
|
String appKey,
|
||||||
String userId,
|
String userId,
|
||||||
boolean online,
|
boolean online,
|
||||||
long lastSeenAt,
|
long lastSeenAt,
|
||||||
@ -141,7 +141,7 @@ public class PushDiagnosticsService {
|
|||||||
List<DeviceInfo> devices) {}
|
List<DeviceInfo> devices) {}
|
||||||
|
|
||||||
public record TestPushResult(
|
public record TestPushResult(
|
||||||
String appId,
|
String appKey,
|
||||||
String userId,
|
String userId,
|
||||||
boolean sent,
|
boolean sent,
|
||||||
int targetCount,
|
int targetCount,
|
||||||
|
|||||||
@ -49,25 +49,25 @@ public class PushDispatcher {
|
|||||||
.collect(Collectors.toMap(PushProvider::vendorName, p -> p));
|
.collect(Collectors.toMap(PushProvider::vendorName, p -> p));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void pushToUser(String appId, String userId, String title, String body, String payload) {
|
public void pushToUser(String appKey, String userId, String title, String body, String payload) {
|
||||||
List<DeviceTokenEntity> targets = selectTargets(appId, userId);
|
List<DeviceTokenEntity> targets = selectTargets(appKey, userId);
|
||||||
if (targets.isEmpty()) {
|
if (targets.isEmpty()) {
|
||||||
log.info("Skip push to {}@{}: no receive-enabled device", userId, appId);
|
log.info("Skip push to {}@{}: no receive-enabled device", userId, appKey);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (DeviceTokenEntity t : targets) {
|
for (DeviceTokenEntity t : targets) {
|
||||||
PushProvider provider = providers.get(t.getVendor().name());
|
PushProvider provider = providers.get(t.getVendor().name());
|
||||||
if (provider != null) {
|
if (provider != null) {
|
||||||
boolean ok = provider.send(appId, t.getToken(), title, body, payload, resolveOptions(appId, payload, t.getVendor()));
|
boolean ok = provider.send(appKey, t.getToken(), title, body, payload, resolveOptions(appKey, payload, t.getVendor()));
|
||||||
log.info("Push to {}@{} via {} deviceId={}: {}", userId, appId, t.getVendor(), t.getDeviceId(), ok ? "OK" : "FAIL");
|
log.info("Push to {}@{} via {} deviceId={}: {}", userId, appKey, t.getVendor(), t.getDeviceId(), ok ? "OK" : "FAIL");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private PushSendOptions resolveOptions(String appId, String payload, DeviceTokenEntity.Vendor vendor) {
|
private PushSendOptions resolveOptions(String appKey, String payload, DeviceTokenEntity.Vendor vendor) {
|
||||||
String routeType = routeType(payload);
|
String routeType = routeType(payload);
|
||||||
String platform = platformForVendor(vendor, payload);
|
String platform = platformForVendor(vendor, payload);
|
||||||
return pushConfigClient.loadServiceConfig(appId, platform, "PUSH")
|
return pushConfigClient.loadServiceConfig(appKey, platform, "PUSH")
|
||||||
.map(config -> {
|
.map(config -> {
|
||||||
JsonNode route = config.path("routing").path(routeType);
|
JsonNode route = config.path("routing").path(routeType);
|
||||||
String channelKey = route.path("channel").asText("");
|
String channelKey = route.path("channel").asText("");
|
||||||
@ -135,26 +135,26 @@ public class PushDispatcher {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<DeviceTokenEntity> selectedPushTargets(String appId, String userId) {
|
public List<DeviceTokenEntity> selectedPushTargets(String appKey, String userId) {
|
||||||
return selectTargets(appId, userId);
|
return selectTargets(appKey, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void pushToUsers(String appId, List<String> userIds, String title, String body, String payload) {
|
public void pushToUsers(String appKey, List<String> userIds, String title, String body, String payload) {
|
||||||
if (userIds == null || userIds.isEmpty()) {
|
if (userIds == null || userIds.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (String userId : userIds) {
|
for (String userId : userIds) {
|
||||||
pushToUser(appId, userId, title, body, payload);
|
pushToUser(appKey, userId, title, body, payload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<DeviceTokenEntity> selectTargets(String appId, String userId) {
|
private List<DeviceTokenEntity> selectTargets(String appKey, String userId) {
|
||||||
List<DeviceTokenEntity> devices = tokenRepository
|
List<DeviceTokenEntity> devices = tokenRepository
|
||||||
.findByAppIdAndUserIdAndReceivePushTrueOrderByLastLoginAtDescUpdatedAtDesc(appId, userId);
|
.findByAppIdAndUserIdAndReceivePushTrueOrderByLastLoginAtDescUpdatedAtDesc(appKey, userId);
|
||||||
if (devices.isEmpty()) {
|
if (devices.isEmpty()) {
|
||||||
return List.of();
|
return List.of();
|
||||||
}
|
}
|
||||||
String mode = imConfigClient.multiDeviceLoginMode(appId);
|
String mode = imConfigClient.multiDeviceLoginMode(appKey);
|
||||||
return switch (mode) {
|
return switch (mode) {
|
||||||
case "SINGLE_DEVICE" -> List.of(devices.get(0));
|
case "SINGLE_DEVICE" -> List.of(devices.get(0));
|
||||||
case "SAME_PLATFORM_ONE" -> devices.stream()
|
case "SAME_PLATFORM_ONE" -> devices.stream()
|
||||||
@ -174,7 +174,7 @@ public class PushDispatcher {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerToken(String appId,
|
public void registerToken(String appKey,
|
||||||
String userId,
|
String userId,
|
||||||
DeviceTokenEntity.Vendor vendor,
|
DeviceTokenEntity.Vendor vendor,
|
||||||
String token,
|
String token,
|
||||||
@ -185,15 +185,15 @@ public class PushDispatcher {
|
|||||||
String osVersion,
|
String osVersion,
|
||||||
String appVersion) {
|
String appVersion) {
|
||||||
String resolvedDeviceId = normalizeDeviceId(deviceId, vendor, token);
|
String resolvedDeviceId = normalizeDeviceId(deviceId, vendor, token);
|
||||||
Optional<DeviceTokenEntity> existing = tokenRepository.findByAppIdAndUserIdAndDeviceId(appId, userId, resolvedDeviceId);
|
Optional<DeviceTokenEntity> existing = tokenRepository.findByAppIdAndUserIdAndDeviceId(appKey, userId, resolvedDeviceId);
|
||||||
if (existing.isEmpty() && (deviceId == null || deviceId.isBlank())) {
|
if (existing.isEmpty() && (deviceId == null || deviceId.isBlank())) {
|
||||||
existing = tokenRepository.findByAppIdAndUserIdAndVendor(appId, userId, vendor);
|
existing = tokenRepository.findByAppIdAndUserIdAndVendor(appKey, userId, vendor);
|
||||||
}
|
}
|
||||||
LocalDateTime now = LocalDateTime.now();
|
LocalDateTime now = LocalDateTime.now();
|
||||||
DeviceTokenEntity entity = existing.orElseGet(() -> {
|
DeviceTokenEntity entity = existing.orElseGet(() -> {
|
||||||
DeviceTokenEntity e = new DeviceTokenEntity();
|
DeviceTokenEntity e = new DeviceTokenEntity();
|
||||||
e.setId(UUID.randomUUID().toString());
|
e.setId(UUID.randomUUID().toString());
|
||||||
e.setAppId(appId);
|
e.setAppId(appKey);
|
||||||
e.setUserId(userId);
|
e.setUserId(userId);
|
||||||
e.setVendor(vendor);
|
e.setVendor(vendor);
|
||||||
e.setCreatedAt(now);
|
e.setCreatedAt(now);
|
||||||
@ -214,10 +214,10 @@ public class PushDispatcher {
|
|||||||
recordLog(saved, DeviceLoginLogEntity.EventType.REGISTER);
|
recordLog(saved, DeviceLoginLogEntity.EventType.REGISTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setReceivePush(String appId, String userId, String deviceId, boolean enabled) {
|
public void setReceivePush(String appKey, String userId, String deviceId, boolean enabled) {
|
||||||
List<DeviceTokenEntity> tokens = deviceId == null || deviceId.isBlank()
|
List<DeviceTokenEntity> tokens = deviceId == null || deviceId.isBlank()
|
||||||
? tokenRepository.findByAppIdAndUserId(appId, userId)
|
? tokenRepository.findByAppIdAndUserId(appKey, userId)
|
||||||
: tokenRepository.findByAppIdAndUserIdAndDeviceId(appId, userId, deviceId).stream().toList();
|
: tokenRepository.findByAppIdAndUserIdAndDeviceId(appKey, userId, deviceId).stream().toList();
|
||||||
for (DeviceTokenEntity token : tokens) {
|
for (DeviceTokenEntity token : tokens) {
|
||||||
token.setReceivePush(enabled);
|
token.setReceivePush(enabled);
|
||||||
token.setUpdatedAt(LocalDateTime.now());
|
token.setUpdatedAt(LocalDateTime.now());
|
||||||
@ -226,16 +226,16 @@ public class PushDispatcher {
|
|||||||
tokenRepository.saveAll(tokens);
|
tokenRepository.saveAll(tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unregisterToken(String appId, String userId, DeviceTokenEntity.Vendor vendor, String deviceId) {
|
public void unregisterToken(String appKey, String userId, DeviceTokenEntity.Vendor vendor, String deviceId) {
|
||||||
if (deviceId != null && !deviceId.isBlank()) {
|
if (deviceId != null && !deviceId.isBlank()) {
|
||||||
tokenRepository.findByAppIdAndUserIdAndDeviceId(appId, userId, deviceId)
|
tokenRepository.findByAppIdAndUserIdAndDeviceId(appKey, userId, deviceId)
|
||||||
.ifPresent(entity -> recordLog(entity, DeviceLoginLogEntity.EventType.UNREGISTER));
|
.ifPresent(entity -> recordLog(entity, DeviceLoginLogEntity.EventType.UNREGISTER));
|
||||||
tokenRepository.deleteByAppIdAndUserIdAndDeviceId(appId, userId, deviceId);
|
tokenRepository.deleteByAppIdAndUserIdAndDeviceId(appKey, userId, deviceId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
tokenRepository.findByAppIdAndUserIdAndVendor(appId, userId, vendor)
|
tokenRepository.findByAppIdAndUserIdAndVendor(appKey, userId, vendor)
|
||||||
.ifPresent(entity -> recordLog(entity, DeviceLoginLogEntity.EventType.UNREGISTER));
|
.ifPresent(entity -> recordLog(entity, DeviceLoginLogEntity.EventType.UNREGISTER));
|
||||||
tokenRepository.deleteByAppIdAndUserIdAndVendor(appId, userId, vendor);
|
tokenRepository.deleteByAppIdAndUserIdAndVendor(appKey, userId, vendor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void recordLog(DeviceTokenEntity token, DeviceLoginLogEntity.EventType eventType) {
|
private void recordLog(DeviceTokenEntity token, DeviceLoginLogEntity.EventType eventType) {
|
||||||
|
|||||||
@ -30,12 +30,12 @@ public class TenantImConfigClient {
|
|||||||
this.objectMapper = objectMapper;
|
this.objectMapper = objectMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String multiDeviceLoginMode(String appId) {
|
public String multiDeviceLoginMode(String appKey) {
|
||||||
try {
|
try {
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
headers.set("X-Internal-Token", internalToken);
|
headers.set("X-Internal-Token", internalToken);
|
||||||
ResponseEntity<JsonNode> response = restTemplate.exchange(
|
ResponseEntity<JsonNode> response = restTemplate.exchange(
|
||||||
tenantServiceBaseUrl + "/api/internal/sdk/apps/" + appId + "/services/ANDROID/IM",
|
tenantServiceBaseUrl + "/api/internal/sdk/apps/" + appKey + "/services/ANDROID/IM",
|
||||||
HttpMethod.GET,
|
HttpMethod.GET,
|
||||||
new HttpEntity<>(headers),
|
new HttpEntity<>(headers),
|
||||||
JsonNode.class);
|
JsonNode.class);
|
||||||
@ -55,12 +55,12 @@ public class TenantImConfigClient {
|
|||||||
return multi ? "MULTI_DEVICE_FREE" : "SINGLE_DEVICE";
|
return multi ? "MULTI_DEVICE_FREE" : "SINGLE_DEVICE";
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("load tenant im config failed appId={} reason={}", appId, e.getMessage());
|
log.warn("load tenant im config failed appKey={} reason={}", appKey, e.getMessage());
|
||||||
}
|
}
|
||||||
return "MULTI_DEVICE_FREE";
|
return "MULTI_DEVICE_FREE";
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean allowMultiDeviceLogin(String appId) {
|
public boolean allowMultiDeviceLogin(String appKey) {
|
||||||
return !"SINGLE_DEVICE".equals(multiDeviceLoginMode(appId));
|
return !"SINGLE_DEVICE".equals(multiDeviceLoginMode(appKey));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,13 +30,38 @@ public class TenantPushConfigClient {
|
|||||||
@Value("${push.internal-token:xuqm-internal-token}")
|
@Value("${push.internal-token:xuqm-internal-token}")
|
||||||
private String internalToken;
|
private String internalToken;
|
||||||
|
|
||||||
public Optional<JsonNode> loadServiceConfig(String appId, String platform, String serviceType) {
|
public Map<String, String> fetchPlatformInfo(String appKey) {
|
||||||
|
try {
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.set("X-Internal-Token", internalToken);
|
||||||
|
ResponseEntity<Map> resp = restTemplate.exchange(
|
||||||
|
tenantServiceBaseUrl + "/api/internal/sdk/apps/" + appKey + "/platform-info",
|
||||||
|
HttpMethod.GET,
|
||||||
|
new HttpEntity<>(null, headers),
|
||||||
|
Map.class
|
||||||
|
);
|
||||||
|
Map<?, ?> body = resp.getBody();
|
||||||
|
if (body == null) return Map.of();
|
||||||
|
Object data = body.get("data");
|
||||||
|
if (!(data instanceof Map<?, ?> dataMap)) return Map.of();
|
||||||
|
Map<String, String> result = new java.util.HashMap<>();
|
||||||
|
dataMap.forEach((k, v) -> {
|
||||||
|
if (k instanceof String && v instanceof String) result.put((String) k, (String) v);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("fetch platform info failed appKey={} reason={}", appKey, e.getMessage());
|
||||||
|
return Map.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<JsonNode> loadServiceConfig(String appKey, String platform, String serviceType) {
|
||||||
try {
|
try {
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
headers.set("X-Internal-Token", internalToken);
|
headers.set("X-Internal-Token", internalToken);
|
||||||
ResponseEntity<Map> resp = restTemplate.exchange(
|
ResponseEntity<Map> resp = restTemplate.exchange(
|
||||||
tenantServiceBaseUrl + "/api/internal/sdk/apps/" + appId + "/services/" + platform + "/" + serviceType,
|
tenantServiceBaseUrl + "/api/internal/sdk/apps/" + appKey + "/services/" + platform + "/" + serviceType,
|
||||||
HttpMethod.GET,
|
HttpMethod.GET,
|
||||||
new HttpEntity<>(null, headers),
|
new HttpEntity<>(null, headers),
|
||||||
Map.class
|
Map.class
|
||||||
@ -59,8 +84,8 @@ public class TenantPushConfigClient {
|
|||||||
}
|
}
|
||||||
return Optional.of(objectMapper.readTree(json));
|
return Optional.of(objectMapper.readTree(json));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("load tenant push config failed appId={} platform={} serviceType={} reason={}",
|
log.warn("load tenant push config failed appKey={} platform={} serviceType={} reason={}",
|
||||||
appId, platform, serviceType, e.getMessage());
|
appKey, platform, serviceType, e.getMessage());
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -63,16 +63,16 @@ public class ApnsPushProvider implements PushProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean send(String appId, String token, String title, String body, String payload) {
|
public boolean send(String appKey, String token, String title, String body, String payload) {
|
||||||
return send(appId, token, title, body, payload, PushSendOptions.empty());
|
return send(appKey, token, title, body, payload, PushSendOptions.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean send(String appId, String token, String title, String body, String payload, PushSendOptions options) {
|
public boolean send(String appKey, String token, String title, String body, String payload, PushSendOptions options) {
|
||||||
String teamId = resolveConfig(appId, "teamId", envTeamId);
|
String teamId = resolveConfig(appKey, "teamId", envTeamId);
|
||||||
String keyId = resolveConfig(appId, "keyId", envKeyId);
|
String keyId = resolveConfig(appKey, "keyId", envKeyId);
|
||||||
String bundleId = resolveConfig(appId, "bundleId", envBundleId);
|
String bundleId = resolveConfig(appKey, "bundleId", envBundleId);
|
||||||
String privateKeyPem = resolveConfig(appId, "privateKey", envPrivateKey);
|
String privateKeyPem = resolveConfig(appKey, "privateKey", envPrivateKey);
|
||||||
if (teamId.isBlank() || keyId.isBlank() || bundleId.isBlank() || privateKeyPem.isBlank()) {
|
if (teamId.isBlank() || keyId.isBlank() || bundleId.isBlank() || privateKeyPem.isBlank()) {
|
||||||
log.warn("APNS push not configured");
|
log.warn("APNS push not configured");
|
||||||
return false;
|
return false;
|
||||||
@ -151,8 +151,8 @@ public class ApnsPushProvider implements PushProvider {
|
|||||||
return kf.generatePrivate(spec);
|
return kf.generatePrivate(spec);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String resolveConfig(String appId, String key, String fallback) {
|
private String resolveConfig(String appKey, String key, String fallback) {
|
||||||
JsonNode config = configClient.loadServiceConfig(appId, "IOS", "PUSH")
|
JsonNode config = configClient.loadServiceConfig(appKey, "IOS", "PUSH")
|
||||||
.map(node -> node.path("apns"))
|
.map(node -> node.path("apns"))
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
|
|||||||
@ -56,14 +56,14 @@ public class FcmPushProvider implements PushProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean send(String appId, String token, String title, String body, String payload) {
|
public boolean send(String appKey, String token, String title, String body, String payload) {
|
||||||
return send(appId, token, title, body, payload, PushSendOptions.empty());
|
return send(appKey, token, title, body, payload, PushSendOptions.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean send(String appId, String token, String title, String body, String payload, PushSendOptions options) {
|
public boolean send(String appKey, String token, String title, String body, String payload, PushSendOptions options) {
|
||||||
String projectId = resolveConfig(appId, "projectId", envProjectId);
|
String projectId = resolveConfig(appKey, "projectId", envProjectId);
|
||||||
String serviceAccountJson = resolveConfig(appId, "serviceAccountJson", envServiceAccountJson);
|
String serviceAccountJson = resolveConfig(appKey, "serviceAccountJson", envServiceAccountJson);
|
||||||
if (projectId.isBlank() || serviceAccountJson.isBlank()) {
|
if (projectId.isBlank() || serviceAccountJson.isBlank()) {
|
||||||
log.warn("FCM push not configured");
|
log.warn("FCM push not configured");
|
||||||
return false;
|
return false;
|
||||||
@ -144,8 +144,8 @@ public class FcmPushProvider implements PushProvider {
|
|||||||
return kf.generatePrivate(spec);
|
return kf.generatePrivate(spec);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String resolveConfig(String appId, String key, String fallback) {
|
private String resolveConfig(String appKey, String key, String fallback) {
|
||||||
JsonNode config = configClient.loadServiceConfig(appId, "ANDROID", "PUSH")
|
JsonNode config = configClient.loadServiceConfig(appKey, "ANDROID", "PUSH")
|
||||||
.map(node -> node.path("fcm"))
|
.map(node -> node.path("fcm"))
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
|
|||||||
@ -53,14 +53,14 @@ public class HarmonyPushProvider implements PushProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean send(String appId, String token, String title, String body, String payload) {
|
public boolean send(String appKey, String token, String title, String body, String payload) {
|
||||||
return send(appId, token, title, body, payload, PushSendOptions.empty());
|
return send(appKey, token, title, body, payload, PushSendOptions.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean send(String appId, String token, String title, String body, String payload, PushSendOptions options) {
|
public boolean send(String appKey, String token, String title, String body, String payload, PushSendOptions options) {
|
||||||
String resolvedAppId = resolveConfig(appId, "appId", envAppId);
|
String resolvedAppId = resolveConfig(appKey, "appId", envAppId);
|
||||||
String resolvedAppSecret = resolveConfig(appId, "appSecret", envAppSecret);
|
String resolvedAppSecret = resolveConfig(appKey, "appSecret", envAppSecret);
|
||||||
if (resolvedAppId.isBlank() || resolvedAppSecret.isBlank()) {
|
if (resolvedAppId.isBlank() || resolvedAppSecret.isBlank()) {
|
||||||
log.warn("Harmony push not configured");
|
log.warn("Harmony push not configured");
|
||||||
return false;
|
return false;
|
||||||
@ -113,8 +113,8 @@ public class HarmonyPushProvider implements PushProvider {
|
|||||||
return (String) json.get("access_token");
|
return (String) json.get("access_token");
|
||||||
}
|
}
|
||||||
|
|
||||||
private String resolveConfig(String appId, String key, String fallback) {
|
private String resolveConfig(String appKey, String key, String fallback) {
|
||||||
JsonNode config = configClient.loadServiceConfig(appId, "HARMONY", "PUSH")
|
JsonNode config = configClient.loadServiceConfig(appKey, "HARMONY", "PUSH")
|
||||||
.map(node -> node.path("harmony"))
|
.map(node -> node.path("harmony"))
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
|
|||||||
@ -45,11 +45,11 @@ public class HonorPushProvider implements PushProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean send(String appId, String token, String title, String body, String payload) {
|
public boolean send(String appKey, String token, String title, String body, String payload) {
|
||||||
String resolvedAppId = resolveConfig(appId, "appId", envAppId);
|
String resolvedAppId = resolveConfig(appKey, "appId", envAppId);
|
||||||
String resolvedAppSecret = resolveConfig(appId, "clientSecret", envAppSecret);
|
String resolvedAppSecret = resolveConfig(appKey, "clientSecret", envAppSecret);
|
||||||
if (resolvedAppId.isBlank() || resolvedAppSecret.isBlank()) {
|
if (resolvedAppId.isBlank() || resolvedAppSecret.isBlank()) {
|
||||||
resolvedAppSecret = resolveConfig(appId, "appSecret", envAppSecret);
|
resolvedAppSecret = resolveConfig(appKey, "appSecret", envAppSecret);
|
||||||
}
|
}
|
||||||
if (resolvedAppId.isBlank() || resolvedAppSecret.isBlank()) {
|
if (resolvedAppId.isBlank() || resolvedAppSecret.isBlank()) {
|
||||||
log.warn("Honor push not configured");
|
log.warn("Honor push not configured");
|
||||||
@ -92,8 +92,8 @@ public class HonorPushProvider implements PushProvider {
|
|||||||
return (String) json.get("access_token");
|
return (String) json.get("access_token");
|
||||||
}
|
}
|
||||||
|
|
||||||
private String resolveConfig(String appId, String key, String fallback) {
|
private String resolveConfig(String appKey, String key, String fallback) {
|
||||||
JsonNode config = configClient.loadServiceConfig(appId, "ANDROID", "PUSH")
|
JsonNode config = configClient.loadServiceConfig(appKey, "ANDROID", "PUSH")
|
||||||
.map(node -> node.path("honor"))
|
.map(node -> node.path("honor"))
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
|
|||||||
@ -46,14 +46,14 @@ public class HuaweiPushProvider implements PushProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean send(String appId, String token, String title, String body, String payload) {
|
public boolean send(String appKey, String token, String title, String body, String payload) {
|
||||||
return send(appId, token, title, body, payload, PushSendOptions.empty());
|
return send(appKey, token, title, body, payload, PushSendOptions.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean send(String appId, String token, String title, String body, String payload, PushSendOptions options) {
|
public boolean send(String appKey, String token, String title, String body, String payload, PushSendOptions options) {
|
||||||
String resolvedAppId = resolveConfig(appId, "appId", envAppId);
|
String resolvedAppId = resolveConfig(appKey, "appId", envAppId);
|
||||||
String resolvedAppSecret = resolveConfig(appId, "appSecret", envAppSecret);
|
String resolvedAppSecret = resolveConfig(appKey, "appSecret", envAppSecret);
|
||||||
if (resolvedAppId.isBlank() || resolvedAppSecret.isBlank()) {
|
if (resolvedAppId.isBlank() || resolvedAppSecret.isBlank()) {
|
||||||
log.warn("Huawei push not configured");
|
log.warn("Huawei push not configured");
|
||||||
return false;
|
return false;
|
||||||
@ -65,8 +65,14 @@ public class HuaweiPushProvider implements PushProvider {
|
|||||||
bodyMap.put("token", new String[]{token});
|
bodyMap.put("token", new String[]{token});
|
||||||
bodyMap.put("notification", Map.of("title", title, "body", body));
|
bodyMap.put("notification", Map.of("title", title, "body", body));
|
||||||
bodyMap.put("data", payload != null ? payload : "{}");
|
bodyMap.put("data", payload != null ? payload : "{}");
|
||||||
if (options != null && options.channelId() != null && !options.channelId().isBlank()) {
|
Map<String, Object> androidNotification = new LinkedHashMap<>();
|
||||||
bodyMap.put("android", Map.of("notification", Map.of("channel_id", options.channelId())));
|
String channelId = options != null && options.channelId() != null && !options.channelId().isBlank()
|
||||||
|
? options.channelId() : "";
|
||||||
|
String category = resolveConfig(appKey, "category", "");
|
||||||
|
if (!channelId.isBlank()) androidNotification.put("channel_id", channelId);
|
||||||
|
if (!category.isBlank()) androidNotification.put("category", category);
|
||||||
|
if (!androidNotification.isEmpty()) {
|
||||||
|
bodyMap.put("android", Map.of("notification", androidNotification));
|
||||||
}
|
}
|
||||||
Map<String, Object> message = Map.of("message", bodyMap);
|
Map<String, Object> message = Map.of("message", bodyMap);
|
||||||
String requestBody = objectMapper.writeValueAsString(message);
|
String requestBody = objectMapper.writeValueAsString(message);
|
||||||
@ -96,8 +102,8 @@ public class HuaweiPushProvider implements PushProvider {
|
|||||||
return (String) json.get("access_token");
|
return (String) json.get("access_token");
|
||||||
}
|
}
|
||||||
|
|
||||||
private String resolveConfig(String appId, String key, String fallback) {
|
private String resolveConfig(String appKey, String key, String fallback) {
|
||||||
JsonNode config = configClient.loadServiceConfig(appId, "ANDROID", "PUSH")
|
JsonNode config = configClient.loadServiceConfig(appKey, "ANDROID", "PUSH")
|
||||||
.map(node -> node.path("huawei"))
|
.map(node -> node.path("huawei"))
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
|
|||||||
@ -39,25 +39,25 @@ public class OppoPushProvider implements PushProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean send(String appId, String token, String title, String body, String payload) {
|
public boolean send(String appKey, String token, String title, String body, String payload) {
|
||||||
String appKey = resolveConfig(appId, "appKey");
|
String vendorAppKey = resolveConfig(appKey, "appKey");
|
||||||
String masterSecret = resolveConfig(appId, "masterSecret");
|
String masterSecret = resolveConfig(appKey, "masterSecret");
|
||||||
if (appKey.isBlank() || masterSecret.isBlank()) {
|
if (vendorAppKey.isBlank() || masterSecret.isBlank()) {
|
||||||
log.warn("OPPO push not configured");
|
log.warn("OPPO push not configured");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
String authToken = getAccessToken(appKey, masterSecret);
|
String authToken = getAccessToken(vendorAppKey, masterSecret);
|
||||||
String messageId = appId + "_" + System.currentTimeMillis();
|
String messageId = appKey + "_" + System.currentTimeMillis();
|
||||||
Map<String, Object> message = Map.of(
|
Map<String, Object> inner = new java.util.LinkedHashMap<>();
|
||||||
"message", Map.of(
|
inner.put("app_message_id", messageId);
|
||||||
"app_message_id", messageId,
|
inner.put("title", title);
|
||||||
"title", title,
|
inner.put("content", body);
|
||||||
"content", body,
|
inner.put("target_type", 2);
|
||||||
"target_type", 2,
|
inner.put("target_value", token);
|
||||||
"target_value", token
|
String channelId = resolveConfig(appKey, "channelId");
|
||||||
)
|
if (!channelId.isBlank()) inner.put("channel_id", channelId);
|
||||||
);
|
Map<String, Object> message = Map.of("message", inner);
|
||||||
String requestBody = objectMapper.writeValueAsString(message);
|
String requestBody = objectMapper.writeValueAsString(message);
|
||||||
HttpRequest request = HttpRequest.newBuilder()
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
.uri(URI.create(PUSH_URL))
|
.uri(URI.create(PUSH_URL))
|
||||||
@ -96,8 +96,8 @@ public class OppoPushProvider implements PushProvider {
|
|||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String resolveConfig(String appId, String key) {
|
private String resolveConfig(String appKey, String key) {
|
||||||
JsonNode config = configClient.loadServiceConfig(appId, "ANDROID", "PUSH")
|
JsonNode config = configClient.loadServiceConfig(appKey, "ANDROID", "PUSH")
|
||||||
.map(node -> node.path("oppo"))
|
.map(node -> node.path("oppo"))
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
|
|||||||
@ -2,9 +2,9 @@ package com.xuqm.push.service.provider;
|
|||||||
|
|
||||||
public interface PushProvider {
|
public interface PushProvider {
|
||||||
String vendorName();
|
String vendorName();
|
||||||
boolean send(String appId, String token, String title, String body, String payload);
|
boolean send(String appKey, String token, String title, String body, String payload);
|
||||||
|
|
||||||
default boolean send(String appId, String token, String title, String body, String payload, PushSendOptions options) {
|
default boolean send(String appKey, String token, String title, String body, String payload, PushSendOptions options) {
|
||||||
return send(appId, token, title, body, payload);
|
return send(appKey, token, title, body, payload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,21 +39,29 @@ public class VivoPushProvider implements PushProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean send(String appId, String token, String title, String body, String payload) {
|
public boolean send(String appKey, String token, String title, String body, String payload) {
|
||||||
String appKey = resolveConfig(appId, "appKey");
|
String vendorAppKey = resolveConfig(appKey, "appKey");
|
||||||
String appIdConfig = resolveConfig(appId, "appId");
|
String appIdConfig = resolveConfig(appKey, "appId");
|
||||||
if (appKey.isBlank() || appIdConfig.isBlank()) {
|
if (vendorAppKey.isBlank() || appIdConfig.isBlank()) {
|
||||||
log.warn("Vivo push not configured");
|
log.warn("Vivo push not configured");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
String authToken = getAccessToken(appIdConfig, appKey);
|
String authToken = getAccessToken(appIdConfig, vendorAppKey);
|
||||||
Map<String, Object> message = Map.of(
|
Map<String, Object> message = new java.util.LinkedHashMap<>();
|
||||||
"regId", token,
|
message.put("regId", token);
|
||||||
"title", title,
|
message.put("title", title);
|
||||||
"content", body,
|
message.put("content", body);
|
||||||
"notifyType", 1
|
message.put("notifyType", 1);
|
||||||
);
|
String category = resolveConfig(appKey, "category");
|
||||||
|
String receiptId = resolveConfig(appKey, "receiptId");
|
||||||
|
if (!category.isBlank()) {
|
||||||
|
// vivo classification: 0=operation, 1=IM/system
|
||||||
|
message.put("classification", "IM".equalsIgnoreCase(category) ? 1 : 0);
|
||||||
|
}
|
||||||
|
if (!receiptId.isBlank()) {
|
||||||
|
message.put("requestId", receiptId + "_" + System.currentTimeMillis());
|
||||||
|
}
|
||||||
String requestBody = objectMapper.writeValueAsString(message);
|
String requestBody = objectMapper.writeValueAsString(message);
|
||||||
HttpRequest request = HttpRequest.newBuilder()
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
.uri(URI.create(PUSH_URL))
|
.uri(URI.create(PUSH_URL))
|
||||||
@ -92,8 +100,8 @@ public class VivoPushProvider implements PushProvider {
|
|||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String resolveConfig(String appId, String key) {
|
private String resolveConfig(String appKey, String key) {
|
||||||
JsonNode config = configClient.loadServiceConfig(appId, "ANDROID", "PUSH")
|
JsonNode config = configClient.loadServiceConfig(appKey, "ANDROID", "PUSH")
|
||||||
.map(node -> node.path("vivo"))
|
.map(node -> node.path("vivo"))
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import java.net.http.HttpClient;
|
|||||||
import java.net.http.HttpRequest;
|
import java.net.http.HttpRequest;
|
||||||
import java.net.http.HttpResponse;
|
import java.net.http.HttpResponse;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class XiaomiPushProvider implements PushProvider {
|
public class XiaomiPushProvider implements PushProvider {
|
||||||
@ -36,50 +37,79 @@ public class XiaomiPushProvider implements PushProvider {
|
|||||||
public String vendorName() { return "XIAOMI"; }
|
public String vendorName() { return "XIAOMI"; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean send(String appId, String token, String title, String body, String payload) {
|
public boolean send(String appKey, String token, String title, String body, String payload) {
|
||||||
return send(appId, token, title, body, payload, PushSendOptions.empty());
|
return send(appKey, token, title, body, payload, PushSendOptions.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean send(String appId, String token, String title, String body, String payload, PushSendOptions options) {
|
public boolean send(String appKey, String token, String title, String body, String payload, PushSendOptions options) {
|
||||||
String appSecret = resolveConfig(appId, "appSecret", envAppSecret);
|
String appSecret = resolveVendorConfig(appKey, "appSecret", envAppSecret);
|
||||||
if (appSecret.isBlank()) {
|
if (appSecret.isBlank()) {
|
||||||
appSecret = resolveConfig(appId, "appKey", envAppSecret);
|
appSecret = resolveVendorConfig(appKey, "appKey", envAppSecret);
|
||||||
}
|
}
|
||||||
if (appSecret.isBlank()) {
|
if (appSecret.isBlank()) {
|
||||||
log.warn("Xiaomi push not configured");
|
log.warn("Xiaomi push not configured");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
String packageName = resolveConfig(appId, "packageName", "");
|
|
||||||
|
Map<String, String> platformInfo = configClient.fetchPlatformInfo(appKey);
|
||||||
|
String packageName = platformInfo.getOrDefault("androidPackageName", "");
|
||||||
if (packageName.isBlank()) {
|
if (packageName.isBlank()) {
|
||||||
log.warn("Xiaomi push skipped: packageName not configured for appId={}", appId);
|
log.warn("Xiaomi push skipped: Android packageName not set in app config for appKey={}", appKey);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String form = "registration_id=" + URLEncoder.encode(token, StandardCharsets.UTF_8)
|
String channelId = resolveVendorConfig(appKey, "channelId", "");
|
||||||
+ "&title=" + URLEncoder.encode(title, StandardCharsets.UTF_8)
|
|
||||||
+ "&description=" + URLEncoder.encode(body, StandardCharsets.UTF_8)
|
|
||||||
+ "&restricted_package_name=" + URLEncoder.encode(packageName, StandardCharsets.UTF_8)
|
|
||||||
+ "¬ify_type=1";
|
|
||||||
if (options != null && options.channelId() != null && !options.channelId().isBlank()) {
|
if (options != null && options.channelId() != null && !options.channelId().isBlank()) {
|
||||||
form += "&channel_id=" + URLEncoder.encode(options.channelId(), StandardCharsets.UTF_8);
|
channelId = options.channelId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StringBuilder form = new StringBuilder();
|
||||||
|
form.append("registration_id=").append(URLEncoder.encode(token, StandardCharsets.UTF_8))
|
||||||
|
.append("&title=").append(URLEncoder.encode(title, StandardCharsets.UTF_8))
|
||||||
|
.append("&description=").append(URLEncoder.encode(body, StandardCharsets.UTF_8))
|
||||||
|
.append("&restricted_package_name=").append(URLEncoder.encode(packageName, StandardCharsets.UTF_8))
|
||||||
|
.append("¬ify_type=1")
|
||||||
|
.append("&extra.notify_foreground=1");
|
||||||
|
if (!channelId.isBlank()) {
|
||||||
|
form.append("&channel_id=").append(URLEncoder.encode(channelId, StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
HttpRequest request = HttpRequest.newBuilder()
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
.uri(URI.create(pushUrl))
|
.uri(URI.create(pushUrl))
|
||||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||||
.header("Authorization", "key=" + appSecret)
|
.header("Authorization", "key=" + appSecret)
|
||||||
.POST(HttpRequest.BodyPublishers.ofString(form))
|
.POST(HttpRequest.BodyPublishers.ofString(form.toString()))
|
||||||
.build();
|
.build();
|
||||||
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
return response.statusCode() == 200;
|
log.debug("Xiaomi push response status={} body={}", response.statusCode(), response.body());
|
||||||
|
if (response.statusCode() != 200) {
|
||||||
|
log.warn("Xiaomi push HTTP error status={} body={}", response.statusCode(), response.body());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Xiaomi returns 200 even on error; check result field
|
||||||
|
try {
|
||||||
|
com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
|
||||||
|
JsonNode json = mapper.readTree(response.body());
|
||||||
|
String result = json.path("result").asText("");
|
||||||
|
if (!"ok".equalsIgnoreCase(result)) {
|
||||||
|
log.warn("Xiaomi push rejected result={} description={} body={}",
|
||||||
|
result, json.path("description").asText(""), response.body());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("Xiaomi push response parse error: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Xiaomi push failed: {}", e.getMessage());
|
log.error("Xiaomi push failed: {}", e.getMessage());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String resolveConfig(String appId, String key, String fallback) {
|
private String resolveVendorConfig(String appKey, String key, String fallback) {
|
||||||
JsonNode config = configClient.loadServiceConfig(appId, "ANDROID", "PUSH")
|
JsonNode config = configClient.loadServiceConfig(appKey, "ANDROID", "PUSH")
|
||||||
.map(node -> node.path("xiaomi"))
|
.map(node -> node.path("xiaomi"))
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
|
|||||||
@ -48,4 +48,4 @@ push:
|
|||||||
push-url: https://api.push.apple.com/3/device/{token}
|
push-url: https://api.push.apple.com/3/device/{token}
|
||||||
sandbox-push-url: https://api.sandbox.push.apple.com/3/device/{token}
|
sandbox-push-url: https://api.sandbox.push.apple.com/3/device/{token}
|
||||||
|
|
||||||
tenant-service-base-url: ${TENANT_SERVICE_BASE_URL:http://tenant-service:8081}
|
tenant-service-base-url: ${TENANT_SERVICE_BASE_URL:http://tenant-service:9001}
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/apps/{appId}/services")
|
@RequestMapping("/api/apps/{appKey}/services")
|
||||||
public class FeatureServiceController {
|
public class FeatureServiceController {
|
||||||
|
|
||||||
private final FeatureServiceManager featureServiceManager;
|
private final FeatureServiceManager featureServiceManager;
|
||||||
@ -37,34 +37,34 @@ public class FeatureServiceController {
|
|||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public ResponseEntity<ApiResponse<List<FeatureServiceEntity>>> list(
|
public ResponseEntity<ApiResponse<List<FeatureServiceEntity>>> list(
|
||||||
@PathVariable String appId, @AuthenticationPrincipal String tenantId) {
|
@PathVariable String appKey, @AuthenticationPrincipal String tenantId) {
|
||||||
appService.getById(appId, tenantId);
|
appService.getById(appKey, tenantId);
|
||||||
return ResponseEntity.ok(ApiResponse.success(featureServiceManager.listByApp(appId)));
|
return ResponseEntity.ok(ApiResponse.success(featureServiceManager.listByApp(appKey)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/item")
|
@GetMapping("/item")
|
||||||
public ResponseEntity<ApiResponse<FeatureServiceEntity>> get(
|
public ResponseEntity<ApiResponse<FeatureServiceEntity>> get(
|
||||||
@PathVariable String appId,
|
@PathVariable String appKey,
|
||||||
@RequestParam FeatureServiceEntity.Platform platform,
|
@RequestParam FeatureServiceEntity.Platform platform,
|
||||||
@RequestParam FeatureServiceEntity.ServiceType serviceType,
|
@RequestParam FeatureServiceEntity.ServiceType serviceType,
|
||||||
@AuthenticationPrincipal String tenantId) {
|
@AuthenticationPrincipal String tenantId) {
|
||||||
appService.getById(appId, tenantId);
|
appService.getById(appKey, tenantId);
|
||||||
return ResponseEntity.ok(ApiResponse.success(featureServiceManager.getByPlatform(appId, platform, serviceType)));
|
return ResponseEntity.ok(ApiResponse.success(featureServiceManager.getByPlatform(appKey, platform, serviceType)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Disable a service (enable=false only; enabling requires ops approval via request-activation). */
|
/** Disable a service (enable=false only; enabling requires ops approval via request-activation). */
|
||||||
@PostMapping("/toggle")
|
@PostMapping("/toggle")
|
||||||
public ResponseEntity<ApiResponse<FeatureServiceEntity>> toggle(
|
public ResponseEntity<ApiResponse<FeatureServiceEntity>> toggle(
|
||||||
@PathVariable String appId,
|
@PathVariable String appKey,
|
||||||
@RequestParam FeatureServiceEntity.Platform platform,
|
@RequestParam FeatureServiceEntity.Platform platform,
|
||||||
@RequestParam FeatureServiceEntity.ServiceType serviceType,
|
@RequestParam FeatureServiceEntity.ServiceType serviceType,
|
||||||
@RequestParam boolean enable,
|
@RequestParam boolean enable,
|
||||||
@AuthenticationPrincipal String tenantId) {
|
@AuthenticationPrincipal String tenantId) {
|
||||||
appService.getById(appId, tenantId);
|
appService.getById(appKey, tenantId);
|
||||||
if (enable) {
|
if (enable) {
|
||||||
throw new com.xuqm.common.exception.BusinessException(400, "开启服务请通过 request-activation 申请");
|
throw new com.xuqm.common.exception.BusinessException(400, "开启服务请通过 request-activation 申请");
|
||||||
}
|
}
|
||||||
FeatureServiceEntity saved = featureServiceManager.disable(appId, platform, serviceType);
|
FeatureServiceEntity saved = featureServiceManager.disable(appKey, platform, serviceType);
|
||||||
operationLogService.record(tenantId, "SERVICE", "FEATURE_SERVICE", saved.getId(), "DISABLE_SERVICE", java.util.Map.of(
|
operationLogService.record(tenantId, "SERVICE", "FEATURE_SERVICE", saved.getId(), "DISABLE_SERVICE", java.util.Map.of(
|
||||||
"platform", platform.name(),
|
"platform", platform.name(),
|
||||||
"serviceType", serviceType.name()
|
"serviceType", serviceType.name()
|
||||||
@ -74,15 +74,15 @@ public class FeatureServiceController {
|
|||||||
|
|
||||||
@PutMapping("/config")
|
@PutMapping("/config")
|
||||||
public ResponseEntity<ApiResponse<FeatureServiceEntity>> updateConfig(
|
public ResponseEntity<ApiResponse<FeatureServiceEntity>> updateConfig(
|
||||||
@PathVariable String appId,
|
@PathVariable String appKey,
|
||||||
@RequestParam FeatureServiceEntity.Platform platform,
|
@RequestParam FeatureServiceEntity.Platform platform,
|
||||||
@RequestParam FeatureServiceEntity.ServiceType serviceType,
|
@RequestParam FeatureServiceEntity.ServiceType serviceType,
|
||||||
@RequestBody FeatureServiceConfigRequest req,
|
@RequestBody FeatureServiceConfigRequest req,
|
||||||
@AuthenticationPrincipal String tenantId) {
|
@AuthenticationPrincipal String tenantId) {
|
||||||
appService.getById(appId, tenantId);
|
appService.getById(appKey, tenantId);
|
||||||
String config = switch (serviceType) {
|
String config = switch (serviceType) {
|
||||||
case IM -> featureServiceManager.buildImConfig(
|
case IM -> featureServiceManager.buildImConfig(
|
||||||
appId,
|
appKey,
|
||||||
platform,
|
platform,
|
||||||
req == null ? null : req.allowStrangerMessage(),
|
req == null ? null : req.allowStrangerMessage(),
|
||||||
req == null ? null : req.allowFriendRequest(),
|
req == null ? null : req.allowFriendRequest(),
|
||||||
@ -95,7 +95,7 @@ public class FeatureServiceController {
|
|||||||
req == null ? null : req.allowMultiDeviceLogin(),
|
req == null ? null : req.allowMultiDeviceLogin(),
|
||||||
req == null ? null : req.multiClientConversationDeleteSync());
|
req == null ? null : req.multiClientConversationDeleteSync());
|
||||||
case UPDATE -> featureServiceManager.buildUpdateConfig(
|
case UPDATE -> featureServiceManager.buildUpdateConfig(
|
||||||
appId,
|
appKey,
|
||||||
platform,
|
platform,
|
||||||
req == null ? null : req.defaultStoreTargets(),
|
req == null ? null : req.defaultStoreTargets(),
|
||||||
req == null ? null : req.defaultPublishMode(),
|
req == null ? null : req.defaultPublishMode(),
|
||||||
@ -110,19 +110,24 @@ public class FeatureServiceController {
|
|||||||
req == null ? null : req.defaultAppStoreUrl(),
|
req == null ? null : req.defaultAppStoreUrl(),
|
||||||
req == null ? null : req.defaultMarketUrl());
|
req == null ? null : req.defaultMarketUrl());
|
||||||
case PUSH -> featureServiceManager.buildPushConfig(
|
case PUSH -> featureServiceManager.buildPushConfig(
|
||||||
appId,
|
appKey,
|
||||||
platform,
|
platform,
|
||||||
req == null ? null : req.huaweiAppIdValue(),
|
req == null ? null : req.huaweiAppIdValue(),
|
||||||
req == null ? null : req.huaweiAppSecretValue(),
|
req == null ? null : req.huaweiAppSecretValue(),
|
||||||
|
req == null ? null : req.huaweiCategoryValue(),
|
||||||
req == null ? null : req.xiaomiAppIdValue(),
|
req == null ? null : req.xiaomiAppIdValue(),
|
||||||
req == null ? null : req.xiaomiAppKeyValue(),
|
req == null ? null : req.xiaomiAppKeyValue(),
|
||||||
req == null ? null : req.xiaomiAppSecretValue(),
|
req == null ? null : req.xiaomiAppSecretValue(),
|
||||||
|
req == null ? null : req.xiaomiChannelIdValue(),
|
||||||
req == null ? null : req.oppoAppIdValue(),
|
req == null ? null : req.oppoAppIdValue(),
|
||||||
req == null ? null : req.oppoAppKeyValue(),
|
req == null ? null : req.oppoAppKeyValue(),
|
||||||
req == null ? null : req.oppoMasterSecretValue(),
|
req == null ? null : req.oppoMasterSecretValue(),
|
||||||
|
req == null ? null : req.oppoChannelIdValue(),
|
||||||
req == null ? null : req.vivoAppIdValue(),
|
req == null ? null : req.vivoAppIdValue(),
|
||||||
req == null ? null : req.vivoAppKeyValue(),
|
req == null ? null : req.vivoAppKeyValue(),
|
||||||
req == null ? null : req.vivoAppSecretValue(),
|
req == null ? null : req.vivoAppSecretValue(),
|
||||||
|
req == null ? null : req.vivoCategoryValue(),
|
||||||
|
req == null ? null : req.vivoReceiptIdValue(),
|
||||||
req == null ? null : req.honorAppIdValue(),
|
req == null ? null : req.honorAppIdValue(),
|
||||||
req == null ? null : req.honorClientIdValue(),
|
req == null ? null : req.honorClientIdValue(),
|
||||||
req == null ? null : req.honorClientSecretValue(),
|
req == null ? null : req.honorClientSecretValue(),
|
||||||
@ -138,7 +143,7 @@ public class FeatureServiceController {
|
|||||||
req == null ? null : req.routing());
|
req == null ? null : req.routing());
|
||||||
};
|
};
|
||||||
FeatureServiceEntity saved = featureServiceManager.updateConfig(
|
FeatureServiceEntity saved = featureServiceManager.updateConfig(
|
||||||
appId, platform, serviceType, config);
|
appKey, platform, serviceType, config);
|
||||||
operationLogService.record(tenantId, "SERVICE", "FEATURE_SERVICE", saved.getId(), "UPDATE_SERVICE_CONFIG", java.util.Map.of(
|
operationLogService.record(tenantId, "SERVICE", "FEATURE_SERVICE", saved.getId(), "UPDATE_SERVICE_CONFIG", java.util.Map.of(
|
||||||
"platform", platform.name(),
|
"platform", platform.name(),
|
||||||
"serviceType", serviceType.name()
|
"serviceType", serviceType.name()
|
||||||
@ -149,13 +154,13 @@ public class FeatureServiceController {
|
|||||||
/** Submit an activation request for ops approval. */
|
/** Submit an activation request for ops approval. */
|
||||||
@PostMapping("/request-activation")
|
@PostMapping("/request-activation")
|
||||||
public ResponseEntity<ApiResponse<ServiceActivationRequestEntity>> requestActivation(
|
public ResponseEntity<ApiResponse<ServiceActivationRequestEntity>> requestActivation(
|
||||||
@PathVariable String appId,
|
@PathVariable String appKey,
|
||||||
@RequestParam FeatureServiceEntity.Platform platform,
|
@RequestParam FeatureServiceEntity.Platform platform,
|
||||||
@RequestParam FeatureServiceEntity.ServiceType serviceType,
|
@RequestParam FeatureServiceEntity.ServiceType serviceType,
|
||||||
@RequestParam(required = false) String applyReason,
|
@RequestParam(required = false) String applyReason,
|
||||||
@AuthenticationPrincipal String tenantId) {
|
@AuthenticationPrincipal String tenantId) {
|
||||||
appService.getById(appId, tenantId);
|
appService.getById(appKey, tenantId);
|
||||||
ServiceActivationRequestEntity saved = featureServiceManager.submitActivationRequest(appId, platform, serviceType, applyReason);
|
ServiceActivationRequestEntity saved = featureServiceManager.submitActivationRequest(appKey, platform, serviceType, applyReason);
|
||||||
java.util.Map<String, Object> detail = new java.util.LinkedHashMap<>();
|
java.util.Map<String, Object> detail = new java.util.LinkedHashMap<>();
|
||||||
detail.put("platform", platform.name());
|
detail.put("platform", platform.name());
|
||||||
detail.put("serviceType", serviceType.name());
|
detail.put("serviceType", serviceType.name());
|
||||||
@ -168,17 +173,17 @@ public class FeatureServiceController {
|
|||||||
|
|
||||||
@GetMapping("/requests")
|
@GetMapping("/requests")
|
||||||
public ResponseEntity<ApiResponse<List<ServiceActivationRequestEntity>>> listRequests(
|
public ResponseEntity<ApiResponse<List<ServiceActivationRequestEntity>>> listRequests(
|
||||||
@PathVariable String appId, @AuthenticationPrincipal String tenantId) {
|
@PathVariable String appKey, @AuthenticationPrincipal String tenantId) {
|
||||||
appService.getById(appId, tenantId);
|
appService.getById(appKey, tenantId);
|
||||||
return ResponseEntity.ok(ApiResponse.success(featureServiceManager.listRequestsByApp(appId)));
|
return ResponseEntity.ok(ApiResponse.success(featureServiceManager.listRequestsByApp(appKey)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{id}/regenerate-key")
|
@PostMapping("/{id}/regenerate-key")
|
||||||
public ResponseEntity<ApiResponse<FeatureServiceEntity>> regenerateKey(
|
public ResponseEntity<ApiResponse<FeatureServiceEntity>> regenerateKey(
|
||||||
@PathVariable String appId,
|
@PathVariable String appKey,
|
||||||
@PathVariable String id,
|
@PathVariable String id,
|
||||||
@AuthenticationPrincipal String tenantId) {
|
@AuthenticationPrincipal String tenantId) {
|
||||||
appService.getById(appId, tenantId);
|
appService.getById(appKey, tenantId);
|
||||||
FeatureServiceEntity updated = featureServiceManager.regenerateSecretKey(id);
|
FeatureServiceEntity updated = featureServiceManager.regenerateSecretKey(id);
|
||||||
operationLogService.record(tenantId, "SERVICE", "FEATURE_SERVICE", updated.getId(), "REGENERATE_KEY", java.util.Map.of(
|
operationLogService.record(tenantId, "SERVICE", "FEATURE_SERVICE", updated.getId(), "REGENERATE_KEY", java.util.Map.of(
|
||||||
"platform", updated.getPlatform().name(),
|
"platform", updated.getPlatform().name(),
|
||||||
@ -212,15 +217,20 @@ public class FeatureServiceController {
|
|||||||
String defaultMarketUrl,
|
String defaultMarketUrl,
|
||||||
String huaweiAppId,
|
String huaweiAppId,
|
||||||
String huaweiAppSecret,
|
String huaweiAppSecret,
|
||||||
|
String huaweiCategory,
|
||||||
String xiaomiAppId,
|
String xiaomiAppId,
|
||||||
String xiaomiAppKey,
|
String xiaomiAppKey,
|
||||||
String xiaomiAppSecret,
|
String xiaomiAppSecret,
|
||||||
|
String xiaomiChannelId,
|
||||||
String oppoAppId,
|
String oppoAppId,
|
||||||
String oppoAppKey,
|
String oppoAppKey,
|
||||||
String oppoMasterSecret,
|
String oppoMasterSecret,
|
||||||
|
String oppoChannelId,
|
||||||
String vivoAppId,
|
String vivoAppId,
|
||||||
String vivoAppKey,
|
String vivoAppKey,
|
||||||
String vivoAppSecret,
|
String vivoAppSecret,
|
||||||
|
String vivoCategory,
|
||||||
|
String vivoReceiptId,
|
||||||
String honorAppId,
|
String honorAppId,
|
||||||
String honorClientId,
|
String honorClientId,
|
||||||
String honorClientSecret,
|
String honorClientSecret,
|
||||||
@ -243,21 +253,26 @@ public class FeatureServiceController {
|
|||||||
JsonNode channels,
|
JsonNode channels,
|
||||||
JsonNode routing
|
JsonNode routing
|
||||||
) {
|
) {
|
||||||
public String huaweiAppIdValue() { return firstText(huaweiAppId, huawei == null ? null : huawei.appId()); }
|
public String huaweiAppIdValue() { return firstText(huaweiAppId, huawei == null ? null : huawei.appKey()); }
|
||||||
public String huaweiAppSecretValue() { return firstText(huaweiAppSecret, huawei == null ? null : huawei.appSecret()); }
|
public String huaweiAppSecretValue() { return firstText(huaweiAppSecret, huawei == null ? null : huawei.appSecret()); }
|
||||||
public String xiaomiAppIdValue() { return firstText(xiaomiAppId, xiaomi == null ? null : xiaomi.appId()); }
|
public String huaweiCategoryValue() { return firstText(huaweiCategory, huawei == null ? null : huawei.category()); }
|
||||||
|
public String xiaomiAppIdValue() { return firstText(xiaomiAppId, xiaomi == null ? null : xiaomi.appKey()); }
|
||||||
public String xiaomiAppKeyValue() { return firstText(xiaomiAppKey, xiaomi == null ? null : xiaomi.appKey()); }
|
public String xiaomiAppKeyValue() { return firstText(xiaomiAppKey, xiaomi == null ? null : xiaomi.appKey()); }
|
||||||
public String xiaomiAppSecretValue() { return firstText(xiaomiAppSecret, xiaomi == null ? null : xiaomi.appSecret()); }
|
public String xiaomiAppSecretValue() { return firstText(xiaomiAppSecret, xiaomi == null ? null : xiaomi.appSecret()); }
|
||||||
public String oppoAppIdValue() { return firstText(oppoAppId, oppo == null ? null : oppo.appId()); }
|
public String xiaomiChannelIdValue() { return firstText(xiaomiChannelId, xiaomi == null ? null : xiaomi.channelId()); }
|
||||||
|
public String oppoAppIdValue() { return firstText(oppoAppId, oppo == null ? null : oppo.appKey()); }
|
||||||
public String oppoAppKeyValue() { return firstText(oppoAppKey, oppo == null ? null : oppo.appKey()); }
|
public String oppoAppKeyValue() { return firstText(oppoAppKey, oppo == null ? null : oppo.appKey()); }
|
||||||
public String oppoMasterSecretValue() { return firstText(oppoMasterSecret, oppo == null ? null : oppo.masterSecret()); }
|
public String oppoMasterSecretValue() { return firstText(oppoMasterSecret, oppo == null ? null : oppo.masterSecret()); }
|
||||||
public String vivoAppIdValue() { return firstText(vivoAppId, vivo == null ? null : vivo.appId()); }
|
public String oppoChannelIdValue() { return firstText(oppoChannelId, oppo == null ? null : oppo.channelId()); }
|
||||||
|
public String vivoAppIdValue() { return firstText(vivoAppId, vivo == null ? null : vivo.appKey()); }
|
||||||
public String vivoAppKeyValue() { return firstText(vivoAppKey, vivo == null ? null : vivo.appKey()); }
|
public String vivoAppKeyValue() { return firstText(vivoAppKey, vivo == null ? null : vivo.appKey()); }
|
||||||
public String vivoAppSecretValue() { return firstText(vivoAppSecret, vivo == null ? null : vivo.appSecret()); }
|
public String vivoAppSecretValue() { return firstText(vivoAppSecret, vivo == null ? null : vivo.appSecret()); }
|
||||||
public String honorAppIdValue() { return firstText(honorAppId, honor == null ? null : honor.appId()); }
|
public String vivoCategoryValue() { return firstText(vivoCategory, vivo == null ? null : vivo.category()); }
|
||||||
|
public String vivoReceiptIdValue() { return firstText(vivoReceiptId, vivo == null ? null : vivo.receiptId()); }
|
||||||
|
public String honorAppIdValue() { return firstText(honorAppId, honor == null ? null : honor.appKey()); }
|
||||||
public String honorClientIdValue() { return firstText(honorClientId, honor == null ? null : honor.clientId()); }
|
public String honorClientIdValue() { return firstText(honorClientId, honor == null ? null : honor.clientId()); }
|
||||||
public String honorClientSecretValue() { return firstText(honorClientSecret, honor == null ? null : honor.clientSecret()); }
|
public String honorClientSecretValue() { return firstText(honorClientSecret, honor == null ? null : honor.clientSecret()); }
|
||||||
public String harmonyAppIdValue() { return firstText(harmonyAppId, harmony == null ? null : harmony.appId()); }
|
public String harmonyAppIdValue() { return firstText(harmonyAppId, harmony == null ? null : harmony.appKey()); }
|
||||||
public String harmonyAppSecretValue() { return firstText(harmonyAppSecret, harmony == null ? null : harmony.appSecret()); }
|
public String harmonyAppSecretValue() { return firstText(harmonyAppSecret, harmony == null ? null : harmony.appSecret()); }
|
||||||
public String apnsTeamIdValue() { return firstText(apnsTeamId, apns == null ? null : apns.teamId()); }
|
public String apnsTeamIdValue() { return firstText(apnsTeamId, apns == null ? null : apns.teamId()); }
|
||||||
public String apnsKeyIdValue() { return firstText(apnsKeyId, apns == null ? null : apns.keyId()); }
|
public String apnsKeyIdValue() { return firstText(apnsKeyId, apns == null ? null : apns.keyId()); }
|
||||||
@ -282,7 +297,7 @@ public class FeatureServiceController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public record PushVendorConfig(
|
public record PushVendorConfig(
|
||||||
String appId,
|
String appKey,
|
||||||
String appKey,
|
String appKey,
|
||||||
String appSecret,
|
String appSecret,
|
||||||
String masterSecret,
|
String masterSecret,
|
||||||
@ -293,6 +308,9 @@ public class FeatureServiceController {
|
|||||||
String bundleId,
|
String bundleId,
|
||||||
String keyPath,
|
String keyPath,
|
||||||
Boolean sandbox,
|
Boolean sandbox,
|
||||||
String serviceAccountJson
|
String serviceAccountJson,
|
||||||
|
String channelId,
|
||||||
|
String category,
|
||||||
|
String receiptId
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,24 +32,40 @@ public class InternalSdkController {
|
|||||||
this.featureServiceManager = featureServiceManager;
|
this.featureServiceManager = featureServiceManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/apps/{appId}/secret")
|
@GetMapping("/apps/{appKey}/secret")
|
||||||
public ResponseEntity<ApiResponse<Map<String, String>>> getAppSecret(
|
public ResponseEntity<ApiResponse<Map<String, String>>> getAppSecret(
|
||||||
@PathVariable String appId,
|
@PathVariable String appKey,
|
||||||
@RequestHeader(value = "X-Internal-Token", required = false) String token) {
|
@RequestHeader(value = "X-Internal-Token", required = false) String token) {
|
||||||
if (token == null || !internalToken.equals(token)) {
|
if (token == null || !internalToken.equals(token)) {
|
||||||
return ResponseEntity.status(403)
|
return ResponseEntity.status(403)
|
||||||
.body(ApiResponse.error(403, "Forbidden"));
|
.body(ApiResponse.error(403, "Forbidden"));
|
||||||
}
|
}
|
||||||
AppEntity app = provisioningService.resolveApp(appId);
|
AppEntity app = provisioningService.resolveApp(appKey);
|
||||||
return ResponseEntity.ok(ApiResponse.success(Map.of(
|
return ResponseEntity.ok(ApiResponse.success(Map.of(
|
||||||
"appId", app.getAppKey(),
|
"appKey", app.getAppKey(),
|
||||||
"appSecret", app.getAppSecret()
|
"appSecret", app.getAppSecret()
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/apps/{appId}/services/{platform}/{serviceType}")
|
@GetMapping("/apps/{appKey}/platform-info")
|
||||||
|
public ResponseEntity<ApiResponse<Map<String, String>>> getPlatformInfo(
|
||||||
|
@PathVariable String appKey,
|
||||||
|
@RequestHeader(value = "X-Internal-Token", required = false) String token) {
|
||||||
|
if (token == null || !internalToken.equals(token)) {
|
||||||
|
return ResponseEntity.status(403)
|
||||||
|
.body(ApiResponse.error(403, "Forbidden"));
|
||||||
|
}
|
||||||
|
AppEntity app = provisioningService.resolveApp(appKey);
|
||||||
|
return ResponseEntity.ok(ApiResponse.success(Map.of(
|
||||||
|
"androidPackageName", app.getPackageName() == null ? "" : app.getPackageName(),
|
||||||
|
"iosBundleId", app.getIosBundleId() == null ? "" : app.getIosBundleId(),
|
||||||
|
"harmonyBundleName", app.getHarmonyBundleName() == null ? "" : app.getHarmonyBundleName()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/apps/{appKey}/services/{platform}/{serviceType}")
|
||||||
public ResponseEntity<ApiResponse<Map<String, Object>>> getService(
|
public ResponseEntity<ApiResponse<Map<String, Object>>> getService(
|
||||||
@PathVariable String appId,
|
@PathVariable String appKey,
|
||||||
@PathVariable FeatureServiceEntity.Platform platform,
|
@PathVariable FeatureServiceEntity.Platform platform,
|
||||||
@PathVariable FeatureServiceEntity.ServiceType serviceType,
|
@PathVariable FeatureServiceEntity.ServiceType serviceType,
|
||||||
@RequestHeader(value = "X-Internal-Token", required = false) String token) {
|
@RequestHeader(value = "X-Internal-Token", required = false) String token) {
|
||||||
@ -57,8 +73,8 @@ public class InternalSdkController {
|
|||||||
return ResponseEntity.status(403)
|
return ResponseEntity.status(403)
|
||||||
.body(ApiResponse.error(403, "Forbidden"));
|
.body(ApiResponse.error(403, "Forbidden"));
|
||||||
}
|
}
|
||||||
provisioningService.resolveApp(appId);
|
provisioningService.resolveApp(appKey);
|
||||||
FeatureServiceEntity service = featureServiceManager.getOrFail(appId, platform, serviceType);
|
FeatureServiceEntity service = featureServiceManager.getOrFail(appKey, platform, serviceType);
|
||||||
return ResponseEntity.ok(ApiResponse.success(Map.of(
|
return ResponseEntity.ok(ApiResponse.success(Map.of(
|
||||||
"enabled", service.isEnabled(),
|
"enabled", service.isEnabled(),
|
||||||
"config", service.getConfig() == null ? "" : service.getConfig()
|
"config", service.getConfig() == null ? "" : service.getConfig()
|
||||||
|
|||||||
@ -168,25 +168,25 @@ public class OpsController {
|
|||||||
@PreAuthorize("hasAuthority('ROLE_OPS')")
|
@PreAuthorize("hasAuthority('ROLE_OPS')")
|
||||||
public ResponseEntity<ApiResponse<Map<String, Object>>> searchPushByToken(
|
public ResponseEntity<ApiResponse<Map<String, Object>>> searchPushByToken(
|
||||||
@RequestParam String token,
|
@RequestParam String token,
|
||||||
@RequestParam(required = false) String appId) {
|
@RequestParam(required = false) String appKey) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(pushDiagnosticsClient.searchByToken(token, appId)));
|
return ResponseEntity.ok(ApiResponse.success(pushDiagnosticsClient.searchByToken(token, appKey)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/api/ops/push/device-logs")
|
@GetMapping("/api/ops/push/device-logs")
|
||||||
@PreAuthorize("hasAuthority('ROLE_OPS')")
|
@PreAuthorize("hasAuthority('ROLE_OPS')")
|
||||||
public ResponseEntity<ApiResponse<Map<String, Object>>> pushDeviceLogs(
|
public ResponseEntity<ApiResponse<Map<String, Object>>> pushDeviceLogs(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam String userId,
|
@RequestParam String userId,
|
||||||
@RequestParam(defaultValue = "0") int page,
|
@RequestParam(defaultValue = "0") int page,
|
||||||
@RequestParam(defaultValue = "20") int size) {
|
@RequestParam(defaultValue = "20") int size) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(pushDiagnosticsClient.deviceLogs(appId, userId, page, size)));
|
return ResponseEntity.ok(ApiResponse.success(pushDiagnosticsClient.deviceLogs(appKey, userId, page, size)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/api/ops/push/test-offline")
|
@PostMapping("/api/ops/push/test-offline")
|
||||||
@PreAuthorize("hasAuthority('ROLE_OPS')")
|
@PreAuthorize("hasAuthority('ROLE_OPS')")
|
||||||
public ResponseEntity<ApiResponse<Map<String, Object>>> sendPushTestOffline(@RequestBody Map<String, String> body) {
|
public ResponseEntity<ApiResponse<Map<String, Object>>> sendPushTestOffline(@RequestBody Map<String, String> body) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(pushDiagnosticsClient.sendTestOffline(
|
return ResponseEntity.ok(ApiResponse.success(pushDiagnosticsClient.sendTestOffline(
|
||||||
body.get("appId"),
|
body.get("appKey"),
|
||||||
body.get("userId"),
|
body.get("userId"),
|
||||||
body.getOrDefault("title", "XuqmGroup Push 测试"),
|
body.getOrDefault("title", "XuqmGroup Push 测试"),
|
||||||
body.getOrDefault("body", "这是一条离线推送测试消息"),
|
body.getOrDefault("body", "这是一条离线推送测试消息"),
|
||||||
|
|||||||
@ -43,19 +43,19 @@ public class SdkConfigController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GET /api/sdk/config?appId=XXX&platform=ANDROID — public, no auth required.
|
* GET /api/sdk/config?appKey=XXX&platform=ANDROID — public, no auth required.
|
||||||
*
|
*
|
||||||
* Returns SDK configuration URLs and enabled feature flags for the given appId/appKey.
|
* 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.
|
* 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
|
* For update releases, the platform-specific UPDATE row drives both the enabled flag and
|
||||||
* the default release configuration.
|
* the default release configuration.
|
||||||
*/
|
*/
|
||||||
@GetMapping("/config")
|
@GetMapping("/config")
|
||||||
public ResponseEntity<ApiResponse<SdkConfigResponse>> getConfig(
|
public ResponseEntity<ApiResponse<SdkConfigResponse>> getConfig(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam(required = false, defaultValue = "ANDROID") FeatureServiceEntity.Platform platform) {
|
@RequestParam(required = false, defaultValue = "ANDROID") FeatureServiceEntity.Platform platform) {
|
||||||
|
|
||||||
AppEntity app = sdkAppProvisioningService.resolveApp(appId);
|
AppEntity app = sdkAppProvisioningService.resolveApp(appKey);
|
||||||
List<FeatureServiceEntity> features = featureServiceRepository.findByAppId(app.getAppKey());
|
List<FeatureServiceEntity> features = featureServiceRepository.findByAppId(app.getAppKey());
|
||||||
|
|
||||||
boolean imEnabled = features.stream()
|
boolean imEnabled = features.stream()
|
||||||
|
|||||||
@ -5,6 +5,8 @@ import jakarta.validation.constraints.Size;
|
|||||||
|
|
||||||
public record CreateAppRequest(
|
public record CreateAppRequest(
|
||||||
@NotBlank @Size(max = 128) String packageName,
|
@NotBlank @Size(max = 128) String packageName,
|
||||||
|
@Size(max = 128) String iosBundleId,
|
||||||
|
@Size(max = 128) String harmonyBundleName,
|
||||||
@NotBlank @Size(max = 128) String name,
|
@NotBlank @Size(max = 128) String name,
|
||||||
@Size(max = 512) String description,
|
@Size(max = 512) String description,
|
||||||
String iconUrl
|
String iconUrl
|
||||||
|
|||||||
@ -19,6 +19,12 @@ public class AppEntity {
|
|||||||
@Column(nullable = false, length = 128)
|
@Column(nullable = false, length = 128)
|
||||||
private String packageName;
|
private String packageName;
|
||||||
|
|
||||||
|
@Column(length = 128)
|
||||||
|
private String iosBundleId;
|
||||||
|
|
||||||
|
@Column(length = 128)
|
||||||
|
private String harmonyBundleName;
|
||||||
|
|
||||||
@Column(nullable = false, length = 128)
|
@Column(nullable = false, length = 128)
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
@ -46,6 +52,12 @@ public class AppEntity {
|
|||||||
public String getPackageName() { return packageName; }
|
public String getPackageName() { return packageName; }
|
||||||
public void setPackageName(String packageName) { this.packageName = packageName; }
|
public void setPackageName(String packageName) { this.packageName = packageName; }
|
||||||
|
|
||||||
|
public String getIosBundleId() { return iosBundleId; }
|
||||||
|
public void setIosBundleId(String iosBundleId) { this.iosBundleId = iosBundleId; }
|
||||||
|
|
||||||
|
public String getHarmonyBundleName() { return harmonyBundleName; }
|
||||||
|
public void setHarmonyBundleName(String harmonyBundleName) { this.harmonyBundleName = harmonyBundleName; }
|
||||||
|
|
||||||
public String getName() { return name; }
|
public String getName() { return name; }
|
||||||
public void setName(String name) { this.name = name; }
|
public void setName(String name) { this.name = name; }
|
||||||
|
|
||||||
|
|||||||
@ -47,6 +47,8 @@ public class AppService {
|
|||||||
app.setId(UUID.randomUUID().toString());
|
app.setId(UUID.randomUUID().toString());
|
||||||
app.setTenantId(tenantId);
|
app.setTenantId(tenantId);
|
||||||
app.setPackageName(req.packageName());
|
app.setPackageName(req.packageName());
|
||||||
|
app.setIosBundleId(req.iosBundleId());
|
||||||
|
app.setHarmonyBundleName(req.harmonyBundleName());
|
||||||
app.setName(req.name());
|
app.setName(req.name());
|
||||||
app.setDescription(req.description());
|
app.setDescription(req.description());
|
||||||
app.setIconUrl(req.iconUrl());
|
app.setIconUrl(req.iconUrl());
|
||||||
@ -69,6 +71,8 @@ public class AppService {
|
|||||||
before.put("packageName", app.getPackageName());
|
before.put("packageName", app.getPackageName());
|
||||||
before.put("description", app.getDescription());
|
before.put("description", app.getDescription());
|
||||||
before.put("iconUrl", app.getIconUrl());
|
before.put("iconUrl", app.getIconUrl());
|
||||||
|
app.setIosBundleId(req.iosBundleId());
|
||||||
|
app.setHarmonyBundleName(req.harmonyBundleName());
|
||||||
app.setName(req.name());
|
app.setName(req.name());
|
||||||
app.setDescription(req.description());
|
app.setDescription(req.description());
|
||||||
app.setIconUrl(req.iconUrl());
|
app.setIconUrl(req.iconUrl());
|
||||||
|
|||||||
@ -37,8 +37,8 @@ public class FeatureServiceManager {
|
|||||||
this.objectMapper = objectMapper;
|
this.objectMapper = objectMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FeatureServiceEntity> listByApp(String appId) {
|
public List<FeatureServiceEntity> listByApp(String appKey) {
|
||||||
List<FeatureServiceEntity> services = repository.findByAppId(appId);
|
List<FeatureServiceEntity> services = repository.findByAppId(appKey);
|
||||||
if (services.isEmpty()) {
|
if (services.isEmpty()) {
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
@ -62,13 +62,13 @@ public class FeatureServiceManager {
|
|||||||
*/
|
*/
|
||||||
@Transactional
|
@Transactional
|
||||||
public ServiceActivationRequestEntity submitActivationRequest(
|
public ServiceActivationRequestEntity submitActivationRequest(
|
||||||
String appId,
|
String appKey,
|
||||||
FeatureServiceEntity.Platform platform,
|
FeatureServiceEntity.Platform platform,
|
||||||
FeatureServiceEntity.ServiceType serviceType,
|
FeatureServiceEntity.ServiceType serviceType,
|
||||||
String applyReason) {
|
String applyReason) {
|
||||||
|
|
||||||
if (isAppWideService(serviceType)) {
|
if (isAppWideService(serviceType)) {
|
||||||
requestRepository.findFirstByAppIdAndServiceTypeOrderByCreatedAtDesc(appId, serviceType)
|
requestRepository.findFirstByAppIdAndServiceTypeOrderByCreatedAtDesc(appKey, serviceType)
|
||||||
.ifPresent(req -> {
|
.ifPresent(req -> {
|
||||||
if (req.getStatus() == Status.PENDING) {
|
if (req.getStatus() == Status.PENDING) {
|
||||||
throw new BusinessException(400, "已有待审核的开通申请,请等待运营人员处理");
|
throw new BusinessException(400, "已有待审核的开通申请,请等待运营人员处理");
|
||||||
@ -78,7 +78,7 @@ public class FeatureServiceManager {
|
|||||||
|
|
||||||
ServiceActivationRequestEntity req = new ServiceActivationRequestEntity();
|
ServiceActivationRequestEntity req = new ServiceActivationRequestEntity();
|
||||||
req.setId(UUID.randomUUID().toString());
|
req.setId(UUID.randomUUID().toString());
|
||||||
req.setAppId(appId);
|
req.setAppId(appKey);
|
||||||
req.setPlatform(platform);
|
req.setPlatform(platform);
|
||||||
req.setServiceType(serviceType);
|
req.setServiceType(serviceType);
|
||||||
req.setStatus(Status.PENDING);
|
req.setStatus(Status.PENDING);
|
||||||
@ -91,10 +91,10 @@ public class FeatureServiceManager {
|
|||||||
* Disable a service immediately (no approval needed).
|
* Disable a service immediately (no approval needed).
|
||||||
*/
|
*/
|
||||||
@Transactional
|
@Transactional
|
||||||
public FeatureServiceEntity disable(String appId, FeatureServiceEntity.Platform platform,
|
public FeatureServiceEntity disable(String appKey, FeatureServiceEntity.Platform platform,
|
||||||
FeatureServiceEntity.ServiceType serviceType) {
|
FeatureServiceEntity.ServiceType serviceType) {
|
||||||
if (isAppWideService(serviceType)) {
|
if (isAppWideService(serviceType)) {
|
||||||
List<FeatureServiceEntity> services = repository.findByAppIdAndServiceType(appId, serviceType);
|
List<FeatureServiceEntity> services = repository.findByAppIdAndServiceType(appKey, serviceType);
|
||||||
if (services.isEmpty()) {
|
if (services.isEmpty()) {
|
||||||
throw new BusinessException(404, "服务未开通");
|
throw new BusinessException(404, "服务未开通");
|
||||||
}
|
}
|
||||||
@ -104,7 +104,7 @@ public class FeatureServiceManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FeatureServiceEntity entity = repository
|
FeatureServiceEntity entity = repository
|
||||||
.findByAppIdAndPlatformAndServiceType(appId, platform, serviceType)
|
.findByAppIdAndPlatformAndServiceType(appKey, platform, serviceType)
|
||||||
.orElseThrow(() -> new BusinessException(404, "服务未开通"));
|
.orElseThrow(() -> new BusinessException(404, "服务未开通"));
|
||||||
entity.setEnabled(false);
|
entity.setEnabled(false);
|
||||||
return repository.save(entity);
|
return repository.save(entity);
|
||||||
@ -182,29 +182,29 @@ public class FeatureServiceManager {
|
|||||||
return requestRepository.save(req);
|
return requestRepository.save(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ServiceActivationRequestEntity> listRequestsByApp(String appId) {
|
public List<ServiceActivationRequestEntity> listRequestsByApp(String appKey) {
|
||||||
return requestRepository.findByAppIdOrderByCreatedAtDesc(appId);
|
return requestRepository.findByAppIdOrderByCreatedAtDesc(appKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FeatureServiceEntity getOrFail(String appId, FeatureServiceEntity.Platform platform,
|
public FeatureServiceEntity getOrFail(String appKey, FeatureServiceEntity.Platform platform,
|
||||||
FeatureServiceEntity.ServiceType serviceType) {
|
FeatureServiceEntity.ServiceType serviceType) {
|
||||||
if (serviceType == FeatureServiceEntity.ServiceType.IM) {
|
if (serviceType == FeatureServiceEntity.ServiceType.IM) {
|
||||||
return repository.findByAppIdAndServiceType(appId, serviceType)
|
return repository.findByAppIdAndServiceType(appKey, serviceType)
|
||||||
.stream()
|
.stream()
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElseThrow(() -> new BusinessException(404, "服务未配置"));
|
.orElseThrow(() -> new BusinessException(404, "服务未配置"));
|
||||||
}
|
}
|
||||||
return repository.findByAppIdAndPlatformAndServiceType(appId, platform, serviceType)
|
return repository.findByAppIdAndPlatformAndServiceType(appKey, platform, serviceType)
|
||||||
.orElseThrow(() -> new BusinessException(404, "服务未配置"));
|
.orElseThrow(() -> new BusinessException(404, "服务未配置"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public FeatureServiceEntity updateConfig(String appId,
|
public FeatureServiceEntity updateConfig(String appKey,
|
||||||
FeatureServiceEntity.Platform platform,
|
FeatureServiceEntity.Platform platform,
|
||||||
FeatureServiceEntity.ServiceType serviceType,
|
FeatureServiceEntity.ServiceType serviceType,
|
||||||
String config) {
|
String config) {
|
||||||
if (serviceType == FeatureServiceEntity.ServiceType.IM) {
|
if (serviceType == FeatureServiceEntity.ServiceType.IM) {
|
||||||
List<FeatureServiceEntity> services = repository.findByAppIdAndServiceType(appId, serviceType);
|
List<FeatureServiceEntity> services = repository.findByAppIdAndServiceType(appKey, serviceType);
|
||||||
if (services.isEmpty()) {
|
if (services.isEmpty()) {
|
||||||
throw new BusinessException(404, "服务未配置");
|
throw new BusinessException(404, "服务未配置");
|
||||||
}
|
}
|
||||||
@ -213,73 +213,73 @@ public class FeatureServiceManager {
|
|||||||
return services.get(0);
|
return services.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
FeatureServiceEntity entity = getOrFail(appId, platform, serviceType);
|
FeatureServiceEntity entity = getOrFail(appKey, platform, serviceType);
|
||||||
entity.setConfig(config);
|
entity.setConfig(config);
|
||||||
return repository.save(entity);
|
return repository.save(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FeatureServiceEntity getByPlatform(String appId,
|
public FeatureServiceEntity getByPlatform(String appKey,
|
||||||
FeatureServiceEntity.Platform platform,
|
FeatureServiceEntity.Platform platform,
|
||||||
FeatureServiceEntity.ServiceType serviceType) {
|
FeatureServiceEntity.ServiceType serviceType) {
|
||||||
return getOrFail(appId, platform, serviceType);
|
return getOrFail(appKey, platform, serviceType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean allowStrangerMessage(String appId,
|
public boolean allowStrangerMessage(String appKey,
|
||||||
FeatureServiceEntity.Platform platform,
|
FeatureServiceEntity.Platform platform,
|
||||||
FeatureServiceEntity.ServiceType serviceType) {
|
FeatureServiceEntity.ServiceType serviceType) {
|
||||||
return readConfigNode(appId, platform, serviceType).path("allowStrangerMessage").asBoolean(false);
|
return readConfigNode(appKey, platform, serviceType).path("allowStrangerMessage").asBoolean(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean allowFriendRequest(String appId,
|
public boolean allowFriendRequest(String appKey,
|
||||||
FeatureServiceEntity.Platform platform,
|
FeatureServiceEntity.Platform platform,
|
||||||
FeatureServiceEntity.ServiceType serviceType) {
|
FeatureServiceEntity.ServiceType serviceType) {
|
||||||
return readConfigNode(appId, platform, serviceType).path("allowFriendRequest").asBoolean(true);
|
return readConfigNode(appKey, platform, serviceType).path("allowFriendRequest").asBoolean(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean allowGroupJoinRequest(String appId,
|
public boolean allowGroupJoinRequest(String appKey,
|
||||||
FeatureServiceEntity.Platform platform,
|
FeatureServiceEntity.Platform platform,
|
||||||
FeatureServiceEntity.ServiceType serviceType) {
|
FeatureServiceEntity.ServiceType serviceType) {
|
||||||
return readConfigNode(appId, platform, serviceType).path("allowGroupJoinRequest").asBoolean(true);
|
return readConfigNode(appKey, platform, serviceType).path("allowGroupJoinRequest").asBoolean(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String friendRequestMode(String appId,
|
public String friendRequestMode(String appKey,
|
||||||
FeatureServiceEntity.Platform platform,
|
FeatureServiceEntity.Platform platform,
|
||||||
FeatureServiceEntity.ServiceType serviceType) {
|
FeatureServiceEntity.ServiceType serviceType) {
|
||||||
String mode = readConfigNode(appId, platform, serviceType).path("friendRequestMode").asText("");
|
String mode = readConfigNode(appKey, platform, serviceType).path("friendRequestMode").asText("");
|
||||||
return switch (mode == null ? "" : mode.trim().toUpperCase()) {
|
return switch (mode == null ? "" : mode.trim().toUpperCase()) {
|
||||||
case "DIRECT_ACCEPT", "DISALLOW" -> mode.trim().toUpperCase();
|
case "DIRECT_ACCEPT", "DISALLOW" -> mode.trim().toUpperCase();
|
||||||
default -> "REQUIRE_CONFIRM";
|
default -> "REQUIRE_CONFIRM";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean blacklistSendSuccess(String appId,
|
public boolean blacklistSendSuccess(String appKey,
|
||||||
FeatureServiceEntity.Platform platform,
|
FeatureServiceEntity.Platform platform,
|
||||||
FeatureServiceEntity.ServiceType serviceType) {
|
FeatureServiceEntity.ServiceType serviceType) {
|
||||||
return readConfigNode(appId, platform, serviceType).path("blacklistSendSuccess").asBoolean(true);
|
return readConfigNode(appKey, platform, serviceType).path("blacklistSendSuccess").asBoolean(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int messageRecallMinutes(String appId,
|
public int messageRecallMinutes(String appKey,
|
||||||
FeatureServiceEntity.Platform platform,
|
FeatureServiceEntity.Platform platform,
|
||||||
FeatureServiceEntity.ServiceType serviceType) {
|
FeatureServiceEntity.ServiceType serviceType) {
|
||||||
int minutes = readConfigNode(appId, platform, serviceType).path("messageRecallMinutes").asInt(2);
|
int minutes = readConfigNode(appKey, platform, serviceType).path("messageRecallMinutes").asInt(2);
|
||||||
return Math.max(minutes, 0);
|
return Math.max(minutes, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int conversationPullLimit(String appId,
|
public int conversationPullLimit(String appKey,
|
||||||
FeatureServiceEntity.Platform platform,
|
FeatureServiceEntity.Platform platform,
|
||||||
FeatureServiceEntity.ServiceType serviceType) {
|
FeatureServiceEntity.ServiceType serviceType) {
|
||||||
int limit = readConfigNode(appId, platform, serviceType).path("conversationPullLimit").asInt(100);
|
int limit = readConfigNode(appKey, platform, serviceType).path("conversationPullLimit").asInt(100);
|
||||||
return Math.min(Math.max(limit, 1), 500);
|
return Math.min(Math.max(limit, 1), 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int historyRetentionDays(String appId,
|
public int historyRetentionDays(String appKey,
|
||||||
FeatureServiceEntity.Platform platform,
|
FeatureServiceEntity.Platform platform,
|
||||||
FeatureServiceEntity.ServiceType serviceType) {
|
FeatureServiceEntity.ServiceType serviceType) {
|
||||||
int days = readConfigNode(appId, platform, serviceType).path("historyRetentionDays").asInt(7);
|
int days = readConfigNode(appKey, platform, serviceType).path("historyRetentionDays").asInt(7);
|
||||||
return Math.max(days, 1);
|
return Math.max(days, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String buildImConfig(String appId,
|
public String buildImConfig(String appKey,
|
||||||
FeatureServiceEntity.Platform platform,
|
FeatureServiceEntity.Platform platform,
|
||||||
Boolean allowStrangerMessage,
|
Boolean allowStrangerMessage,
|
||||||
Boolean allowFriendRequest,
|
Boolean allowFriendRequest,
|
||||||
@ -291,7 +291,7 @@ public class FeatureServiceManager {
|
|||||||
Integer conversationPullLimit,
|
Integer conversationPullLimit,
|
||||||
Boolean allowMultiDeviceLogin,
|
Boolean allowMultiDeviceLogin,
|
||||||
Boolean multiClientConversationDeleteSync) {
|
Boolean multiClientConversationDeleteSync) {
|
||||||
ObjectNode node = readConfigNode(appId, platform, FeatureServiceEntity.ServiceType.IM).deepCopy();
|
ObjectNode node = readConfigNode(appKey, platform, FeatureServiceEntity.ServiceType.IM).deepCopy();
|
||||||
if (!node.has("allowStrangerMessage")) {
|
if (!node.has("allowStrangerMessage")) {
|
||||||
node.put("allowStrangerMessage", false);
|
node.put("allowStrangerMessage", false);
|
||||||
}
|
}
|
||||||
@ -379,7 +379,7 @@ public class FeatureServiceManager {
|
|||||||
return node.toString();
|
return node.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String buildUpdateConfig(String appId,
|
public String buildUpdateConfig(String appKey,
|
||||||
FeatureServiceEntity.Platform platform,
|
FeatureServiceEntity.Platform platform,
|
||||||
List<String> defaultStoreTargets,
|
List<String> defaultStoreTargets,
|
||||||
String defaultPublishMode,
|
String defaultPublishMode,
|
||||||
@ -393,7 +393,7 @@ public class FeatureServiceManager {
|
|||||||
String defaultPackageName,
|
String defaultPackageName,
|
||||||
String defaultAppStoreUrl,
|
String defaultAppStoreUrl,
|
||||||
String defaultMarketUrl) {
|
String defaultMarketUrl) {
|
||||||
ObjectNode node = readConfigNode(appId, platform, FeatureServiceEntity.ServiceType.UPDATE).deepCopy();
|
ObjectNode node = readConfigNode(appKey, platform, FeatureServiceEntity.ServiceType.UPDATE).deepCopy();
|
||||||
if (!node.has("defaultStoreTargets")) {
|
if (!node.has("defaultStoreTargets")) {
|
||||||
node.putArray("defaultStoreTargets");
|
node.putArray("defaultStoreTargets");
|
||||||
}
|
}
|
||||||
@ -475,19 +475,24 @@ public class FeatureServiceManager {
|
|||||||
return node.toString();
|
return node.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String buildPushConfig(String appId,
|
public String buildPushConfig(String appKey,
|
||||||
FeatureServiceEntity.Platform platform,
|
FeatureServiceEntity.Platform platform,
|
||||||
String huaweiAppId,
|
String huaweiAppId,
|
||||||
String huaweiAppSecret,
|
String huaweiAppSecret,
|
||||||
|
String huaweiCategory,
|
||||||
String xiaomiAppId,
|
String xiaomiAppId,
|
||||||
String xiaomiAppKey,
|
String xiaomiAppKey,
|
||||||
String xiaomiAppSecret,
|
String xiaomiAppSecret,
|
||||||
|
String xiaomiChannelId,
|
||||||
String oppoAppId,
|
String oppoAppId,
|
||||||
String oppoAppKey,
|
String oppoAppKey,
|
||||||
String oppoMasterSecret,
|
String oppoMasterSecret,
|
||||||
|
String oppoChannelId,
|
||||||
String vivoAppId,
|
String vivoAppId,
|
||||||
String vivoAppKey,
|
String vivoAppKey,
|
||||||
String vivoAppSecret,
|
String vivoAppSecret,
|
||||||
|
String vivoCategory,
|
||||||
|
String vivoReceiptId,
|
||||||
String honorAppId,
|
String honorAppId,
|
||||||
String honorClientId,
|
String honorClientId,
|
||||||
String honorClientSecret,
|
String honorClientSecret,
|
||||||
@ -501,7 +506,7 @@ public class FeatureServiceManager {
|
|||||||
String fcmServiceAccountJson,
|
String fcmServiceAccountJson,
|
||||||
JsonNode channels,
|
JsonNode channels,
|
||||||
JsonNode routing) {
|
JsonNode routing) {
|
||||||
ObjectNode node = readConfigNode(appId, platform, FeatureServiceEntity.ServiceType.PUSH).deepCopy();
|
ObjectNode node = readConfigNode(appKey, platform, FeatureServiceEntity.ServiceType.PUSH).deepCopy();
|
||||||
ensureObjectNode(node, "huawei");
|
ensureObjectNode(node, "huawei");
|
||||||
ensureObjectNode(node, "xiaomi");
|
ensureObjectNode(node, "xiaomi");
|
||||||
ensureObjectNode(node, "oppo");
|
ensureObjectNode(node, "oppo");
|
||||||
@ -513,18 +518,23 @@ public class FeatureServiceManager {
|
|||||||
|
|
||||||
putText(node.with("huawei"), "appId", huaweiAppId);
|
putText(node.with("huawei"), "appId", huaweiAppId);
|
||||||
putText(node.with("huawei"), "appSecret", huaweiAppSecret);
|
putText(node.with("huawei"), "appSecret", huaweiAppSecret);
|
||||||
|
putText(node.with("huawei"), "category", huaweiCategory);
|
||||||
|
|
||||||
putText(node.with("xiaomi"), "appId", xiaomiAppId);
|
putText(node.with("xiaomi"), "appId", xiaomiAppId);
|
||||||
putText(node.with("xiaomi"), "appKey", xiaomiAppKey);
|
putText(node.with("xiaomi"), "appKey", xiaomiAppKey);
|
||||||
putText(node.with("xiaomi"), "appSecret", xiaomiAppSecret);
|
putText(node.with("xiaomi"), "appSecret", xiaomiAppSecret);
|
||||||
|
putText(node.with("xiaomi"), "channelId", xiaomiChannelId);
|
||||||
|
|
||||||
putText(node.with("oppo"), "appId", oppoAppId);
|
putText(node.with("oppo"), "appId", oppoAppId);
|
||||||
putText(node.with("oppo"), "appKey", oppoAppKey);
|
putText(node.with("oppo"), "appKey", oppoAppKey);
|
||||||
putText(node.with("oppo"), "masterSecret", oppoMasterSecret);
|
putText(node.with("oppo"), "masterSecret", oppoMasterSecret);
|
||||||
|
putText(node.with("oppo"), "channelId", oppoChannelId);
|
||||||
|
|
||||||
putText(node.with("vivo"), "appId", vivoAppId);
|
putText(node.with("vivo"), "appId", vivoAppId);
|
||||||
putText(node.with("vivo"), "appKey", vivoAppKey);
|
putText(node.with("vivo"), "appKey", vivoAppKey);
|
||||||
putText(node.with("vivo"), "appSecret", vivoAppSecret);
|
putText(node.with("vivo"), "appSecret", vivoAppSecret);
|
||||||
|
putText(node.with("vivo"), "category", vivoCategory);
|
||||||
|
putText(node.with("vivo"), "receiptId", vivoReceiptId);
|
||||||
|
|
||||||
putText(node.with("honor"), "appId", honorAppId);
|
putText(node.with("honor"), "appId", honorAppId);
|
||||||
putText(node.with("honor"), "clientId", honorClientId);
|
putText(node.with("honor"), "clientId", honorClientId);
|
||||||
@ -634,17 +644,17 @@ public class FeatureServiceManager {
|
|||||||
|| serviceType == FeatureServiceEntity.ServiceType.UPDATE;
|
|| serviceType == FeatureServiceEntity.ServiceType.UPDATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private JsonNode readConfigNode(String appId,
|
private JsonNode readConfigNode(String appKey,
|
||||||
FeatureServiceEntity.Platform platform,
|
FeatureServiceEntity.Platform platform,
|
||||||
FeatureServiceEntity.ServiceType serviceType) {
|
FeatureServiceEntity.ServiceType serviceType) {
|
||||||
FeatureServiceEntity entity;
|
FeatureServiceEntity entity;
|
||||||
if (serviceType == FeatureServiceEntity.ServiceType.IM) {
|
if (serviceType == FeatureServiceEntity.ServiceType.IM) {
|
||||||
entity = repository.findByAppIdAndServiceType(appId, serviceType)
|
entity = repository.findByAppIdAndServiceType(appKey, serviceType)
|
||||||
.stream()
|
.stream()
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
} else {
|
} else {
|
||||||
entity = repository.findByAppIdAndPlatformAndServiceType(appId, platform, serviceType)
|
entity = repository.findByAppIdAndPlatformAndServiceType(appKey, platform, serviceType)
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
if (entity == null || entity.getConfig() == null || entity.getConfig().isBlank()) {
|
if (entity == null || entity.getConfig() == null || entity.getConfig().isBlank()) {
|
||||||
|
|||||||
@ -30,18 +30,18 @@ public class OpsPushDiagnosticsClient {
|
|||||||
this.objectMapper = objectMapper;
|
this.objectMapper = objectMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Object> searchByToken(String token, String appId) {
|
public Map<String, Object> searchByToken(String token, String appKey) {
|
||||||
String uri = UriComponentsBuilder.fromHttpUrl(pushServiceBaseUrl + "/api/push/internal/diagnostics/search")
|
String uri = UriComponentsBuilder.fromHttpUrl(pushServiceBaseUrl + "/api/push/internal/diagnostics/search")
|
||||||
.queryParam("queryToken", token)
|
.queryParam("queryToken", token)
|
||||||
.queryParamIfPresent("appId", appId == null || appId.isBlank() ? java.util.Optional.empty() : java.util.Optional.of(appId))
|
.queryParamIfPresent("appKey", appKey == null || appKey.isBlank() ? java.util.Optional.empty() : java.util.Optional.of(appKey))
|
||||||
.build()
|
.build()
|
||||||
.toUriString();
|
.toUriString();
|
||||||
return dataMap(exchange(uri));
|
return dataMap(exchange(uri));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Object> deviceLogs(String appId, String userId, int page, int size) {
|
public Map<String, Object> deviceLogs(String appKey, String userId, int page, int size) {
|
||||||
String uri = UriComponentsBuilder.fromHttpUrl(pushServiceBaseUrl + "/api/push/internal/device-logs")
|
String uri = UriComponentsBuilder.fromHttpUrl(pushServiceBaseUrl + "/api/push/internal/device-logs")
|
||||||
.queryParam("appId", appId)
|
.queryParam("appKey", appKey)
|
||||||
.queryParam("userId", userId)
|
.queryParam("userId", userId)
|
||||||
.queryParam("page", page)
|
.queryParam("page", page)
|
||||||
.queryParam("size", size)
|
.queryParam("size", size)
|
||||||
@ -50,13 +50,13 @@ public class OpsPushDiagnosticsClient {
|
|||||||
return dataMap(exchange(uri));
|
return dataMap(exchange(uri));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Object> sendTestOffline(String appId, String userId, String title, String body, String payload) {
|
public Map<String, Object> sendTestOffline(String appKey, String userId, String title, String body, String payload) {
|
||||||
String uri = pushServiceBaseUrl + "/api/push/internal/test-offline";
|
String uri = pushServiceBaseUrl + "/api/push/internal/test-offline";
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
headers.set("X-Internal-Token", internalToken);
|
headers.set("X-Internal-Token", internalToken);
|
||||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
Map<String, Object> request = new java.util.LinkedHashMap<>();
|
Map<String, Object> request = new java.util.LinkedHashMap<>();
|
||||||
request.put("appId", appId);
|
request.put("appKey", appKey);
|
||||||
request.put("userId", userId);
|
request.put("userId", userId);
|
||||||
request.put("title", title);
|
request.put("title", title);
|
||||||
request.put("body", body);
|
request.put("body", body);
|
||||||
|
|||||||
@ -163,10 +163,10 @@ public class OpsService {
|
|||||||
return appRepository.findByNameContainingIgnoreCaseOrAppKeyContainingIgnoreCase(keyword, keyword, pageable);
|
return appRepository.findByNameContainingIgnoreCaseOrAppKeyContainingIgnoreCase(keyword, keyword, pageable);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Object> getAppDetail(String appId) {
|
public Map<String, Object> getAppDetail(String appKey) {
|
||||||
AppEntity app = appRepository.findById(appId)
|
AppEntity app = appRepository.findById(appKey)
|
||||||
.orElseThrow(() -> new IllegalArgumentException("应用不存在"));
|
.orElseThrow(() -> new IllegalArgumentException("应用不存在"));
|
||||||
List<FeatureServiceEntity> services = featureServiceRepository.findByAppId(appId);
|
List<FeatureServiceEntity> services = featureServiceRepository.findByAppId(appKey);
|
||||||
long enabledCount = services.stream().filter(FeatureServiceEntity::isEnabled).count();
|
long enabledCount = services.stream().filter(FeatureServiceEntity::isEnabled).count();
|
||||||
Map<String, Object> result = new LinkedHashMap<>();
|
Map<String, Object> result = new LinkedHashMap<>();
|
||||||
result.put("app", app);
|
result.put("app", app);
|
||||||
@ -177,10 +177,10 @@ public class OpsService {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FeatureServiceEntity> listAppServices(String appId) {
|
public List<FeatureServiceEntity> listAppServices(String appKey) {
|
||||||
appRepository.findById(appId)
|
appRepository.findById(appKey)
|
||||||
.orElseThrow(() -> new IllegalArgumentException("应用不存在"));
|
.orElseThrow(() -> new IllegalArgumentException("应用不存在"));
|
||||||
return featureServiceRepository.findByAppId(appId);
|
return featureServiceRepository.findByAppId(appKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Page<OperationLogEntity> listOperationLogs(int page, int size) {
|
public Page<OperationLogEntity> listOperationLogs(int page, int size) {
|
||||||
|
|||||||
@ -56,14 +56,14 @@ public class SdkAppProvisioningService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public AppEntity resolveApp(String appId) {
|
public AppEntity resolveApp(String appKey) {
|
||||||
return appRepository.findByAppKey(appId)
|
return appRepository.findByAppKey(appKey)
|
||||||
.or(() -> appRepository.findById(appId))
|
.or(() -> appRepository.findById(appKey))
|
||||||
.orElseGet(() -> {
|
.orElseGet(() -> {
|
||||||
if (!bootstrapAppKey.equals(appId)) {
|
if (!bootstrapAppKey.equals(appKey)) {
|
||||||
throw new BusinessException(404, "App not found: " + appId);
|
throw new BusinessException(404, "App not found: " + appKey);
|
||||||
}
|
}
|
||||||
return ensureApp(appId, true);
|
return ensureApp(appKey, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
server:
|
server:
|
||||||
port: 8081
|
port: 9001
|
||||||
|
|
||||||
spring:
|
spring:
|
||||||
application:
|
application:
|
||||||
|
|||||||
@ -41,8 +41,8 @@ public class AppStoreController {
|
|||||||
// ── Store credential config ──────────────────────────────────────────────
|
// ── Store credential config ──────────────────────────────────────────────
|
||||||
|
|
||||||
@GetMapping("/configs")
|
@GetMapping("/configs")
|
||||||
public ResponseEntity<ApiResponse<List<AppStoreConfigEntity>>> getConfigs(@RequestParam String appId) {
|
public ResponseEntity<ApiResponse<List<AppStoreConfigEntity>>> getConfigs(@RequestParam String appKey) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(storeService.getConfigs(appId)));
|
return ResponseEntity.ok(ApiResponse.success(storeService.getConfigs(appKey)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,7 +52,7 @@ public class AppStoreController {
|
|||||||
*/
|
*/
|
||||||
@PutMapping("/configs/{storeType}")
|
@PutMapping("/configs/{storeType}")
|
||||||
public ResponseEntity<ApiResponse<AppStoreConfigEntity>> saveConfig(
|
public ResponseEntity<ApiResponse<AppStoreConfigEntity>> saveConfig(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable AppStoreConfigEntity.StoreType storeType,
|
@PathVariable AppStoreConfigEntity.StoreType storeType,
|
||||||
@RequestBody Map<String, Object> body) {
|
@RequestBody Map<String, Object> body) {
|
||||||
|
|
||||||
@ -60,24 +60,24 @@ public class AppStoreController {
|
|||||||
boolean enabled = !Boolean.FALSE.equals(body.get("enabled"));
|
boolean enabled = !Boolean.FALSE.equals(body.get("enabled"));
|
||||||
validateStoreConfig(storeType, configJson);
|
validateStoreConfig(storeType, configJson);
|
||||||
return ResponseEntity.ok(ApiResponse.success(
|
return ResponseEntity.ok(ApiResponse.success(
|
||||||
storeService.saveConfig(appId, storeType, configJson, enabled)));
|
storeService.saveConfig(appKey, storeType, configJson, enabled)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/configs/{storeType}")
|
@DeleteMapping("/configs/{storeType}")
|
||||||
public ResponseEntity<ApiResponse<Void>> deleteConfig(
|
public ResponseEntity<ApiResponse<Void>> deleteConfig(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@PathVariable AppStoreConfigEntity.StoreType storeType) {
|
@PathVariable AppStoreConfigEntity.StoreType storeType) {
|
||||||
storeService.deleteConfig(appId, storeType);
|
storeService.deleteConfig(appKey, storeType);
|
||||||
return ResponseEntity.ok(ApiResponse.success(null));
|
return ResponseEntity.ok(ApiResponse.success(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns enabled store credentials for the given appId.
|
* Returns enabled store credentials for the given appKey.
|
||||||
* Called by the release script so it can submit to stores without storing secrets locally.
|
* Called by the release script so it can submit to stores without storing secrets locally.
|
||||||
*/
|
*/
|
||||||
@GetMapping("/credentials")
|
@GetMapping("/credentials")
|
||||||
public ResponseEntity<ApiResponse<Map<String, Object>>> getCredentials(@RequestParam String appId) throws Exception {
|
public ResponseEntity<ApiResponse<Map<String, Object>>> getCredentials(@RequestParam String appKey) throws Exception {
|
||||||
return ResponseEntity.ok(ApiResponse.success(storeService.getStoreCredentials(appId)));
|
return ResponseEntity.ok(ApiResponse.success(storeService.getStoreCredentials(appKey)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Version store submission ──────────────────────────────────────────────
|
// ── Version store submission ──────────────────────────────────────────────
|
||||||
|
|||||||
@ -47,17 +47,17 @@ public class AppVersionController {
|
|||||||
|
|
||||||
@GetMapping("/app/check")
|
@GetMapping("/app/check")
|
||||||
public ResponseEntity<ApiResponse<Map<String, Object>>> checkUpdate(
|
public ResponseEntity<ApiResponse<Map<String, Object>>> checkUpdate(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam AppVersionEntity.Platform platform,
|
@RequestParam AppVersionEntity.Platform platform,
|
||||||
@RequestParam int currentVersionCode,
|
@RequestParam int currentVersionCode,
|
||||||
@RequestParam(required = false) String userId) {
|
@RequestParam(required = false) String userId) {
|
||||||
|
|
||||||
Optional<AppVersionEntity> latest = versionRepository
|
Optional<AppVersionEntity> latest = versionRepository
|
||||||
.findTopByAppIdAndPlatformAndPublishStatusAndVersionCodeGreaterThanOrderByVersionCodeDesc(
|
.findTopByAppIdAndPlatformAndPublishStatusAndVersionCodeGreaterThanOrderByVersionCodeDesc(
|
||||||
appId, platform, AppVersionEntity.PublishStatus.PUBLISHED, currentVersionCode);
|
appKey, platform, AppVersionEntity.PublishStatus.PUBLISHED, currentVersionCode);
|
||||||
Optional<AppVersionEntity> forcedHigher = versionRepository
|
Optional<AppVersionEntity> forcedHigher = versionRepository
|
||||||
.findTopByAppIdAndPlatformAndPublishStatusAndVersionCodeGreaterThanAndForceUpdateTrueOrderByVersionCodeDesc(
|
.findTopByAppIdAndPlatformAndPublishStatusAndVersionCodeGreaterThanAndForceUpdateTrueOrderByVersionCodeDesc(
|
||||||
appId, platform, AppVersionEntity.PublishStatus.PUBLISHED, currentVersionCode);
|
appKey, platform, AppVersionEntity.PublishStatus.PUBLISHED, currentVersionCode);
|
||||||
|
|
||||||
if (latest.isEmpty()) {
|
if (latest.isEmpty()) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(Map.of("needsUpdate", false)));
|
return ResponseEntity.ok(ApiResponse.success(Map.of("needsUpdate", false)));
|
||||||
@ -82,10 +82,10 @@ public class AppVersionController {
|
|||||||
|
|
||||||
String appStoreJumpUrl = hasText(v.getAppStoreUrl())
|
String appStoreJumpUrl = hasText(v.getAppStoreUrl())
|
||||||
? v.getAppStoreUrl()
|
? v.getAppStoreUrl()
|
||||||
: appStoreService.getStoreJumpUrl(appId, com.xuqm.update.entity.AppStoreConfigEntity.StoreType.APP_STORE);
|
: appStoreService.getStoreJumpUrl(appKey, com.xuqm.update.entity.AppStoreConfigEntity.StoreType.APP_STORE);
|
||||||
String harmonyJumpUrl = hasText(v.getMarketUrl())
|
String harmonyJumpUrl = hasText(v.getMarketUrl())
|
||||||
? v.getMarketUrl()
|
? v.getMarketUrl()
|
||||||
: appStoreService.getStoreJumpUrl(appId, com.xuqm.update.entity.AppStoreConfigEntity.StoreType.HARMONY_APP);
|
: appStoreService.getStoreJumpUrl(appKey, com.xuqm.update.entity.AppStoreConfigEntity.StoreType.HARMONY_APP);
|
||||||
return ResponseEntity.ok(ApiResponse.success(Map.of(
|
return ResponseEntity.ok(ApiResponse.success(Map.of(
|
||||||
"needsUpdate", true,
|
"needsUpdate", true,
|
||||||
"versionName", v.getVersionName(),
|
"versionName", v.getVersionName(),
|
||||||
@ -100,7 +100,7 @@ public class AppVersionController {
|
|||||||
|
|
||||||
@PostMapping("/app/upload")
|
@PostMapping("/app/upload")
|
||||||
public ResponseEntity<ApiResponse<AppVersionEntity>> upload(
|
public ResponseEntity<ApiResponse<AppVersionEntity>> upload(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam AppVersionEntity.Platform platform,
|
@RequestParam AppVersionEntity.Platform platform,
|
||||||
@RequestParam(required = false) String versionName,
|
@RequestParam(required = false) String versionName,
|
||||||
@RequestParam(required = false) Integer versionCode,
|
@RequestParam(required = false) Integer versionCode,
|
||||||
@ -124,8 +124,8 @@ public class AppVersionController {
|
|||||||
? updateAssetService.inspectAppPackage(apkUrl)
|
? updateAssetService.inspectAppPackage(apkUrl)
|
||||||
: (apkFile != null && !apkFile.isEmpty() ? updateAssetService.inspectAppPackage(apkFile) : null);
|
: (apkFile != null && !apkFile.isEmpty() ? updateAssetService.inspectAppPackage(apkFile) : null);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
log.warn("Unable to inspect upload package for appId={}, platform={}, source={}, fallback to manual version fields: {}",
|
log.warn("Unable to inspect upload package for appKey={}, platform={}, source={}, fallback to manual version fields: {}",
|
||||||
appId, platform, hasText(apkUrl) ? apkUrl : (apkFile != null ? apkFile.getOriginalFilename() : null), ex.getMessage());
|
appKey, platform, hasText(apkUrl) ? apkUrl : (apkFile != null ? apkFile.getOriginalFilename() : null), ex.getMessage());
|
||||||
}
|
}
|
||||||
String resolvedVersionName = hasText(versionName) ? versionName : (inspected != null ? inspected.versionName() : null);
|
String resolvedVersionName = hasText(versionName) ? versionName : (inspected != null ? inspected.versionName() : null);
|
||||||
Integer resolvedVersionCode = versionCode != null ? versionCode : (inspected != null ? inspected.versionCode() : null);
|
Integer resolvedVersionCode = versionCode != null ? versionCode : (inspected != null ? inspected.versionCode() : null);
|
||||||
@ -141,7 +141,7 @@ public class AppVersionController {
|
|||||||
}
|
}
|
||||||
AppVersionEntity entity = new AppVersionEntity();
|
AppVersionEntity entity = new AppVersionEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appId);
|
entity.setAppId(appKey);
|
||||||
entity.setPlatform(platform);
|
entity.setPlatform(platform);
|
||||||
entity.setVersionName(resolvedVersionName);
|
entity.setVersionName(resolvedVersionName);
|
||||||
entity.setVersionCode(resolvedVersionCode);
|
entity.setVersionCode(resolvedVersionCode);
|
||||||
@ -170,12 +170,12 @@ public class AppVersionController {
|
|||||||
entity.setAppStoreUrl(hasText(appStoreUrl)
|
entity.setAppStoreUrl(hasText(appStoreUrl)
|
||||||
? appStoreUrl
|
? appStoreUrl
|
||||||
: (platform == AppVersionEntity.Platform.IOS
|
: (platform == AppVersionEntity.Platform.IOS
|
||||||
? appStoreService.getStoreJumpUrl(appId, com.xuqm.update.entity.AppStoreConfigEntity.StoreType.APP_STORE)
|
? appStoreService.getStoreJumpUrl(appKey, com.xuqm.update.entity.AppStoreConfigEntity.StoreType.APP_STORE)
|
||||||
: null));
|
: null));
|
||||||
entity.setMarketUrl(hasText(marketUrl)
|
entity.setMarketUrl(hasText(marketUrl)
|
||||||
? marketUrl
|
? marketUrl
|
||||||
: (platform == AppVersionEntity.Platform.HARMONY
|
: (platform == AppVersionEntity.Platform.HARMONY
|
||||||
? appStoreService.getStoreJumpUrl(appId, com.xuqm.update.entity.AppStoreConfigEntity.StoreType.HARMONY_APP)
|
? appStoreService.getStoreJumpUrl(appKey, com.xuqm.update.entity.AppStoreConfigEntity.StoreType.HARMONY_APP)
|
||||||
: null));
|
: null));
|
||||||
if (publishImmediately) {
|
if (publishImmediately) {
|
||||||
entity.setPublishStatus(AppVersionEntity.PublishStatus.PUBLISHED);
|
entity.setPublishStatus(AppVersionEntity.PublishStatus.PUBLISHED);
|
||||||
@ -326,9 +326,9 @@ public class AppVersionController {
|
|||||||
|
|
||||||
@GetMapping("/app/list")
|
@GetMapping("/app/list")
|
||||||
public ResponseEntity<ApiResponse<List<AppVersionEntity>>> list(
|
public ResponseEntity<ApiResponse<List<AppVersionEntity>>> list(
|
||||||
@RequestParam String appId, @RequestParam AppVersionEntity.Platform platform) {
|
@RequestParam String appKey, @RequestParam AppVersionEntity.Platform platform) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(
|
return ResponseEntity.ok(ApiResponse.success(
|
||||||
versionRepository.findByAppIdAndPlatformOrderByVersionCodeDesc(appId, platform)));
|
versionRepository.findByAppIdAndPlatformOrderByVersionCodeDesc(appKey, platform)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String publishAction(AppVersionEntity.PublishStatus previousStatus,
|
private String publishAction(AppVersionEntity.PublishStatus previousStatus,
|
||||||
|
|||||||
@ -20,38 +20,38 @@ public class PublishConfigController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/publish/config")
|
@GetMapping("/publish/config")
|
||||||
public ResponseEntity<ApiResponse<AppPublishConfigEntity>> getConfig(@RequestParam String appId) {
|
public ResponseEntity<ApiResponse<AppPublishConfigEntity>> getConfig(@RequestParam String appKey) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(publishConfigService.getConfig(appId)));
|
return ResponseEntity.ok(ApiResponse.success(publishConfigService.getConfig(appKey)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/publish/config")
|
@PutMapping("/publish/config")
|
||||||
public ResponseEntity<ApiResponse<AppPublishConfigEntity>> saveConfig(
|
public ResponseEntity<ApiResponse<AppPublishConfigEntity>> saveConfig(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestBody Map<String, Object> body) {
|
@RequestBody Map<String, Object> body) {
|
||||||
validateCallbacks(body);
|
validateCallbacks(body);
|
||||||
return ResponseEntity.ok(ApiResponse.success(publishConfigService.saveConfig(appId, body)));
|
return ResponseEntity.ok(ApiResponse.success(publishConfigService.saveConfig(appKey, body)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/gray/members")
|
@GetMapping("/gray/members")
|
||||||
public ResponseEntity<ApiResponse<List<PublishConfigService.GrayMemberGroupView>>> listMembers(
|
public ResponseEntity<ApiResponse<List<PublishConfigService.GrayMemberGroupView>>> listMembers(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam(required = false) String keyword,
|
@RequestParam(required = false) String keyword,
|
||||||
@RequestParam(required = false) String groupName) {
|
@RequestParam(required = false) String groupName) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(
|
return ResponseEntity.ok(ApiResponse.success(
|
||||||
publishConfigService.listGrayMembers(appId, keyword, groupName)));
|
publishConfigService.listGrayMembers(appKey, keyword, groupName)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/gray/members/sync")
|
@PostMapping("/gray/members/sync")
|
||||||
public ResponseEntity<ApiResponse<List<PublishConfigService.GrayMemberGroupView>>> syncMembers(
|
public ResponseEntity<ApiResponse<List<PublishConfigService.GrayMemberGroupView>>> syncMembers(
|
||||||
@RequestParam String appId) {
|
@RequestParam String appKey) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(publishConfigService.syncGrayMembers(appId)));
|
return ResponseEntity.ok(ApiResponse.success(publishConfigService.syncGrayMembers(appKey)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/gray/members/import")
|
@PostMapping("/gray/members/import")
|
||||||
public ResponseEntity<ApiResponse<List<PublishConfigService.GrayMemberGroupView>>> importMembers(
|
public ResponseEntity<ApiResponse<List<PublishConfigService.GrayMemberGroupView>>> importMembers(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestBody String payload) {
|
@RequestBody String payload) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(publishConfigService.replaceGrayMembers(appId, payload)));
|
return ResponseEntity.ok(ApiResponse.success(publishConfigService.replaceGrayMembers(appKey, payload)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateCallbacks(Map<String, Object> body) {
|
private void validateCallbacks(Map<String, Object> body) {
|
||||||
|
|||||||
@ -42,7 +42,7 @@ public class RnBundleController {
|
|||||||
|
|
||||||
@GetMapping("/update/check")
|
@GetMapping("/update/check")
|
||||||
public ResponseEntity<ApiResponse<Map<String, Object>>> checkUpdate(
|
public ResponseEntity<ApiResponse<Map<String, Object>>> checkUpdate(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam String moduleId,
|
@RequestParam String moduleId,
|
||||||
@RequestParam String platform,
|
@RequestParam String platform,
|
||||||
@RequestParam String currentVersion,
|
@RequestParam String currentVersion,
|
||||||
@ -51,7 +51,7 @@ public class RnBundleController {
|
|||||||
RnBundleEntity.Platform p = RnBundleEntity.Platform.valueOf(platform.toUpperCase());
|
RnBundleEntity.Platform p = RnBundleEntity.Platform.valueOf(platform.toUpperCase());
|
||||||
Optional<RnBundleEntity> latest = bundleRepository
|
Optional<RnBundleEntity> latest = bundleRepository
|
||||||
.findTopByAppIdAndModuleIdAndPlatformAndPublishStatusOrderByCreatedAtDesc(
|
.findTopByAppIdAndModuleIdAndPlatformAndPublishStatusOrderByCreatedAtDesc(
|
||||||
appId, moduleId, p, RnBundleEntity.PublishStatus.PUBLISHED);
|
appKey, moduleId, p, RnBundleEntity.PublishStatus.PUBLISHED);
|
||||||
|
|
||||||
if (latest.isEmpty()) {
|
if (latest.isEmpty()) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(Map.of("needsUpdate", false)));
|
return ResponseEntity.ok(ApiResponse.success(Map.of("needsUpdate", false)));
|
||||||
@ -63,7 +63,7 @@ public class RnBundleController {
|
|||||||
"needsUpdate", needsUpdate,
|
"needsUpdate", needsUpdate,
|
||||||
"bundleVersion", parseBundleVersion(b.getVersion()),
|
"bundleVersion", parseBundleVersion(b.getVersion()),
|
||||||
"latestVersion", b.getVersion(),
|
"latestVersion", b.getVersion(),
|
||||||
"downloadUrl", resolvePublicBaseUrl() + "/api/v1/rn/files/" + appId + "/" + platform.toLowerCase() + "/" + moduleId,
|
"downloadUrl", resolvePublicBaseUrl() + "/api/v1/rn/files/" + appKey + "/" + platform.toLowerCase() + "/" + moduleId,
|
||||||
"md5", b.getMd5(),
|
"md5", b.getMd5(),
|
||||||
"minCommonVersion", b.getMinCommonVersion() != null ? b.getMinCommonVersion() : "0.0.0",
|
"minCommonVersion", b.getMinCommonVersion() != null ? b.getMinCommonVersion() : "0.0.0",
|
||||||
"note", b.getNote() != null ? b.getNote() : "",
|
"note", b.getNote() != null ? b.getNote() : "",
|
||||||
@ -75,7 +75,7 @@ public class RnBundleController {
|
|||||||
|
|
||||||
@PostMapping("/upload")
|
@PostMapping("/upload")
|
||||||
public ResponseEntity<ApiResponse<RnBundleEntity>> upload(
|
public ResponseEntity<ApiResponse<RnBundleEntity>> upload(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam(required = false) String moduleId,
|
@RequestParam(required = false) String moduleId,
|
||||||
@RequestParam(required = false) RnBundleEntity.Platform platform,
|
@RequestParam(required = false) RnBundleEntity.Platform platform,
|
||||||
@RequestParam(required = false) String version,
|
@RequestParam(required = false) String version,
|
||||||
@ -93,11 +93,11 @@ public class RnBundleController {
|
|||||||
throw new IllegalArgumentException("moduleId, version and platform are required or must be readable from the bundle name");
|
throw new IllegalArgumentException("moduleId, version and platform are required or must be readable from the bundle name");
|
||||||
}
|
}
|
||||||
UpdateAssetService.StoredRnBundle stored = updateAssetService.storeRnBundle(
|
UpdateAssetService.StoredRnBundle stored = updateAssetService.storeRnBundle(
|
||||||
appId, resolvedPlatform.name(), resolvedModuleId, bundle);
|
appKey, resolvedPlatform.name(), resolvedModuleId, bundle);
|
||||||
|
|
||||||
RnBundleEntity entity = new RnBundleEntity();
|
RnBundleEntity entity = new RnBundleEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appId);
|
entity.setAppId(appKey);
|
||||||
entity.setModuleId(resolvedModuleId);
|
entity.setModuleId(resolvedModuleId);
|
||||||
entity.setPlatform(resolvedPlatform);
|
entity.setPlatform(resolvedPlatform);
|
||||||
entity.setVersion(resolvedVersion);
|
entity.setVersion(resolvedVersion);
|
||||||
@ -136,17 +136,17 @@ public class RnBundleController {
|
|||||||
|
|
||||||
@GetMapping("/list")
|
@GetMapping("/list")
|
||||||
public ResponseEntity<ApiResponse<List<RnBundleEntity>>> list(
|
public ResponseEntity<ApiResponse<List<RnBundleEntity>>> list(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam(required = false) String moduleId,
|
@RequestParam(required = false) String moduleId,
|
||||||
@RequestParam(required = false) String platform) {
|
@RequestParam(required = false) String platform) {
|
||||||
List<RnBundleEntity> result;
|
List<RnBundleEntity> result;
|
||||||
if (moduleId != null && platform != null) {
|
if (moduleId != null && platform != null) {
|
||||||
RnBundleEntity.Platform p = RnBundleEntity.Platform.valueOf(platform.toUpperCase());
|
RnBundleEntity.Platform p = RnBundleEntity.Platform.valueOf(platform.toUpperCase());
|
||||||
result = bundleRepository.findByAppIdAndModuleIdAndPlatformOrderByCreatedAtDesc(appId, moduleId, p);
|
result = bundleRepository.findByAppIdAndModuleIdAndPlatformOrderByCreatedAtDesc(appKey, moduleId, p);
|
||||||
} else if (moduleId != null) {
|
} else if (moduleId != null) {
|
||||||
result = bundleRepository.findByAppIdAndModuleIdOrderByCreatedAtDesc(appId, moduleId);
|
result = bundleRepository.findByAppIdAndModuleIdOrderByCreatedAtDesc(appKey, moduleId);
|
||||||
} else {
|
} else {
|
||||||
result = bundleRepository.findByAppIdOrderByCreatedAtDesc(appId);
|
result = bundleRepository.findByAppIdOrderByCreatedAtDesc(appKey);
|
||||||
}
|
}
|
||||||
return ResponseEntity.ok(ApiResponse.success(result));
|
return ResponseEntity.ok(ApiResponse.success(result));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,7 +50,7 @@ public class UnifiedReleaseController {
|
|||||||
|
|
||||||
@PostMapping("/unified/upload")
|
@PostMapping("/unified/upload")
|
||||||
public ResponseEntity<ApiResponse<UnifiedReleaseResult>> upload(
|
public ResponseEntity<ApiResponse<UnifiedReleaseResult>> upload(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam String manifest,
|
@RequestParam String manifest,
|
||||||
HttpServletRequest request) throws Exception {
|
HttpServletRequest request) throws Exception {
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ public class UnifiedReleaseController {
|
|||||||
}
|
}
|
||||||
AppVersionEntity entity = new AppVersionEntity();
|
AppVersionEntity entity = new AppVersionEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appId);
|
entity.setAppId(appKey);
|
||||||
entity.setPlatform(item.platform());
|
entity.setPlatform(item.platform());
|
||||||
entity.setVersionName(item.versionName());
|
entity.setVersionName(item.versionName());
|
||||||
entity.setVersionCode(item.versionCode());
|
entity.setVersionCode(item.versionCode());
|
||||||
@ -110,14 +110,14 @@ public class UnifiedReleaseController {
|
|||||||
for (UnifiedReleaseManifest.RnBundleUploadItem item : safeList(unifiedReleaseManifest.rnBundles())) {
|
for (UnifiedReleaseManifest.RnBundleUploadItem item : safeList(unifiedReleaseManifest.rnBundles())) {
|
||||||
MultipartFile file = multipartRequest.getFile(item.fileKey());
|
MultipartFile file = multipartRequest.getFile(item.fileKey());
|
||||||
UpdateAssetService.StoredRnBundle stored = updateAssetService.storeRnBundle(
|
UpdateAssetService.StoredRnBundle stored = updateAssetService.storeRnBundle(
|
||||||
appId,
|
appKey,
|
||||||
item.platform().name(),
|
item.platform().name(),
|
||||||
item.moduleId(),
|
item.moduleId(),
|
||||||
file);
|
file);
|
||||||
|
|
||||||
RnBundleEntity entity = new RnBundleEntity();
|
RnBundleEntity entity = new RnBundleEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appId);
|
entity.setAppId(appKey);
|
||||||
entity.setModuleId(item.moduleId());
|
entity.setModuleId(item.moduleId());
|
||||||
entity.setPlatform(item.platform());
|
entity.setPlatform(item.platform());
|
||||||
entity.setVersion(item.version());
|
entity.setVersion(item.version());
|
||||||
|
|||||||
@ -29,12 +29,12 @@ public class UpdateFileController {
|
|||||||
return serveFile(file, MediaType.parseMediaType("application/vnd.android.package-archive"));
|
return serveFile(file, MediaType.parseMediaType("application/vnd.android.package-archive"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/api/v1/rn/files/{appId}/{platform}/{moduleId}")
|
@GetMapping("/api/v1/rn/files/{appKey}/{platform}/{moduleId}")
|
||||||
public ResponseEntity<Resource> downloadRnBundle(
|
public ResponseEntity<Resource> downloadRnBundle(
|
||||||
@PathVariable String appId,
|
@PathVariable String appKey,
|
||||||
@PathVariable String platform,
|
@PathVariable String platform,
|
||||||
@PathVariable String moduleId) throws Exception {
|
@PathVariable String moduleId) throws Exception {
|
||||||
Path file = Paths.get(uploadDir, "rn", appId, platform, moduleId,
|
Path file = Paths.get(uploadDir, "rn", appKey, platform, moduleId,
|
||||||
moduleId + "." + platform + ".bundle").normalize();
|
moduleId + "." + platform + ".bundle").normalize();
|
||||||
return serveFile(file, MediaType.APPLICATION_OCTET_STREAM);
|
return serveFile(file, MediaType.APPLICATION_OCTET_STREAM);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,8 +23,8 @@ public class UpdateOperationLogController {
|
|||||||
|
|
||||||
@GetMapping("/logs")
|
@GetMapping("/logs")
|
||||||
public ResponseEntity<ApiResponse<List<UpdateOperationLogEntity>>> list(
|
public ResponseEntity<ApiResponse<List<UpdateOperationLogEntity>>> list(
|
||||||
@RequestParam String appId,
|
@RequestParam String appKey,
|
||||||
@RequestParam(defaultValue = "100") int limit) {
|
@RequestParam(defaultValue = "100") int limit) {
|
||||||
return ResponseEntity.ok(ApiResponse.success(operationLogService.list(appId, limit)));
|
return ResponseEntity.ok(ApiResponse.success(operationLogService.list(appKey, limit)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,22 +45,22 @@ public class AppStoreService {
|
|||||||
|
|
||||||
// ── Store config CRUD ────────────────────────────────────────────────────
|
// ── Store config CRUD ────────────────────────────────────────────────────
|
||||||
|
|
||||||
public List<AppStoreConfigEntity> getConfigs(String appId) {
|
public List<AppStoreConfigEntity> getConfigs(String appKey) {
|
||||||
return configRepo.findByAppId(appId);
|
return configRepo.findByAppId(appKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AppStoreConfigEntity saveConfig(String appId,
|
public AppStoreConfigEntity saveConfig(String appKey,
|
||||||
AppStoreConfigEntity.StoreType storeType,
|
AppStoreConfigEntity.StoreType storeType,
|
||||||
String configJson,
|
String configJson,
|
||||||
boolean enabled) {
|
boolean enabled) {
|
||||||
boolean isCreate = configRepo.findByAppIdAndStoreType(appId, storeType).isEmpty();
|
boolean isCreate = configRepo.findByAppIdAndStoreType(appKey, storeType).isEmpty();
|
||||||
AppStoreConfigEntity entity = configRepo
|
AppStoreConfigEntity entity = configRepo
|
||||||
.findByAppIdAndStoreType(appId, storeType)
|
.findByAppIdAndStoreType(appKey, storeType)
|
||||||
.orElseGet(AppStoreConfigEntity::new);
|
.orElseGet(AppStoreConfigEntity::new);
|
||||||
|
|
||||||
if (entity.getId() == null) {
|
if (entity.getId() == null) {
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appId);
|
entity.setAppId(appKey);
|
||||||
entity.setStoreType(storeType);
|
entity.setStoreType(storeType);
|
||||||
}
|
}
|
||||||
entity.setConfigJson(configJson);
|
entity.setConfigJson(configJson);
|
||||||
@ -68,7 +68,7 @@ public class AppStoreService {
|
|||||||
entity.setUpdatedAt(LocalDateTime.now());
|
entity.setUpdatedAt(LocalDateTime.now());
|
||||||
AppStoreConfigEntity saved = configRepo.save(entity);
|
AppStoreConfigEntity saved = configRepo.save(entity);
|
||||||
operationLogService.record(
|
operationLogService.record(
|
||||||
appId,
|
appKey,
|
||||||
"STORE_CONFIG",
|
"STORE_CONFIG",
|
||||||
saved.getId(),
|
saved.getId(),
|
||||||
isCreate ? "CREATE_STORE_CONFIG" : "UPDATE_STORE_CONFIG",
|
isCreate ? "CREATE_STORE_CONFIG" : "UPDATE_STORE_CONFIG",
|
||||||
@ -80,11 +80,11 @@ public class AppStoreService {
|
|||||||
return saved;
|
return saved;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteConfig(String appId, AppStoreConfigEntity.StoreType storeType) {
|
public void deleteConfig(String appKey, AppStoreConfigEntity.StoreType storeType) {
|
||||||
configRepo.findByAppIdAndStoreType(appId, storeType).ifPresent(cfg -> {
|
configRepo.findByAppIdAndStoreType(appKey, storeType).ifPresent(cfg -> {
|
||||||
configRepo.delete(cfg);
|
configRepo.delete(cfg);
|
||||||
operationLogService.record(
|
operationLogService.record(
|
||||||
appId,
|
appKey,
|
||||||
"STORE_CONFIG",
|
"STORE_CONFIG",
|
||||||
cfg.getId(),
|
cfg.getId(),
|
||||||
"DELETE_STORE_CONFIG",
|
"DELETE_STORE_CONFIG",
|
||||||
@ -151,8 +151,8 @@ public class AppStoreService {
|
|||||||
* Fetch enabled store credentials for use by the release script.
|
* Fetch enabled store credentials for use by the release script.
|
||||||
* Returns a map of storeType -> configJson (as parsed map, not raw string).
|
* Returns a map of storeType -> configJson (as parsed map, not raw string).
|
||||||
*/
|
*/
|
||||||
public Map<String, Object> getStoreCredentials(String appId) throws Exception {
|
public Map<String, Object> getStoreCredentials(String appKey) throws Exception {
|
||||||
List<AppStoreConfigEntity> configs = configRepo.findByAppIdAndEnabled(appId, true);
|
List<AppStoreConfigEntity> configs = configRepo.findByAppIdAndEnabled(appKey, true);
|
||||||
Map<String, Object> result = new LinkedHashMap<>();
|
Map<String, Object> result = new LinkedHashMap<>();
|
||||||
for (AppStoreConfigEntity cfg : configs) {
|
for (AppStoreConfigEntity cfg : configs) {
|
||||||
if (cfg.getStoreType() == AppStoreConfigEntity.StoreType.REVIEW_WEBHOOK) {
|
if (cfg.getStoreType() == AppStoreConfigEntity.StoreType.REVIEW_WEBHOOK) {
|
||||||
@ -166,20 +166,20 @@ public class AppStoreService {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, String> getReviewWebhookConfig(String appId) throws Exception {
|
public Map<String, String> getReviewWebhookConfig(String appKey) throws Exception {
|
||||||
AppStoreConfigEntity cfg = configRepo.findByAppIdAndStoreType(
|
AppStoreConfigEntity cfg = configRepo.findByAppIdAndStoreType(
|
||||||
appId, AppStoreConfigEntity.StoreType.REVIEW_WEBHOOK).orElse(null);
|
appKey, AppStoreConfigEntity.StoreType.REVIEW_WEBHOOK).orElse(null);
|
||||||
if (cfg == null || !cfg.isEnabled() || cfg.getConfigJson() == null || cfg.getConfigJson().isBlank()) {
|
if (cfg == null || !cfg.isEnabled() || cfg.getConfigJson() == null || cfg.getConfigJson().isBlank()) {
|
||||||
return Map.of();
|
return Map.of();
|
||||||
}
|
}
|
||||||
return mapper.readValue(cfg.getConfigJson(), new TypeReference<>() {});
|
return mapper.readValue(cfg.getConfigJson(), new TypeReference<>() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getStoreJumpUrl(String appId, AppStoreConfigEntity.StoreType storeType) {
|
public String getStoreJumpUrl(String appKey, AppStoreConfigEntity.StoreType storeType) {
|
||||||
if (storeType == null || storeType == AppStoreConfigEntity.StoreType.REVIEW_WEBHOOK) {
|
if (storeType == null || storeType == AppStoreConfigEntity.StoreType.REVIEW_WEBHOOK) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return configRepo.findByAppIdAndStoreType(appId, storeType)
|
return configRepo.findByAppIdAndStoreType(appKey, storeType)
|
||||||
.filter(AppStoreConfigEntity::isEnabled)
|
.filter(AppStoreConfigEntity::isEnabled)
|
||||||
.map(AppStoreConfigEntity::getConfigJson)
|
.map(AppStoreConfigEntity::getConfigJson)
|
||||||
.map(this::extractJumpUrl)
|
.map(this::extractJumpUrl)
|
||||||
@ -296,7 +296,7 @@ public class AppStoreService {
|
|||||||
String body = mapper.writeValueAsString(Map.of(
|
String body = mapper.writeValueAsString(Map.of(
|
||||||
"event", "store_review_update",
|
"event", "store_review_update",
|
||||||
"versionId", v.getId(),
|
"versionId", v.getId(),
|
||||||
"appId", v.getAppId(),
|
"appKey", v.getAppId(),
|
||||||
"versionName", v.getVersionName(),
|
"versionName", v.getVersionName(),
|
||||||
"storeType", storeType,
|
"storeType", storeType,
|
||||||
"reviewState", state.name(),
|
"reviewState", state.name(),
|
||||||
@ -332,9 +332,9 @@ public class AppStoreService {
|
|||||||
AppVersionEntity.StoreReviewState.APPROVED.name().equals(readReviewState(reviewMap.get(t))));
|
AppVersionEntity.StoreReviewState.APPROVED.name().equals(readReviewState(reviewMap.get(t))));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String resolveWebhookSecret(String appId) {
|
private String resolveWebhookSecret(String appKey) {
|
||||||
try {
|
try {
|
||||||
return getReviewWebhookConfig(appId).getOrDefault("secret", "");
|
return getReviewWebhookConfig(appKey).getOrDefault("secret", "");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,22 +44,22 @@ public class PublishConfigService {
|
|||||||
this.objectMapper = objectMapper;
|
this.objectMapper = objectMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AppPublishConfigEntity getConfig(String appId) {
|
public AppPublishConfigEntity getConfig(String appKey) {
|
||||||
return configRepository.findByAppId(appId).orElseGet(() -> {
|
return configRepository.findByAppId(appKey).orElseGet(() -> {
|
||||||
AppPublishConfigEntity entity = new AppPublishConfigEntity();
|
AppPublishConfigEntity entity = new AppPublishConfigEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appId);
|
entity.setAppId(appKey);
|
||||||
entity.setConfigJson(defaultConfigJson());
|
entity.setConfigJson(defaultConfigJson());
|
||||||
entity.setUpdatedAt(LocalDateTime.now());
|
entity.setUpdatedAt(LocalDateTime.now());
|
||||||
return entity;
|
return entity;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public AppPublishConfigEntity saveConfig(String appId, Map<String, Object> body) {
|
public AppPublishConfigEntity saveConfig(String appKey, Map<String, Object> body) {
|
||||||
AppPublishConfigEntity entity = configRepository.findByAppId(appId).orElseGet(AppPublishConfigEntity::new);
|
AppPublishConfigEntity entity = configRepository.findByAppId(appKey).orElseGet(AppPublishConfigEntity::new);
|
||||||
if (entity.getId() == null) {
|
if (entity.getId() == null) {
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appId);
|
entity.setAppId(appKey);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
entity.setConfigJson(objectMapper.writeValueAsString(body == null ? Map.of() : body));
|
entity.setConfigJson(objectMapper.writeValueAsString(body == null ? Map.of() : body));
|
||||||
@ -70,8 +70,8 @@ public class PublishConfigService {
|
|||||||
return configRepository.save(entity);
|
return configRepository.save(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public JsonNode getConfigNode(String appId) {
|
public JsonNode getConfigNode(String appKey) {
|
||||||
AppPublishConfigEntity entity = configRepository.findByAppId(appId).orElse(null);
|
AppPublishConfigEntity entity = configRepository.findByAppId(appKey).orElse(null);
|
||||||
if (entity == null || entity.getConfigJson() == null || entity.getConfigJson().isBlank()) {
|
if (entity == null || entity.getConfigJson() == null || entity.getConfigJson().isBlank()) {
|
||||||
try {
|
try {
|
||||||
return objectMapper.readTree(defaultConfigJson());
|
return objectMapper.readTree(defaultConfigJson());
|
||||||
@ -87,11 +87,11 @@ public class PublishConfigService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<GrayMemberGroupView> listGrayMembers(String appId, String keyword, String groupName) {
|
public List<GrayMemberGroupView> listGrayMembers(String appKey, String keyword, String groupName) {
|
||||||
String normalizedKeyword = keyword == null ? "" : keyword.trim().toLowerCase(Locale.ROOT);
|
String normalizedKeyword = keyword == null ? "" : keyword.trim().toLowerCase(Locale.ROOT);
|
||||||
String normalizedGroup = groupName == null ? "" : groupName.trim().toLowerCase(Locale.ROOT);
|
String normalizedGroup = groupName == null ? "" : groupName.trim().toLowerCase(Locale.ROOT);
|
||||||
|
|
||||||
List<AppGrayMemberEntity> members = grayMemberRepository.findByAppIdOrderByGroupNameAscNameAscUserIdAsc(appId)
|
List<AppGrayMemberEntity> members = grayMemberRepository.findByAppIdOrderByGroupNameAscNameAscUserIdAsc(appKey)
|
||||||
.stream()
|
.stream()
|
||||||
.filter(item -> normalizedGroup.isBlank()
|
.filter(item -> normalizedGroup.isBlank()
|
||||||
|| safe(item.getGroupName()).toLowerCase(Locale.ROOT).contains(normalizedGroup))
|
|| safe(item.getGroupName()).toLowerCase(Locale.ROOT).contains(normalizedGroup))
|
||||||
@ -116,15 +116,15 @@ public class PublishConfigService {
|
|||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<GrayMemberGroupView> syncGrayMembers(String appId) {
|
public List<GrayMemberGroupView> syncGrayMembers(String appKey) {
|
||||||
JsonNode config = getConfigNode(appId);
|
JsonNode config = getConfigNode(appKey);
|
||||||
String url = config.path("grayDirectorySyncCallbackUrl").asText("");
|
String url = config.path("grayDirectorySyncCallbackUrl").asText("");
|
||||||
if (url == null || url.isBlank()) {
|
if (url == null || url.isBlank()) {
|
||||||
throw new IllegalStateException("grayDirectorySyncCallbackUrl is not configured");
|
throw new IllegalStateException("grayDirectorySyncCallbackUrl is not configured");
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Object> requestBody = Map.of(
|
Map<String, Object> requestBody = Map.of(
|
||||||
"appId", appId,
|
"appKey", appKey,
|
||||||
"timestamp", System.currentTimeMillis(),
|
"timestamp", System.currentTimeMillis(),
|
||||||
"action", "sync"
|
"action", "sync"
|
||||||
);
|
);
|
||||||
@ -137,11 +137,11 @@ public class PublishConfigService {
|
|||||||
ResponseEntity<String> response = restTemplate.exchange(
|
ResponseEntity<String> response = restTemplate.exchange(
|
||||||
URI.create(url), org.springframework.http.HttpMethod.POST,
|
URI.create(url), org.springframework.http.HttpMethod.POST,
|
||||||
new org.springframework.http.HttpEntity<>(requestBody, headers), String.class);
|
new org.springframework.http.HttpEntity<>(requestBody, headers), String.class);
|
||||||
return replaceGrayMembers(appId, response.getBody());
|
return replaceGrayMembers(appKey, response.getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> resolveGrayMembers(String appId, Map<String, Object> requestBody) {
|
public List<String> resolveGrayMembers(String appKey, Map<String, Object> requestBody) {
|
||||||
JsonNode config = getConfigNode(appId);
|
JsonNode config = getConfigNode(appKey);
|
||||||
String url = config.path("graySelectCallbackUrl").asText("");
|
String url = config.path("graySelectCallbackUrl").asText("");
|
||||||
if (url == null || url.isBlank()) {
|
if (url == null || url.isBlank()) {
|
||||||
throw new IllegalStateException("graySelectCallbackUrl is not configured");
|
throw new IllegalStateException("graySelectCallbackUrl is not configured");
|
||||||
@ -150,7 +150,7 @@ public class PublishConfigService {
|
|||||||
if (requestBody != null) {
|
if (requestBody != null) {
|
||||||
payload.putAll(requestBody);
|
payload.putAll(requestBody);
|
||||||
}
|
}
|
||||||
payload.put("appId", appId);
|
payload.put("appKey", appKey);
|
||||||
payload.put("timestamp", System.currentTimeMillis());
|
payload.put("timestamp", System.currentTimeMillis());
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
@ -164,13 +164,13 @@ public class PublishConfigService {
|
|||||||
return extractMemberIds(response.getBody());
|
return extractMemberIds(response.getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<GrayMemberGroupView> replaceGrayMembers(String appId, String responseBody) {
|
public List<GrayMemberGroupView> replaceGrayMembers(String appKey, String responseBody) {
|
||||||
List<GrayMemberGroupPayload> groups = parseGroups(responseBody);
|
List<GrayMemberGroupPayload> groups = parseGroups(responseBody);
|
||||||
if (groups == null) {
|
if (groups == null) {
|
||||||
throw new IllegalStateException("Invalid gray member payload");
|
throw new IllegalStateException("Invalid gray member payload");
|
||||||
}
|
}
|
||||||
grayMemberRepository.deleteAll(
|
grayMemberRepository.deleteAll(
|
||||||
grayMemberRepository.findByAppIdOrderByGroupNameAscNameAscUserIdAsc(appId));
|
grayMemberRepository.findByAppIdOrderByGroupNameAscNameAscUserIdAsc(appKey));
|
||||||
|
|
||||||
List<AppGrayMemberEntity> saved = new ArrayList<>();
|
List<AppGrayMemberEntity> saved = new ArrayList<>();
|
||||||
for (GrayMemberGroupPayload group : groups) {
|
for (GrayMemberGroupPayload group : groups) {
|
||||||
@ -181,7 +181,7 @@ public class PublishConfigService {
|
|||||||
}
|
}
|
||||||
AppGrayMemberEntity entity = new AppGrayMemberEntity();
|
AppGrayMemberEntity entity = new AppGrayMemberEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appId);
|
entity.setAppId(appKey);
|
||||||
entity.setGroupName(groupName);
|
entity.setGroupName(groupName);
|
||||||
entity.setUserId(member.userId().trim());
|
entity.setUserId(member.userId().trim());
|
||||||
entity.setName(member.name() == null ? "" : member.name().trim());
|
entity.setName(member.name() == null ? "" : member.name().trim());
|
||||||
@ -191,7 +191,7 @@ public class PublishConfigService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
grayMemberRepository.saveAll(saved);
|
grayMemberRepository.saveAll(saved);
|
||||||
return listGrayMembers(appId, null, null);
|
return listGrayMembers(appKey, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> extractMemberIds(String responseBody) {
|
private List<String> extractMemberIds(String responseBody) {
|
||||||
|
|||||||
@ -136,12 +136,12 @@ public class UpdateAssetService {
|
|||||||
return inspectRnBundleName(fileName);
|
return inspectRnBundleName(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public StoredRnBundle storeRnBundle(String appId, String platform, String moduleId, MultipartFile bundle) throws Exception {
|
public StoredRnBundle storeRnBundle(String appKey, String platform, String moduleId, MultipartFile bundle) throws Exception {
|
||||||
if (bundle == null || bundle.isEmpty()) {
|
if (bundle == null || bundle.isEmpty()) {
|
||||||
throw new IllegalArgumentException("bundle file is required");
|
throw new IllegalArgumentException("bundle file is required");
|
||||||
}
|
}
|
||||||
String filename = resolveBundleFilename(moduleId, platform, bundle.getOriginalFilename());
|
String filename = resolveBundleFilename(moduleId, platform, bundle.getOriginalFilename());
|
||||||
Path dir = Paths.get(uploadDir, "rn", appId, platform.toLowerCase(), moduleId);
|
Path dir = Paths.get(uploadDir, "rn", appKey, platform.toLowerCase(), moduleId);
|
||||||
Files.createDirectories(dir);
|
Files.createDirectories(dir);
|
||||||
Path dest = dir.resolve(filename);
|
Path dest = dir.resolve(filename);
|
||||||
|
|
||||||
|
|||||||
@ -25,7 +25,7 @@ public class UpdateOperationLogService {
|
|||||||
this.objectMapper = objectMapper;
|
this.objectMapper = objectMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void record(String appId,
|
public void record(String appKey,
|
||||||
String resourceType,
|
String resourceType,
|
||||||
String resourceId,
|
String resourceId,
|
||||||
String action,
|
String action,
|
||||||
@ -33,7 +33,7 @@ public class UpdateOperationLogService {
|
|||||||
Map<String, Object> detail) {
|
Map<String, Object> detail) {
|
||||||
UpdateOperationLogEntity entity = new UpdateOperationLogEntity();
|
UpdateOperationLogEntity entity = new UpdateOperationLogEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
entity.setAppId(appId);
|
entity.setAppId(appKey);
|
||||||
entity.setResourceType(resourceType);
|
entity.setResourceType(resourceType);
|
||||||
entity.setResourceId(resourceId);
|
entity.setResourceId(resourceId);
|
||||||
entity.setAction(action);
|
entity.setAction(action);
|
||||||
@ -44,10 +44,10 @@ public class UpdateOperationLogService {
|
|||||||
repository.save(entity);
|
repository.save(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<UpdateOperationLogEntity> list(String appId, int limit) {
|
public List<UpdateOperationLogEntity> list(String appKey, int limit) {
|
||||||
int size = Math.min(Math.max(limit, 1), 200);
|
int size = Math.min(Math.max(limit, 1), 200);
|
||||||
return repository.findByAppIdOrderByCreatedAtDesc(
|
return repository.findByAppIdOrderByCreatedAtDesc(
|
||||||
appId,
|
appKey,
|
||||||
PageRequest.of(0, size));
|
PageRequest.of(0, size));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户