XuqmGroup-Server/push-service/src/main/java/com/xuqm/push/service/PushDiagnosticsService.java
XuqmGroup 83ca8c0f49 feat(push): 添加 romVersion 字段及设备 Token 查询接口
- DeviceTokenEntity: 新增 rom_version 字段(厂商系统版本)
- PushController: register 接口添加可选 romVersion 参数
- PushDispatcher: 注册时保存 romVersion
- PushDiagnosticsService.DeviceInfo: 返回 romVersion
- PushManagementController: 新增 GET /admin/devices/{id}/token 接口(管理端查完整 token)
- PushSchemaMigrationService: 添加 v20260626_add_rom_version 迁移

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 12:24:58 +08:00

182 行
7.4 KiB
Java

package com.xuqm.push.service;
import com.xuqm.common.security.JwtUtil;
import com.xuqm.push.entity.DeviceLoginLogEntity;
import com.xuqm.push.entity.DeviceTokenEntity;
import com.xuqm.push.repository.DeviceLoginLogRepository;
import com.xuqm.push.repository.DeviceTokenRepository;
import io.jsonwebtoken.Claims;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
@Service
public class PushDiagnosticsService {
private final DeviceTokenRepository tokenRepository;
private final DeviceLoginLogRepository logRepository;
private final ImPresenceClient presenceClient;
private final PushDispatcher pushDispatcher;
private final JwtUtil jwtUtil;
public PushDiagnosticsService(DeviceTokenRepository tokenRepository,
DeviceLoginLogRepository logRepository,
ImPresenceClient presenceClient,
PushDispatcher pushDispatcher,
JwtUtil jwtUtil) {
this.tokenRepository = tokenRepository;
this.logRepository = logRepository;
this.presenceClient = presenceClient;
this.pushDispatcher = pushDispatcher;
this.jwtUtil = jwtUtil;
}
public PushTokenDiagnostics searchByToken(String token, String appKeyHint) {
Optional<DeviceTokenEntity> tokenMatch = tokenRepository.findFirstByToken(token);
String appKey = appKeyHint;
String userId = null;
String tokenType = "UNKNOWN";
if (tokenMatch.isPresent()) {
DeviceTokenEntity device = tokenMatch.get();
appKey = device.getAppKey();
userId = device.getUserId();
tokenType = "PUSH";
} else {
Optional<ImPresenceClient.PresenceStatus> resolved = presenceClient.resolveToken(token);
if (resolved.isPresent()) {
appKey = resolved.get().appKey();
userId = resolved.get().userId();
tokenType = "IM";
} else {
try {
Claims claims = jwtUtil.parse(token);
userId = claims.getSubject();
String claimAppKey = claims.get("appKey", String.class);
appKey = claimAppKey == null || claimAppKey.isBlank() ? appKeyHint : claimAppKey;
tokenType = "IM";
} catch (Exception ignored) {
// Keep UNKNOWN and return an empty diagnostic.
}
}
}
if (appKey == null || appKey.isBlank() || userId == null || userId.isBlank()) {
return new PushTokenDiagnostics(tokenType, appKey, userId, false, 0L, false, null, List.of(), List.of());
}
ImPresenceClient.PresenceStatus presence = presenceClient.userStatus(appKey, userId)
.orElse(new ImPresenceClient.PresenceStatus(appKey, userId, false, 0L));
List<DeviceTokenEntity> devices = tokenRepository.findByAppKeyAndUserIdOrderByLastLoginAtDescUpdatedAtDesc(appKey, userId);
List<DeviceInfo> deliverableDevices = pushDispatcher.selectedPushTargets(appKey, userId)
.stream()
.map(DeviceInfo::from)
.toList();
DeviceInfo deliverable = deliverableDevices.isEmpty() ? null : deliverableDevices.get(0);
boolean canSendOffline = !presence.online() && !deliverableDevices.isEmpty();
return new PushTokenDiagnostics(
tokenType,
appKey,
userId,
presence.online(),
presence.lastSeenAt(),
canSendOffline,
deliverable,
deliverableDevices,
devices.stream().map(DeviceInfo::from).toList());
}
public PushTokenDiagnostics searchByUserId(String appKey, String userId) {
ImPresenceClient.PresenceStatus presence = presenceClient.userStatus(appKey, userId)
.orElse(new ImPresenceClient.PresenceStatus(appKey, userId, false, 0L));
List<DeviceTokenEntity> devices = tokenRepository.findByAppKeyAndUserIdOrderByLastLoginAtDescUpdatedAtDesc(appKey, userId);
List<DeviceInfo> deliverableDevices = pushDispatcher.selectedPushTargets(appKey, userId)
.stream()
.map(DeviceInfo::from)
.toList();
DeviceInfo deliverable = deliverableDevices.isEmpty() ? null : deliverableDevices.get(0);
boolean canSendOffline = !presence.online() && !deliverableDevices.isEmpty();
return new PushTokenDiagnostics(
"USER",
appKey,
userId,
presence.online(),
presence.lastSeenAt(),
canSendOffline,
deliverable,
deliverableDevices,
devices.stream().map(DeviceInfo::from).toList());
}
public TestPushResult sendTestOfflineMessage(String appKey, String userId, String title, String body, String payload) {
List<DeviceInfo> targets = pushDispatcher.selectedPushTargets(appKey, userId)
.stream()
.map(DeviceInfo::from)
.toList();
if (!targets.isEmpty()) {
pushDispatcher.pushToUser(appKey, userId, title, body, payload);
}
return new TestPushResult(appKey, userId, !targets.isEmpty(), targets.size(), targets);
}
public Page<DeviceLoginLogEntity> deviceLogs(String appKey, String userId, int page, int size) {
int safePage = Math.max(page, 0);
int safeSize = Math.min(Math.max(size, 1), 200);
return logRepository.findByAppKeyAndUserIdOrderByCreatedAtDesc(appKey, userId, PageRequest.of(safePage, safeSize));
}
public record PushTokenDiagnostics(
String tokenType,
String appKey,
String userId,
boolean online,
long lastSeenAt,
boolean canSendOfflineMessage,
DeviceInfo deliverableDevice,
List<DeviceInfo> deliverableDevices,
List<DeviceInfo> devices) {}
public record TestPushResult(
String appKey,
String userId,
boolean sent,
int targetCount,
List<DeviceInfo> targets) {}
public record DeviceInfo(
String id,
String vendor,
String tokenPreview,
String platform,
String deviceId,
String brand,
String model,
String osVersion,
String romVersion,
String appVersion,
boolean receivePush,
LocalDateTime lastLoginAt,
LocalDateTime updatedAt) {
static DeviceInfo from(DeviceTokenEntity entity) {
return new DeviceInfo(
entity.getId(),
entity.getVendor().name(),
PushDispatcher.previewToken(entity.getToken()),
entity.getPlatform(),
entity.getDeviceId(),
entity.getBrand(),
entity.getModel(),
entity.getOsVersion(),
entity.getRomVersion(),
entity.getAppVersion(),
entity.isReceivePush(),
entity.getLastLoginAt(),
entity.getUpdatedAt());
}
}
}