fix(push): 修复设备换绑驱逐时 NonUniqueResultException
findByAppKeyAndDeviceId / findByAppKeyAndToken 改为返回 List, 避免 DB 中存在多条同 appKey+deviceId 记录时 Hibernate 抛出 NonUniqueResultException(HTTP 500)。 驱逐逻辑改为 stream + forEach,将所有旧用户记录一并清除。 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
这个提交包含在:
父节点
86ab6e73ba
当前提交
7194126a4c
@ -19,8 +19,8 @@ public interface DeviceTokenRepository extends JpaRepository<DeviceTokenEntity,
|
||||
List<DeviceTokenEntity> findByAppKeyAndUserIdOrderByLastLoginAtDescUpdatedAtDesc(String appKey, String userId);
|
||||
void deleteByAppKeyAndUserIdAndVendor(String appKey, String userId, DeviceTokenEntity.Vendor vendor);
|
||||
void deleteByAppKeyAndUserIdAndDeviceId(String appKey, String userId, String deviceId);
|
||||
// Find device by appKey+deviceId regardless of userId (for cross-user eviction)
|
||||
Optional<DeviceTokenEntity> findByAppKeyAndDeviceId(String appKey, String deviceId);
|
||||
// Find device by appKey+token regardless of userId (token rotation detection)
|
||||
Optional<DeviceTokenEntity> findByAppKeyAndToken(String appKey, String token);
|
||||
// Find all entries for this device across all users (for cross-user eviction)
|
||||
List<DeviceTokenEntity> findByAppKeyAndDeviceId(String appKey, String deviceId);
|
||||
// Find all entries for this token across all users (token rotation detection)
|
||||
List<DeviceTokenEntity> findByAppKeyAndToken(String appKey, String token);
|
||||
}
|
||||
|
||||
@ -247,18 +247,18 @@ public class PushDispatcher {
|
||||
String romVersion) {
|
||||
String resolvedDeviceId = normalizeDeviceId(deviceId, vendor, token);
|
||||
|
||||
// Evict previous owner: if this device (by deviceId OR token) is registered to another user, remove it
|
||||
tokenRepository.findByAppKeyAndDeviceId(appKey, resolvedDeviceId)
|
||||
// Evict all other users who hold this device (by deviceId) or this token
|
||||
tokenRepository.findByAppKeyAndDeviceId(appKey, resolvedDeviceId).stream()
|
||||
.filter(prev -> !userId.equals(prev.getUserId()))
|
||||
.ifPresent(prev -> {
|
||||
log.info("Device {} re-registered by new user={}, evicting previous user={}", resolvedDeviceId, userId, prev.getUserId());
|
||||
.forEach(prev -> {
|
||||
log.info("Device {} re-registered by user={}, evicting previous user={}", resolvedDeviceId, userId, prev.getUserId());
|
||||
recordLog(prev, DeviceLoginLogEntity.EventType.UNREGISTER);
|
||||
tokenRepository.delete(prev);
|
||||
});
|
||||
tokenRepository.findByAppKeyAndToken(appKey, token)
|
||||
tokenRepository.findByAppKeyAndToken(appKey, token).stream()
|
||||
.filter(prev -> !userId.equals(prev.getUserId()))
|
||||
.ifPresent(prev -> {
|
||||
log.info("Token re-registered by new user={}, evicting previous user={} deviceId={}", userId, prev.getUserId(), prev.getDeviceId());
|
||||
.forEach(prev -> {
|
||||
log.info("Token re-registered by user={}, evicting previous user={} deviceId={}", userId, prev.getUserId(), prev.getDeviceId());
|
||||
recordLog(prev, DeviceLoginLogEntity.EventType.UNREGISTER);
|
||||
tokenRepository.delete(prev);
|
||||
});
|
||||
|
||||
正在加载...
在新工单中引用
屏蔽一个用户