fix(domain): clear skill.latest_version_id before deleting skill_version

The PG FK constraint fk_skill_latest_version blocks deleting a
SkillVersion whenever Skill.latest_version_id still references it.
Two services had the wrong order:

- SkillPublishService.deleteReplaceableVersionArtifacts: triggered
  when re-uploading the same version (UPLOADED -> overwritten).
  Reproduced by AstronClaw client retrying personal-skills upload.

- SkillGovernanceService.deleteVersion: triggered when admin deletes
  a draft version that happens to be skill.latest_version_id.

Fix: clear skill.latest_version_id and flush BEFORE deleting the
SkillVersion row, so PG sees no live reference at delete time.

Synced from SAAS commit 4626f0c117d9c0544c4dc1115c3aac7468f0d277
This commit is contained in:
XiaoSeS 2026-06-05 16:00:15 +08:00
parent 1ec93db0d6
commit 491773c092
2 changed files with 12 additions and 5 deletions

View file

@ -182,12 +182,15 @@ public class SkillGovernanceService {
deleteStorageAfterCommit(skill, namespaceSlug, storageKeys);
skillFileRepository.deleteByVersionId(version.getId());
securityScanService.softDeleteByVersionId(version.getId());
skillVersionRepository.delete(version);
// FK 约束 fk_skill_latest_version 阻止删除 skill_version 当 skill.latest_version_id 还指向它。
// 必须先解开引用并 flush,让 PG 在 delete 时看不到引用。
if (version.getId().equals(skill.getLatestVersionId())) {
skill.setLatestVersionId(findLatestPublishedVersionId(skill.getId()));
skill.setUpdatedBy(actorUserId);
skillRepository.save(skill);
skillRepository.flush();
}
skillVersionRepository.delete(version);
auditLogService.record(
actorUserId,
"DELETE_SKILL_VERSION",

View file

@ -564,6 +564,14 @@ public class SkillPublishService {
throw new DomainBadRequestException("error.skill.version.exists", version.getVersion());
}
// FK 约束 fk_skill_latest_version 阻止删除 skill_version 当 skill.latest_version_id 还指向它。
// 必须先解开引用并 flush,让 PG 在 delete 时看不到引用。
if (version.getId().equals(skill.getLatestVersionId())) {
skill.setLatestVersionId(null);
skillRepository.save(skill);
skillRepository.flush();
}
reviewTaskRepository.findBySkillVersionIdAndStatus(version.getId(), ReviewTaskStatus.PENDING)
.ifPresent(reviewTaskRepository::delete);
@ -579,10 +587,6 @@ public class SkillPublishService {
securityScanService.softDeleteByVersionId(version.getId());
skillVersionRepository.delete(version);
skillVersionRepository.flush();
if (version.getId().equals(skill.getLatestVersionId())) {
skill.setLatestVersionId(null);
}
}
private String resolveNamespaceSlug(Long namespaceId) {