diff --git a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/BusinessEventSnapshot.java b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/BusinessEventSnapshot.java new file mode 100644 index 000000000..f7016563b --- /dev/null +++ b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/BusinessEventSnapshot.java @@ -0,0 +1,24 @@ +package com.iflytek.skillhub.domain.event; + +import com.iflytek.skillhub.domain.skill.Skill; +import com.iflytek.skillhub.domain.skill.SkillVisibility; +import java.time.Instant; + +/** + * Immutable producer context, never an entity or a request object. No telemetry dependency. + * The timestamp is event occurrence (immediately before publication), not transaction commit time. + */ +public record BusinessEventSnapshot(Long namespaceId, SkillVisibility visibility, String actorId, + String actorType, String source, Instant occurredAt) { + public static BusinessEventSnapshot capture(Skill skill, String actorId, String source) { + return capture(skill, actorId, "user", source); + } + + public static BusinessEventSnapshot capture(Skill skill, String actorId, String actorType, String source) { + // BuiltinSkillInitializer reserves this identity and verifies isSystemAccount before publishing. + // It uses the ordinary privileged publish path; never mislabel that service account as human. + if ("builtin-skill-publisher".equals(actorId)) { actorType = "system"; } + return new BusinessEventSnapshot(skill.getNamespaceId(), skill.getVisibility(), actorId, + actorType, source, Instant.now()); + } +} diff --git a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/PromotionApprovedEvent.java b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/PromotionApprovedEvent.java index 4bb099041..b90a788f8 100644 --- a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/PromotionApprovedEvent.java +++ b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/PromotionApprovedEvent.java @@ -1,2 +1,7 @@ package com.iflytek.skillhub.domain.event; -public record PromotionApprovedEvent(Long promotionId, Long skillId, String reviewerId, String submitterId) {} +public record PromotionApprovedEvent(Long promotionId, Long skillId, String reviewerId, + String submitterId, BusinessEventSnapshot snapshot) implements SkillBusinessEvent { + public PromotionApprovedEvent(Long promotionId, Long skillId, String reviewerId, String submitterId) { + this(promotionId, skillId, reviewerId, submitterId, null); + } +} diff --git a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/PromotionRejectedEvent.java b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/PromotionRejectedEvent.java index d3a048123..183afc07c 100644 --- a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/PromotionRejectedEvent.java +++ b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/PromotionRejectedEvent.java @@ -1,2 +1,7 @@ package com.iflytek.skillhub.domain.event; -public record PromotionRejectedEvent(Long promotionId, Long skillId, String reviewerId, String submitterId, String reason) {} +public record PromotionRejectedEvent(Long promotionId, Long skillId, String reviewerId, + String submitterId, String reason, BusinessEventSnapshot snapshot) implements SkillBusinessEvent { + public PromotionRejectedEvent(Long promotionId, Long skillId, String reviewerId, String submitterId, String reason) { + this(promotionId, skillId, reviewerId, submitterId, reason, null); + } +} diff --git a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/ReviewApprovedEvent.java b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/ReviewApprovedEvent.java index b600c9fb7..bc00236be 100644 --- a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/ReviewApprovedEvent.java +++ b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/ReviewApprovedEvent.java @@ -1,2 +1,7 @@ package com.iflytek.skillhub.domain.event; -public record ReviewApprovedEvent(Long reviewId, Long skillId, Long versionId, String reviewerId, String submitterId) {} +public record ReviewApprovedEvent(Long reviewId, Long skillId, Long versionId, String reviewerId, + String submitterId, BusinessEventSnapshot snapshot) implements SkillBusinessEvent { + public ReviewApprovedEvent(Long reviewId, Long skillId, Long versionId, String reviewerId, String submitterId) { + this(reviewId, skillId, versionId, reviewerId, submitterId, null); + } +} diff --git a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/ReviewRejectedEvent.java b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/ReviewRejectedEvent.java index dd09b8ad6..65c2841d3 100644 --- a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/ReviewRejectedEvent.java +++ b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/ReviewRejectedEvent.java @@ -1,2 +1,7 @@ package com.iflytek.skillhub.domain.event; -public record ReviewRejectedEvent(Long reviewId, Long skillId, Long versionId, String reviewerId, String submitterId, String reason) {} +public record ReviewRejectedEvent(Long reviewId, Long skillId, Long versionId, String reviewerId, + String submitterId, String reason, BusinessEventSnapshot snapshot) implements SkillBusinessEvent { + public ReviewRejectedEvent(Long reviewId, Long skillId, Long versionId, String reviewerId, String submitterId, String reason) { + this(reviewId, skillId, versionId, reviewerId, submitterId, reason, null); + } +} diff --git a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/SkillBusinessEvent.java b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/SkillBusinessEvent.java new file mode 100644 index 000000000..6721c31c1 --- /dev/null +++ b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/SkillBusinessEvent.java @@ -0,0 +1,7 @@ +package com.iflytek.skillhub.domain.event; + +/** Marker for existing skill facts with a producer-captured scalar analytics snapshot. */ +public interface SkillBusinessEvent { + Long skillId(); + BusinessEventSnapshot snapshot(); +} diff --git a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/SkillDownloadedEvent.java b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/SkillDownloadedEvent.java index 16cb28367..51cb4c35e 100644 --- a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/SkillDownloadedEvent.java +++ b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/SkillDownloadedEvent.java @@ -1,3 +1,8 @@ package com.iflytek.skillhub.domain.event; -public record SkillDownloadedEvent(Long skillId, Long versionId) {} +public record SkillDownloadedEvent(Long skillId, Long versionId, + BusinessEventSnapshot snapshot) implements SkillBusinessEvent { + public SkillDownloadedEvent(Long skillId, Long versionId) { + this(skillId, versionId, null); + } +} diff --git a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/SkillPublishedEvent.java b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/SkillPublishedEvent.java index e9e5b2b80..b914c6177 100644 --- a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/SkillPublishedEvent.java +++ b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/SkillPublishedEvent.java @@ -1,3 +1,8 @@ package com.iflytek.skillhub.domain.event; -public record SkillPublishedEvent(Long skillId, Long versionId, String publisherId) {} +public record SkillPublishedEvent(Long skillId, Long versionId, String publisherId, + BusinessEventSnapshot snapshot) implements SkillBusinessEvent { + public SkillPublishedEvent(Long skillId, Long versionId, String publisherId) { + this(skillId, versionId, publisherId, null); + } +} diff --git a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/SkillStatusChangedEvent.java b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/SkillStatusChangedEvent.java index 63b89f743..13521a6f9 100644 --- a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/SkillStatusChangedEvent.java +++ b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/SkillStatusChangedEvent.java @@ -2,4 +2,9 @@ package com.iflytek.skillhub.domain.event; import com.iflytek.skillhub.domain.skill.SkillStatus; -public record SkillStatusChangedEvent(Long skillId, SkillStatus oldStatus, SkillStatus newStatus) {} +public record SkillStatusChangedEvent(Long skillId, SkillStatus oldStatus, SkillStatus newStatus, + BusinessEventSnapshot snapshot) implements SkillBusinessEvent { + public SkillStatusChangedEvent(Long skillId, SkillStatus oldStatus, SkillStatus newStatus) { + this(skillId, oldStatus, newStatus, null); + } +} diff --git a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/SkillVersionYankedEvent.java b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/SkillVersionYankedEvent.java index a9a6d92d6..24826eae2 100644 --- a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/SkillVersionYankedEvent.java +++ b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/SkillVersionYankedEvent.java @@ -1,3 +1,8 @@ package com.iflytek.skillhub.domain.event; -public record SkillVersionYankedEvent(Long skillId, Long versionId, String actorUserId, boolean wasPublished) {} +public record SkillVersionYankedEvent(Long skillId, Long versionId, String actorUserId, boolean wasPublished, + BusinessEventSnapshot snapshot) implements SkillBusinessEvent { + public SkillVersionYankedEvent(Long skillId, Long versionId, String actorUserId, boolean wasPublished) { + this(skillId, versionId, actorUserId, wasPublished, null); + } +} diff --git a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/SkillVisibilityChangedEvent.java b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/SkillVisibilityChangedEvent.java index 522c5fe6d..00ef75d9a 100644 --- a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/SkillVisibilityChangedEvent.java +++ b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/event/SkillVisibilityChangedEvent.java @@ -4,4 +4,8 @@ import com.iflytek.skillhub.domain.skill.SkillVisibility; /** Access changed without publishing a new package version. */ public record SkillVisibilityChangedEvent(Long skillId, SkillVisibility previousVisibility, - SkillVisibility visibility) {} + SkillVisibility visibility, BusinessEventSnapshot snapshot) implements SkillBusinessEvent { + public SkillVisibilityChangedEvent(Long skillId, SkillVisibility previousVisibility, SkillVisibility visibility) { + this(skillId, previousVisibility, visibility, null); + } +} diff --git a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/review/PromotionService.java b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/review/PromotionService.java index 67ae04aba..adafa816e 100644 --- a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/review/PromotionService.java +++ b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/review/PromotionService.java @@ -1,5 +1,7 @@ package com.iflytek.skillhub.domain.review; +import com.iflytek.skillhub.domain.event.BusinessEventSnapshot; + import com.iflytek.skillhub.domain.skill.SkillOwnerAccessPolicy; import com.fasterxml.jackson.databind.ObjectMapper; import com.iflytek.skillhub.domain.audit.AuditDetail; @@ -294,10 +296,11 @@ public class PromotionService { PromotionRequest savedRequest = promotionRequestRepository.save(approvedRequest); eventPublisher.publishEvent(new SkillPublishedEvent( - targetSkill.getId(), newVersion.getId(), reviewerId)); + targetSkill.getId(), newVersion.getId(), reviewerId, + BusinessEventSnapshot.capture(targetSkill, reviewerId, "promotion"))); eventPublisher.publishEvent(new PromotionApprovedEvent( approvedRequest.getId(), approvedRequest.getSourceSkillId(), - reviewerId, approvedRequest.getSubmittedBy())); + reviewerId, approvedRequest.getSubmittedBy(), BusinessEventSnapshot.capture(sourceSkill, reviewerId, "promotion"))); governanceNotificationService.notifyUser( approvedRequest.getSubmittedBy(), "PROMOTION", @@ -396,9 +399,12 @@ public class PromotionService { } syncPromotionRequestState(request, ReviewTaskStatus.REJECTED, reviewerId, comment); entityManager.detach(request); + Skill sourceSkill = request.getSourceSkillId() == null ? null + : skillRepository.findById(request.getSourceSkillId()).orElse(null); eventPublisher.publishEvent(new PromotionRejectedEvent( request.getId(), request.getSourceSkillId(), - reviewerId, request.getSubmittedBy(), comment)); + reviewerId, request.getSubmittedBy(), comment, + sourceSkill == null ? null : BusinessEventSnapshot.capture(sourceSkill, reviewerId, "promotion"))); governanceNotificationService.notifyUser( request.getSubmittedBy(), "PROMOTION", diff --git a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/review/ReviewService.java b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/review/ReviewService.java index c18159ff2..fb7a497ab 100644 --- a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/review/ReviewService.java +++ b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/review/ReviewService.java @@ -1,5 +1,7 @@ package com.iflytek.skillhub.domain.review; +import com.iflytek.skillhub.domain.event.BusinessEventSnapshot; + import com.iflytek.skillhub.domain.skill.SkillOwnerAccessPolicy; import org.springframework.beans.factory.annotation.Autowired; import com.fasterxml.jackson.databind.ObjectMapper; @@ -289,10 +291,11 @@ public class ReviewService { if (avatarParticipant != null) { avatarParticipant.decide(task, true, reviewerId, comment); } eventPublisher.publishEvent(new SkillPublishedEvent( - skill.getId(), skillVersion.getId(), reviewerId)); + skill.getId(), skillVersion.getId(), reviewerId, + BusinessEventSnapshot.capture(skill, reviewerId, "review"))); eventPublisher.publishEvent(new ReviewApprovedEvent( task.getId(), skill.getId(), skillVersion.getId(), - reviewerId, task.getSubmittedBy())); + reviewerId, task.getSubmittedBy(), BusinessEventSnapshot.capture(skill, reviewerId, "review"))); governanceNotificationService.notifyUser( task.getSubmittedBy(), "REVIEW", @@ -358,7 +361,7 @@ public class ReviewService { if (avatarParticipant != null) { avatarParticipant.decide(task, false, reviewerId, comment); } eventPublisher.publishEvent(new ReviewRejectedEvent( task.getId(), skill.getId(), skillVersion.getId(), - reviewerId, task.getSubmittedBy(), comment)); + reviewerId, task.getSubmittedBy(), comment, BusinessEventSnapshot.capture(skill, reviewerId, "review"))); governanceNotificationService.notifyUser( task.getSubmittedBy(), "REVIEW", diff --git a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/service/SkillDownloadService.java b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/service/SkillDownloadService.java index 67ae243ed..53591d540 100644 --- a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/service/SkillDownloadService.java +++ b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/service/SkillDownloadService.java @@ -1,6 +1,7 @@ package com.iflytek.skillhub.domain.skill.service; import com.iflytek.skillhub.domain.event.SkillDownloadedEvent; +import com.iflytek.skillhub.domain.event.BusinessEventSnapshot; import com.iflytek.skillhub.domain.namespace.Namespace; import com.iflytek.skillhub.domain.namespace.NamespaceRepository; import com.iflytek.skillhub.domain.namespace.NamespaceRole; @@ -172,15 +173,16 @@ public class SkillDownloadService { // Only increment download count for PUBLISHED versions if (version.getStatus() == SkillVersionStatus.PUBLISHED) { - recordPublishedDownload(skill, version); + recordPublishedDownload(skill, version, currentUserId); } return result; } - private void recordPublishedDownload(Skill skill, SkillVersion version) { + private void recordPublishedDownload(Skill skill, SkillVersion version, String currentUserId) { skillRepository.incrementDownloadCount(skill.getId()); skillVersionStatsRepository.incrementDownloadCount(version.getId(), skill.getId()); - eventPublisher.publishEvent(new SkillDownloadedEvent(skill.getId(), version.getId())); + eventPublisher.publishEvent(new SkillDownloadedEvent(skill.getId(), version.getId(), + BusinessEventSnapshot.capture(skill, currentUserId, "download"))); } private DownloadResult buildDownloadResult(Skill skill, SkillVersion version) { diff --git a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/service/SkillGovernanceService.java b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/service/SkillGovernanceService.java index 82536db4c..5161f549f 100644 --- a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/service/SkillGovernanceService.java +++ b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/service/SkillGovernanceService.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.iflytek.skillhub.domain.audit.AuditDetail; import com.iflytek.skillhub.domain.audit.AuditLogService; import com.iflytek.skillhub.domain.event.SkillStatusChangedEvent; +import com.iflytek.skillhub.domain.event.BusinessEventSnapshot; import com.iflytek.skillhub.domain.namespace.NamespaceRole; import com.iflytek.skillhub.domain.review.ReviewTaskRepository; import com.iflytek.skillhub.domain.security.SecurityScanService; @@ -139,7 +140,8 @@ public class SkillGovernanceService { skill.setUpdatedBy(actorUserId); Skill saved = skillRepository.save(skill); auditLogService.record(actorUserId, "ARCHIVE_SKILL", "SKILL", skill.getId(), null, clientIp, userAgent, jsonReason(reason)); - eventPublisher.publishEvent(new SkillStatusChangedEvent(skill.getId(), previousStatus, SkillStatus.ARCHIVED)); + eventPublisher.publishEvent(new SkillStatusChangedEvent(skill.getId(), previousStatus, SkillStatus.ARCHIVED, + BusinessEventSnapshot.capture(skill, actorUserId, "governance"))); return saved; } @@ -180,7 +182,8 @@ public class SkillGovernanceService { skill.setUpdatedBy(actorUserId); Skill saved = skillRepository.save(skill); auditLogService.record(actorUserId, "UNARCHIVE_SKILL", "SKILL", skillId, null, clientIp, userAgent, null); - eventPublisher.publishEvent(new SkillStatusChangedEvent(skillId, previousStatus, SkillStatus.ACTIVE)); + eventPublisher.publishEvent(new SkillStatusChangedEvent(skillId, previousStatus, SkillStatus.ACTIVE, + BusinessEventSnapshot.capture(skill, actorUserId, "governance"))); return saved; } @@ -353,7 +356,8 @@ public class SkillGovernanceService { version.setYankReason(reason); version.setDownloadReady(false); SkillVersion saved = skillVersionRepository.save(version); - skillRepository.findById(version.getSkillId()).ifPresent(skill -> { + Optional eventSkill = skillRepository.findById(version.getSkillId()); + eventSkill.ifPresent(skill -> { if (versionId.equals(skill.getLatestVersionId())) { Optional fallback = findLatestPublishedVersion(skill.getId()); skill.setLatestVersionId(fallback.map(SkillVersion::getId).orElse(null)); @@ -364,7 +368,8 @@ public class SkillGovernanceService { }); auditLogService.record(actorUserId, "YANK_SKILL_VERSION", "SKILL_VERSION", versionId, null, clientIp, userAgent, jsonReason(reason)); eventPublisher.publishEvent(new com.iflytek.skillhub.domain.event.SkillVersionYankedEvent( - version.getSkillId(), versionId, actorUserId, true)); + version.getSkillId(), versionId, actorUserId, true, + eventSkill.map(skill -> BusinessEventSnapshot.capture(skill, actorUserId, "governance")).orElse(null))); return saved; } diff --git a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/service/SkillPublishService.java b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/service/SkillPublishService.java index d6043acf0..c83b90c00 100644 --- a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/service/SkillPublishService.java +++ b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/service/SkillPublishService.java @@ -1,5 +1,7 @@ package com.iflytek.skillhub.domain.skill.service; +import com.iflytek.skillhub.domain.event.BusinessEventSnapshot; + import com.iflytek.skillhub.domain.skill.SkillOwnerAccessPolicy; import com.iflytek.skillhub.domain.review.ReviewAvatarParticipant; import org.springframework.beans.factory.annotation.Autowired; @@ -900,7 +902,9 @@ public class SkillPublishService { skillRepository.save(skill); if (autoPublish) { - eventPublisher.publishEvent(new SkillPublishedEvent(skill.getId(), version.getId(), publisherId)); + eventPublisher.publishEvent(new SkillPublishedEvent(skill.getId(), version.getId(), publisherId, + BusinessEventSnapshot.capture(skill, publisherId, + forceAutoPublish ? "system" : "user", "auto_publish"))); } // 13. Return identifiers for the created version diff --git a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/service/SkillReviewSubmitService.java b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/service/SkillReviewSubmitService.java index c3e641ada..5fd06343e 100644 --- a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/service/SkillReviewSubmitService.java +++ b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/service/SkillReviewSubmitService.java @@ -1,5 +1,7 @@ package com.iflytek.skillhub.domain.skill.service; +import com.iflytek.skillhub.domain.event.BusinessEventSnapshot; + import com.iflytek.skillhub.domain.skill.SkillOwnerAccessPolicy; import com.iflytek.skillhub.domain.event.SkillPublishedEvent; import com.iflytek.skillhub.domain.namespace.NamespaceMemberRepository; @@ -191,7 +193,8 @@ public class SkillReviewSubmitService { skillRepository.save(skill); eventPublisher.publishEvent(new SkillPublishedEvent( - skill.getId(), version.getId(), actorUserId)); + skill.getId(), version.getId(), actorUserId, + BusinessEventSnapshot.capture(skill, actorUserId, "private_confirm"))); } private void assertCanManageLifecycle(Skill skill, String actorUserId, diff --git a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/service/SkillVisibilityService.java b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/service/SkillVisibilityService.java index 0775de3c0..a12694537 100644 --- a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/service/SkillVisibilityService.java +++ b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/service/SkillVisibilityService.java @@ -2,6 +2,7 @@ package com.iflytek.skillhub.domain.skill.service; import com.iflytek.skillhub.domain.skill.SkillOwnerAccessPolicy; import com.iflytek.skillhub.domain.event.SkillVisibilityChangedEvent; +import com.iflytek.skillhub.domain.event.BusinessEventSnapshot; import com.iflytek.skillhub.domain.security.*; import com.iflytek.skillhub.domain.namespace.*; import com.iflytek.skillhub.domain.review.*; @@ -142,6 +143,7 @@ public class SkillVisibilityService { skill.setVisibility(target); skill.setUpdatedBy(actor); skills.save(skill); - events.publishEvent(new SkillVisibilityChangedEvent(skill.getId(), previous, target)); + events.publishEvent(new SkillVisibilityChangedEvent(skill.getId(), previous, target, + BusinessEventSnapshot.capture(skill, actor, "visibility"))); } } diff --git a/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/review/PromotionServiceTest.java b/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/review/PromotionServiceTest.java index 79c5dc6f2..26c070b5e 100644 --- a/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/review/PromotionServiceTest.java +++ b/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/review/PromotionServiceTest.java @@ -593,6 +593,14 @@ class PromotionServiceTest { assertEquals(NEW_SKILL_ID, event.skillId()); assertEquals(NEW_VERSION_ID, event.versionId()); assertEquals(REVIEWER_ID, event.publisherId()); + assertEquals(TARGET_NAMESPACE_ID, event.snapshot().namespaceId()); + assertEquals(REVIEWER_ID, event.snapshot().actorId()); + assertEquals("promotion", event.snapshot().source()); + ArgumentCaptor approved = + ArgumentCaptor.forClass(com.iflytek.skillhub.domain.event.PromotionApprovedEvent.class); + verify(eventPublisher).publishEvent(approved.capture()); + assertEquals(5L, approved.getValue().snapshot().namespaceId()); + assertEquals(REVIEWER_ID, approved.getValue().snapshot().actorId()); // Verify targetSkillId updated on promotion request verify(promotionRequestRepository).save(approvedRequest); @@ -921,6 +929,7 @@ class PromotionServiceTest { void shouldRejectPromotionSuccessfully() { PromotionRequest pr = createPendingPromotion(); when(promotionRequestRepository.findById(PROMOTION_ID)).thenReturn(Optional.of(pr)); + when(skillRepository.findById(SOURCE_SKILL_ID)).thenReturn(Optional.of(createSourceSkill())); when(permissionChecker.canReviewPromotion(pr, REVIEWER_ID, Set.of("SKILL_ADMIN"))).thenReturn(true); when(promotionRequestRepository.updateStatusWithVersion( PROMOTION_ID, ReviewTaskStatus.REJECTED, REVIEWER_ID, "Not ready", null, pr.getVersion())) @@ -935,7 +944,11 @@ class PromotionServiceTest { assertEquals(Instant.now(CLOCK), result.getReviewedAt()); verify(promotionRequestRepository).updateStatusWithVersion( PROMOTION_ID, ReviewTaskStatus.REJECTED, REVIEWER_ID, "Not ready", null, pr.getVersion()); - verify(eventPublisher, never()).publishEvent(any()); + var rejected = ArgumentCaptor.forClass(com.iflytek.skillhub.domain.event.PromotionRejectedEvent.class); + verify(eventPublisher).publishEvent(rejected.capture()); + assertEquals(SOURCE_SKILL_ID, rejected.getValue().skillId()); + assertEquals(5L, rejected.getValue().snapshot().namespaceId()); + assertEquals(REVIEWER_ID, rejected.getValue().snapshot().actorId()); } @Test diff --git a/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/review/ReviewServiceTest.java b/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/review/ReviewServiceTest.java index bf7721578..105432ddf 100644 --- a/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/review/ReviewServiceTest.java +++ b/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/review/ReviewServiceTest.java @@ -392,6 +392,14 @@ class ReviewServiceTest { assertEquals(SKILL_ID, event.skillId()); assertEquals(SKILL_VERSION_ID, event.versionId()); assertEquals(REVIEWER_ID, event.publisherId()); + assertEquals(NAMESPACE_ID, event.snapshot().namespaceId()); + assertEquals(REVIEWER_ID, event.snapshot().actorId()); + assertEquals("review", event.snapshot().source()); + ArgumentCaptor approved = + ArgumentCaptor.forClass(com.iflytek.skillhub.domain.event.ReviewApprovedEvent.class); + verify(eventPublisher).publishEvent(approved.capture()); + assertEquals(REVIEWER_ID, approved.getValue().snapshot().actorId()); + assertNotNull(approved.getValue().snapshot().occurredAt()); } @Test diff --git a/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/service/SkillGovernanceServiceTest.java b/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/service/SkillGovernanceServiceTest.java index 7af64be84..eeb3cdd18 100644 --- a/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/service/SkillGovernanceServiceTest.java +++ b/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/service/SkillGovernanceServiceTest.java @@ -149,7 +149,10 @@ class SkillGovernanceServiceTest { assertThat(result.getStatus()).isEqualTo(SkillStatus.ARCHIVED); verify(auditLogService).record("owner", "ARCHIVE_SKILL", "SKILL", 10L, null, "127.0.0.1", "JUnit", "{\"reason\":\"cleanup\"}"); - verify(eventPublisher).publishEvent(any(SkillStatusChangedEvent.class)); + var archived = org.mockito.ArgumentCaptor.forClass(SkillStatusChangedEvent.class); + verify(eventPublisher).publishEvent(archived.capture()); + assertThat(archived.getValue().snapshot().actorId()).isEqualTo("owner"); + assertThat(archived.getValue().snapshot().namespaceId()).isEqualTo(1L); } @Test @@ -164,7 +167,10 @@ class SkillGovernanceServiceTest { assertThat(result.getStatus()).isEqualTo(SkillStatus.ACTIVE); verify(auditLogService).record("owner", "UNARCHIVE_SKILL", "SKILL", 10L, null, "127.0.0.1", "JUnit", null); - verify(eventPublisher).publishEvent(any(SkillStatusChangedEvent.class)); + var restored = org.mockito.ArgumentCaptor.forClass(SkillStatusChangedEvent.class); + verify(eventPublisher).publishEvent(restored.capture()); + assertThat(restored.getValue().snapshot().actorId()).isEqualTo("owner"); + assertThat(restored.getValue().newStatus()).isEqualTo(SkillStatus.ACTIVE); } @Test @@ -211,7 +217,10 @@ class SkillGovernanceServiceTest { assertThat(result.getYankedBy()).isEqualTo("owner"); assertThat(result.isDownloadReady()).isFalse(); verify(auditLogService).record("owner", "YANK_SKILL_VERSION", "SKILL_VERSION", 22L, null, "127.0.0.1", "JUnit", "{\"reason\":\"broken\"}"); - verify(eventPublisher).publishEvent(any(com.iflytek.skillhub.domain.event.SkillVersionYankedEvent.class)); + var yanked = org.mockito.ArgumentCaptor.forClass(com.iflytek.skillhub.domain.event.SkillVersionYankedEvent.class); + verify(eventPublisher).publishEvent(yanked.capture()); + assertThat(yanked.getValue().snapshot().actorId()).isEqualTo("owner"); + assertThat(yanked.getValue().snapshot().namespaceId()).isEqualTo(1L); } @Test diff --git a/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/service/SkillPublishServiceTest.java b/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/service/SkillPublishServiceTest.java index a47a48824..88fce3aee 100644 --- a/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/service/SkillPublishServiceTest.java +++ b/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/service/SkillPublishServiceTest.java @@ -804,7 +804,13 @@ class SkillPublishServiceTest { verify(reviewTaskRepository, never()).save(any(ReviewTask.class)); verify(skillRepository).save(argThat(savedSkill -> savedSkill.getLatestVersionId() != null && savedSkill.getLatestVersionId().equals(10L))); - verify(eventPublisher).publishEvent(any(SkillPublishedEvent.class)); + var published = ArgumentCaptor.forClass(SkillPublishedEvent.class); + verify(eventPublisher).publishEvent(published.capture()); + assertEquals(1L, published.getValue().snapshot().namespaceId()); + assertEquals(publisherId, published.getValue().snapshot().actorId()); + assertEquals("user", published.getValue().snapshot().actorType()); + assertEquals("auto_publish", published.getValue().snapshot().source()); + assertNotNull(published.getValue().snapshot().occurredAt()); } @Test diff --git a/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/service/SkillReviewSubmitServiceTest.java b/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/service/SkillReviewSubmitServiceTest.java index 1bf52fa4f..e3bb670b0 100644 --- a/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/service/SkillReviewSubmitServiceTest.java +++ b/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/service/SkillReviewSubmitServiceTest.java @@ -190,6 +190,12 @@ class SkillReviewSubmitServiceTest { assertEquals(skillId, event.skillId()); assertEquals(versionId, event.versionId()); assertEquals(userId, event.publisherId()); + assertEquals(namespaceId, event.snapshot().namespaceId()); + assertEquals(SkillVisibility.PRIVATE, event.snapshot().visibility()); + assertEquals(userId, event.snapshot().actorId()); + assertEquals("user", event.snapshot().actorType()); + assertEquals("private_confirm", event.snapshot().source()); + assertNotNull(event.snapshot().occurredAt()); } @Test diff --git a/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/service/SkillVisibilityServiceTest.java b/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/service/SkillVisibilityServiceTest.java index ebd4acee2..81132617e 100644 --- a/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/service/SkillVisibilityServiceTest.java +++ b/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/service/SkillVisibilityServiceTest.java @@ -51,7 +51,12 @@ class SkillVisibilityServiceTest { assertThat(skill.getLatestVersionId()).isEqualTo(3L); verify(versions, never()).save(any()); verify(reviews, never()).save(any()); - verify(events).publishEvent(any(SkillVisibilityChangedEvent.class)); + var event = org.mockito.ArgumentCaptor.forClass(SkillVisibilityChangedEvent.class); + verify(events).publishEvent(event.capture()); + assertThat(event.getValue().snapshot().namespaceId()).isEqualTo(2L); + assertThat(event.getValue().snapshot().visibility()).isEqualTo(SkillVisibility.PRIVATE); + assertThat(event.getValue().snapshot().actorId()).isEqualTo("owner"); + assertThat(event.getValue().previousVisibility()).isEqualTo(SkillVisibility.PUBLIC); } @Test @@ -62,9 +67,13 @@ class SkillVisibilityServiceTest { assertThat(task.getRequestedVisibility()).isEqualTo(SkillVisibility.PUBLIC); assertThat(task.getVisibilityVersionId()).isEqualTo(3L); assertThat(task.getSkillVersionId()).isNull(); + verifyNoInteractions(events); service.approve(task, "admin"); assertThat(skill.getVisibility()).isEqualTo(SkillVisibility.PUBLIC); verify(versions, never()).save(any()); + var event = org.mockito.ArgumentCaptor.forClass(SkillVisibilityChangedEvent.class); + verify(events).publishEvent(event.capture()); + assertThat(event.getValue().snapshot().actorId()).isEqualTo("admin"); } @Test