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);
|
List<DeviceTokenEntity> findByAppKeyAndUserIdOrderByLastLoginAtDescUpdatedAtDesc(String appKey, String userId);
|
||||||
void deleteByAppKeyAndUserIdAndVendor(String appKey, String userId, DeviceTokenEntity.Vendor vendor);
|
void deleteByAppKeyAndUserIdAndVendor(String appKey, String userId, DeviceTokenEntity.Vendor vendor);
|
||||||
void deleteByAppKeyAndUserIdAndDeviceId(String appKey, String userId, String deviceId);
|
void deleteByAppKeyAndUserIdAndDeviceId(String appKey, String userId, String deviceId);
|
||||||
// Find device by appKey+deviceId regardless of userId (for cross-user eviction)
|
// Find all entries for this device across all users (for cross-user eviction)
|
||||||
Optional<DeviceTokenEntity> findByAppKeyAndDeviceId(String appKey, String deviceId);
|
List<DeviceTokenEntity> findByAppKeyAndDeviceId(String appKey, String deviceId);
|
||||||
// Find device by appKey+token regardless of userId (token rotation detection)
|
// Find all entries for this token across all users (token rotation detection)
|
||||||
Optional<DeviceTokenEntity> findByAppKeyAndToken(String appKey, String token);
|
List<DeviceTokenEntity> findByAppKeyAndToken(String appKey, String token);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -247,18 +247,18 @@ public class PushDispatcher {
|
|||||||
String romVersion) {
|
String romVersion) {
|
||||||
String resolvedDeviceId = normalizeDeviceId(deviceId, vendor, token);
|
String resolvedDeviceId = normalizeDeviceId(deviceId, vendor, token);
|
||||||
|
|
||||||
// Evict previous owner: if this device (by deviceId OR token) is registered to another user, remove it
|
// Evict all other users who hold this device (by deviceId) or this token
|
||||||
tokenRepository.findByAppKeyAndDeviceId(appKey, resolvedDeviceId)
|
tokenRepository.findByAppKeyAndDeviceId(appKey, resolvedDeviceId).stream()
|
||||||
.filter(prev -> !userId.equals(prev.getUserId()))
|
.filter(prev -> !userId.equals(prev.getUserId()))
|
||||||
.ifPresent(prev -> {
|
.forEach(prev -> {
|
||||||
log.info("Device {} re-registered by new user={}, evicting previous user={}", resolvedDeviceId, userId, prev.getUserId());
|
log.info("Device {} re-registered by user={}, evicting previous user={}", resolvedDeviceId, userId, prev.getUserId());
|
||||||
recordLog(prev, DeviceLoginLogEntity.EventType.UNREGISTER);
|
recordLog(prev, DeviceLoginLogEntity.EventType.UNREGISTER);
|
||||||
tokenRepository.delete(prev);
|
tokenRepository.delete(prev);
|
||||||
});
|
});
|
||||||
tokenRepository.findByAppKeyAndToken(appKey, token)
|
tokenRepository.findByAppKeyAndToken(appKey, token).stream()
|
||||||
.filter(prev -> !userId.equals(prev.getUserId()))
|
.filter(prev -> !userId.equals(prev.getUserId()))
|
||||||
.ifPresent(prev -> {
|
.forEach(prev -> {
|
||||||
log.info("Token re-registered by new user={}, evicting previous user={} deviceId={}", userId, prev.getUserId(), prev.getDeviceId());
|
log.info("Token re-registered by user={}, evicting previous user={} deviceId={}", userId, prev.getUserId(), prev.getDeviceId());
|
||||||
recordLog(prev, DeviceLoginLogEntity.EventType.UNREGISTER);
|
recordLog(prev, DeviceLoginLogEntity.EventType.UNREGISTER);
|
||||||
tokenRepository.delete(prev);
|
tokenRepository.delete(prev);
|
||||||
});
|
});
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户