mirror of
https://github.com/iflytek/skillhub.git
synced 2026-08-28 11:25:00 +00:00
fix(domain): hide stale rejected preview when newer version published
When a skill had an earlier REJECTED version and a later PUBLISHED version, the my-skills list showed both "已发布" and "已拒绝" badges because ownerPreviewVersion pointed to the stale rejected version. Changed SkillLifecycleProjectionService to filter out non-published versions older than the published one. Only versions newer than the published version are surfaced as the owner preview, treating older rejected attempts as settled history. - Refactored projectForViewer and projectForOwnerSummary to use new buildProjection helper - Replaced resolveOwnerPendingPreview and resolveNewestNonPublishedVersion with resolveNewerNonPublishedVersion that filters by recency - Added test case covering the stale rejected scenario Synced from SAAS commit c9eefa93fix(domain): hide stale rejected preview when newer version published
This commit is contained in:
parent
90fc97e740
commit
ed6ea2417a
2 changed files with 50 additions and 29 deletions
|
|
@ -265,6 +265,27 @@ class MySkillAppServiceTest {
|
|||
assertThat(result.items().get(0).headlineVersion().status()).isEqualTo("REJECTED");
|
||||
}
|
||||
|
||||
@Test
|
||||
void listMySkills_hidesStaleRejectedVersionOlderThanPublished() {
|
||||
Skill skill = createSkill(6L, 101L, "recovered-skill", "user-1");
|
||||
SkillVersion rejectedVersion = createVersion(6L, 60L, "1.0.0", SkillVersionStatus.REJECTED, "2026-03-15T09:30:00Z");
|
||||
SkillVersion publishedVersion = createVersion(6L, 61L, "2.0.0", SkillVersionStatus.PUBLISHED, "2026-03-16T09:30:00Z");
|
||||
|
||||
given(skillRepository.findByOwnerId("user-1", PageRequest.of(0, 10)))
|
||||
.willReturn(new PageImpl<>(List.of(skill), PageRequest.of(0, 10), 1));
|
||||
given(skillVersionRepository.findBySkillId(6L)).willReturn(List.of(rejectedVersion, publishedVersion));
|
||||
given(skillVersionRepository.findBySkillIdAndStatus(6L, SkillVersionStatus.PUBLISHED))
|
||||
.willReturn(List.of(publishedVersion));
|
||||
given(namespaceRepository.findByIdIn(List.of(101L))).willReturn(List.of(namespace(101L, "team-ai")));
|
||||
|
||||
var result = service.listMySkills("user-1", 0, 10);
|
||||
|
||||
assertThat(result.items()).hasSize(1);
|
||||
assertThat(result.items().get(0).headlineVersion().status()).isEqualTo("PUBLISHED");
|
||||
assertThat(result.items().get(0).headlineVersion().version()).isEqualTo("2.0.0");
|
||||
assertThat(result.items().get(0).ownerPreviewVersion()).isNull();
|
||||
}
|
||||
|
||||
private Skill createSkill(Long id, Long namespaceId, String slug, String ownerId) {
|
||||
Skill skill = new Skill(namespaceId, slug, ownerId, SkillVisibility.PUBLIC);
|
||||
skill.setDisplayName(slug);
|
||||
|
|
|
|||
|
|
@ -39,6 +39,10 @@ public class SkillLifecycleProjectionService {
|
|||
ResolutionMode resolutionMode
|
||||
) {}
|
||||
|
||||
private static final Comparator<SkillVersion> RECENCY = Comparator
|
||||
.comparing(SkillVersion::getCreatedAt, Comparator.nullsLast(Comparator.naturalOrder()))
|
||||
.thenComparing(SkillVersion::getId, Comparator.nullsLast(Comparator.naturalOrder()));
|
||||
|
||||
private final SkillVersionRepository skillVersionRepository;
|
||||
|
||||
public SkillLifecycleProjectionService(SkillVersionRepository skillVersionRepository) {
|
||||
|
|
@ -46,22 +50,26 @@ public class SkillLifecycleProjectionService {
|
|||
}
|
||||
|
||||
public Projection projectForViewer(Skill skill, String currentUserId, Map<Long, NamespaceRole> userNsRoles) {
|
||||
VersionProjection publishedVersion = toProjection(resolvePublishedVersion(skill));
|
||||
VersionProjection ownerPreviewVersion = toProjection(resolveOwnerPendingPreview(skill, currentUserId, userNsRoles));
|
||||
VersionProjection headlineVersion = publishedVersion != null ? publishedVersion : ownerPreviewVersion;
|
||||
ResolutionMode resolutionMode = headlineVersion == null
|
||||
? ResolutionMode.NONE
|
||||
: publishedVersion != null ? ResolutionMode.PUBLISHED : ResolutionMode.OWNER_PREVIEW;
|
||||
return new Projection(headlineVersion, publishedVersion, ownerPreviewVersion, resolutionMode);
|
||||
SkillVersion published = resolvePublishedVersion(skill);
|
||||
SkillVersion preview = canManage(skill, currentUserId, userNsRoles)
|
||||
? resolveNewerNonPublishedVersion(skill, published)
|
||||
: null;
|
||||
return buildProjection(published, preview);
|
||||
}
|
||||
|
||||
public Projection projectForOwnerSummary(Skill skill) {
|
||||
VersionProjection publishedVersion = toProjection(resolvePublishedVersion(skill));
|
||||
VersionProjection ownerPreviewVersion = toProjection(resolveNewestNonPublishedVersion(skill));
|
||||
SkillVersion published = resolvePublishedVersion(skill);
|
||||
SkillVersion preview = resolveNewerNonPublishedVersion(skill, published);
|
||||
return buildProjection(published, preview);
|
||||
}
|
||||
|
||||
private Projection buildProjection(SkillVersion published, SkillVersion preview) {
|
||||
VersionProjection publishedVersion = toProjection(published);
|
||||
VersionProjection ownerPreviewVersion = toProjection(preview);
|
||||
VersionProjection headlineVersion = publishedVersion != null ? publishedVersion : ownerPreviewVersion;
|
||||
ResolutionMode resolutionMode = headlineVersion == null
|
||||
? ResolutionMode.NONE
|
||||
: publishedVersion != null ? ResolutionMode.PUBLISHED : ResolutionMode.OWNER_PREVIEW;
|
||||
ResolutionMode resolutionMode = published != null ? ResolutionMode.PUBLISHED
|
||||
: preview != null ? ResolutionMode.OWNER_PREVIEW
|
||||
: ResolutionMode.NONE;
|
||||
return new Projection(headlineVersion, publishedVersion, ownerPreviewVersion, resolutionMode);
|
||||
}
|
||||
|
||||
|
|
@ -116,27 +124,19 @@ public class SkillLifecycleProjectionService {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the newest non-published version the owner can preview.
|
||||
* Includes PENDING_REVIEW, REJECTED, DRAFT, SCANNING, SCAN_FAILED — any status
|
||||
* that isn't already covered by the published projection and isn't yanked.
|
||||
* Returns the newest non-published version (PENDING_REVIEW, REJECTED, DRAFT, SCANNING,
|
||||
* SCAN_FAILED) that represents a NEW round of work layered on top of the current published
|
||||
* version. A non-published version that is older than the published version is treated as
|
||||
* settled history (e.g. an early rejected attempt later superseded by a published release)
|
||||
* and is intentionally not surfaced, so the owner does not see a stale preview/rejected badge
|
||||
* next to an already-published skill.
|
||||
*/
|
||||
private SkillVersion resolveOwnerPendingPreview(Skill skill, String currentUserId, Map<Long, NamespaceRole> userNsRoles) {
|
||||
if (!canManage(skill, currentUserId, userNsRoles)) {
|
||||
return null;
|
||||
}
|
||||
private SkillVersion resolveNewerNonPublishedVersion(Skill skill, SkillVersion publishedVersion) {
|
||||
return skillVersionRepository.findBySkillId(skill.getId()).stream()
|
||||
.filter(v -> v.getStatus() != SkillVersionStatus.PUBLISHED
|
||||
&& v.getStatus() != SkillVersionStatus.YANKED)
|
||||
.max(versionComparator())
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private SkillVersion resolveNewestNonPublishedVersion(Skill skill) {
|
||||
List<SkillVersion> versions = skillVersionRepository.findBySkillId(skill.getId());
|
||||
return versions.stream()
|
||||
.filter(version -> version.getStatus() != SkillVersionStatus.PUBLISHED
|
||||
&& version.getStatus() != SkillVersionStatus.YANKED)
|
||||
.max(versionComparator())
|
||||
.filter(version -> publishedVersion == null || RECENCY.compare(version, publishedVersion) > 0)
|
||||
.max(RECENCY)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue