fix: app_key backfill on re-registration + notify after tx commit

- LicenseDeviceService: update app_key on re-registration if blank,
  fixing devices that registered before the app_key column was added
- FeatureServiceManager: send activation IM notification in afterCommit()
  hook so the frontend refresh sees the committed DB state

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
这个提交包含在:
XuqmGroup 2026-05-16 15:04:57 +08:00
父节点 8443af4818
当前提交 57ad8f7f25
共有 2 个文件被更改,包括 42 次插入2 次删除

查看文件

@ -56,6 +56,9 @@ public class DeviceService {
// Re-issue token // Re-issue token
String token = licenseAuthService.generateToken(appKey, deviceId, existing.getId()); String token = licenseAuthService.generateToken(appKey, deviceId, existing.getId());
String tokenHash = hashToken(token); String tokenHash = hashToken(token);
if (existing.getAppKey() == null || existing.getAppKey().isBlank()) {
existing.setAppKey(appKey);
}
existing.setDeviceName(firstNonBlank(deviceName, existing.getDeviceName())); existing.setDeviceName(firstNonBlank(deviceName, existing.getDeviceName()));
existing.setDeviceModel(firstNonBlank(deviceModel, existing.getDeviceModel())); existing.setDeviceModel(firstNonBlank(deviceModel, existing.getDeviceModel()));
existing.setDeviceVendor(firstNonBlank(deviceVendor, existing.getDeviceVendor())); existing.setDeviceVendor(firstNonBlank(deviceVendor, existing.getDeviceVendor()));

查看文件

@ -11,8 +11,12 @@ import com.xuqm.tenant.entity.ServiceActivationRequestEntity.Status;
import com.xuqm.tenant.repository.AppRepository; import com.xuqm.tenant.repository.AppRepository;
import com.xuqm.tenant.repository.FeatureServiceRepository; import com.xuqm.tenant.repository.FeatureServiceRepository;
import com.xuqm.tenant.repository.ServiceActivationRequestRepository; import com.xuqm.tenant.repository.ServiceActivationRequestRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
@ -22,22 +26,27 @@ import java.util.UUID;
@Service @Service
public class FeatureServiceManager { public class FeatureServiceManager {
private static final Logger log = LoggerFactory.getLogger(FeatureServiceManager.class);
private final FeatureServiceRepository repository; private final FeatureServiceRepository repository;
private final ServiceActivationRequestRepository requestRepository; private final ServiceActivationRequestRepository requestRepository;
private final AppRepository appRepository; private final AppRepository appRepository;
private final ObjectMapper objectMapper; private final ObjectMapper objectMapper;
private final LicenseServiceClient licenseServiceClient; private final LicenseServiceClient licenseServiceClient;
private final ImPlatformEventService imPlatformEventService;
public FeatureServiceManager(FeatureServiceRepository repository, public FeatureServiceManager(FeatureServiceRepository repository,
ServiceActivationRequestRepository requestRepository, ServiceActivationRequestRepository requestRepository,
AppRepository appRepository, AppRepository appRepository,
ObjectMapper objectMapper, ObjectMapper objectMapper,
LicenseServiceClient licenseServiceClient) { LicenseServiceClient licenseServiceClient,
ImPlatformEventService imPlatformEventService) {
this.repository = repository; this.repository = repository;
this.requestRepository = requestRepository; this.requestRepository = requestRepository;
this.appRepository = appRepository; this.appRepository = appRepository;
this.objectMapper = objectMapper; this.objectMapper = objectMapper;
this.licenseServiceClient = licenseServiceClient; this.licenseServiceClient = licenseServiceClient;
this.imPlatformEventService = imPlatformEventService;
} }
public List<FeatureServiceEntity> listByApp(String appKey) { public List<FeatureServiceEntity> listByApp(String appKey) {
@ -173,6 +182,7 @@ public class FeatureServiceManager {
appRepository.findByAppKey(normalizedAppId).ifPresent(app -> appRepository.findByAppKey(normalizedAppId).ifPresent(app ->
licenseServiceClient.syncAppLicense(app.getAppKey(), app.getName(), 1, expiresAt)); licenseServiceClient.syncAppLicense(app.getAppKey(), app.getName(), 1, expiresAt));
} }
sendActivationImNotification(req.getAppKey(), req.getServiceType().name(), "APPROVED", reviewNote);
return req; return req;
} }
@ -189,6 +199,7 @@ public class FeatureServiceManager {
}); });
entity.setEnabled(true); entity.setEnabled(true);
repository.save(entity); repository.save(entity);
sendActivationImNotification(req.getAppKey(), req.getServiceType().name(), "APPROVED", reviewNote);
return req; return req;
} }
@ -205,7 +216,9 @@ public class FeatureServiceManager {
req.setStatus(Status.REJECTED); req.setStatus(Status.REJECTED);
req.setReviewNote(reviewNote); req.setReviewNote(reviewNote);
req.setReviewedAt(LocalDateTime.now()); req.setReviewedAt(LocalDateTime.now());
return requestRepository.save(req); requestRepository.save(req);
sendActivationImNotification(req.getAppKey(), req.getServiceType().name(), "REJECTED", reviewNote);
return req;
} }
public List<ServiceActivationRequestEntity> listRequestsByApp(String appKey) { public List<ServiceActivationRequestEntity> listRequestsByApp(String appKey) {
@ -619,4 +632,28 @@ public class FeatureServiceManager {
node.put(field, value.trim()); node.put(field, value.trim());
} }
} }
private void sendActivationImNotification(String appKey, String serviceType, String status, String reviewNote) {
// Send after transaction commits so the frontend refresh sees the updated DB state.
if (TransactionSynchronizationManager.isActualTransactionActive()) {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
doSendActivationImNotification(appKey, serviceType, status, reviewNote);
}
});
} else {
doSendActivationImNotification(appKey, serviceType, status, reviewNote);
}
}
private void doSendActivationImNotification(String appKey, String serviceType, String status, String reviewNote) {
try {
imPlatformEventService.notifyServiceActivationChange(
new ImPlatformEventService.ServiceActivationEventRequest(appKey, serviceType, status, reviewNote));
} catch (Exception e) {
log.warn("Failed to send service activation IM notification appKey={} serviceType={} status={}: {}",
appKey, serviceType, status, e.getMessage());
}
}
} }