diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/dto/SkillSummaryResponse.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/dto/SkillSummaryResponse.java index 8717ba62..ea6eab16 100644 --- a/server/skillhub-app/src/main/java/com/iflytek/skillhub/dto/SkillSummaryResponse.java +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/dto/SkillSummaryResponse.java @@ -18,6 +18,8 @@ public record SkillSummaryResponse( Integer ratingCount, String namespace, Instant updatedAt, + String ownerId, + String ownerDisplayName, boolean canSubmitPromotion, SkillLifecycleVersionResponse headlineVersion, SkillLifecycleVersionResponse publishedVersion, diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/repository/JpaMySkillQueryRepository.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/repository/JpaMySkillQueryRepository.java index 83c33b4e..2b0948cd 100644 --- a/server/skillhub-app/src/main/java/com/iflytek/skillhub/repository/JpaMySkillQueryRepository.java +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/repository/JpaMySkillQueryRepository.java @@ -75,6 +75,8 @@ public class JpaMySkillQueryRepository implements MySkillQueryRepository { skill.getRatingCount(), namespace != null ? namespace.getSlug() : null, skill.getUpdatedAt(), + skill.getOwnerId(), + null, canSubmitPromotion(skill, publishedVersion, namespace), toLifecycleVersion(headlineVersion), toLifecycleVersion(publishedVersion), diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/service/SkillSearchAppService.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/service/SkillSearchAppService.java index 16e63ec7..91a8b195 100644 --- a/server/skillhub-app/src/main/java/com/iflytek/skillhub/service/SkillSearchAppService.java +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/service/SkillSearchAppService.java @@ -249,6 +249,8 @@ public class SkillSearchAppService { skill.getRatingCount(), namespaceSlug, skill.getUpdatedAt(), + skill.getOwnerId(), + null, false, toLifecycleVersion(projection.headlineVersion()), toLifecycleVersion(projection.publishedVersion()), diff --git a/server/skillhub-app/src/test/java/com/iflytek/skillhub/compat/ClawHubCompatControllerTest.java b/server/skillhub-app/src/test/java/com/iflytek/skillhub/compat/ClawHubCompatControllerTest.java index ac1abaff..5d222aaf 100644 --- a/server/skillhub-app/src/test/java/com/iflytek/skillhub/compat/ClawHubCompatControllerTest.java +++ b/server/skillhub-app/src/test/java/com/iflytek/skillhub/compat/ClawHubCompatControllerTest.java @@ -116,6 +116,8 @@ class ClawHubCompatControllerTest { 2, "global", Instant.parse("2026-03-13T09:00:00Z"), + null, + null, false, new SkillLifecycleVersionResponse(11L, "1.2.0", "PUBLISHED"), new SkillLifecycleVersionResponse(11L, "1.2.0", "PUBLISHED"), diff --git a/server/skillhub-app/src/test/java/com/iflytek/skillhub/compat/ClawHubRegistryFacadeTest.java b/server/skillhub-app/src/test/java/com/iflytek/skillhub/compat/ClawHubRegistryFacadeTest.java index 5485c8d9..a5a18362 100644 --- a/server/skillhub-app/src/test/java/com/iflytek/skillhub/compat/ClawHubRegistryFacadeTest.java +++ b/server/skillhub-app/src/test/java/com/iflytek/skillhub/compat/ClawHubRegistryFacadeTest.java @@ -51,6 +51,8 @@ class ClawHubRegistryFacadeTest { 2, "global", updatedAt, + null, + null, false, new SkillLifecycleVersionResponse(11L, "1.0.0", "PUBLISHED"), new SkillLifecycleVersionResponse(11L, "1.0.0", "PUBLISHED"), diff --git a/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/MeControllerTest.java b/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/MeControllerTest.java index a2ee75fe..715986cd 100644 --- a/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/MeControllerTest.java +++ b/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/MeControllerTest.java @@ -71,6 +71,8 @@ class MeControllerTest { 0, "team-ai", Instant.parse("2026-03-17T12:00:00Z"), + null, + null, false, new SkillLifecycleVersionResponse(11L, "1.0.0", "PUBLISHED"), new SkillLifecycleVersionResponse(11L, "1.0.0", "PUBLISHED"), diff --git a/server/skillhub-app/src/test/java/com/iflytek/skillhub/service/cli/CliSkillAppServiceTest.java b/server/skillhub-app/src/test/java/com/iflytek/skillhub/service/cli/CliSkillAppServiceTest.java index 8e4ae51e..f503c834 100644 --- a/server/skillhub-app/src/test/java/com/iflytek/skillhub/service/cli/CliSkillAppServiceTest.java +++ b/server/skillhub-app/src/test/java/com/iflytek/skillhub/service/cli/CliSkillAppServiceTest.java @@ -72,7 +72,7 @@ class CliSkillAppServiceTest { List.of(new SkillSummaryResponse( 1L, "pdf-parser", "PDF Parser", "Parse PDFs", "PUBLIC", "ACTIVE", 100L, 5, BigDecimal.valueOf(4.5), 10, - "global", Instant.now(), false, + "global", Instant.now(), null, null, false, new SkillLifecycleVersionResponse(1L, "1.2.0", "PUBLISHED"), new SkillLifecycleVersionResponse(1L, "1.2.0", "PUBLISHED"), null, "PUBLISHED", null @@ -100,7 +100,7 @@ class CliSkillAppServiceTest { new SkillSummaryResponse( 2L, "ready", "Ready", "Installable", "PUBLIC", "ACTIVE", 0L, 0, BigDecimal.ZERO, 0, - "global", Instant.now(), false, + "global", Instant.now(), null, null, false, new SkillLifecycleVersionResponse(2L, "1.0.0", "PUBLISHED"), new SkillLifecycleVersionResponse(2L, "1.0.0", "PUBLISHED"), null, "PUBLISHED", null diff --git a/web/src/api/types.ts b/web/src/api/types.ts index 3f9663e6..e5d62e12 100644 --- a/web/src/api/types.ts +++ b/web/src/api/types.ts @@ -208,6 +208,8 @@ export interface SkillSummary { ratingCount: number namespace: string updatedAt: string + ownerId?: string + ownerDisplayName?: string canSubmitPromotion: boolean headlineVersion?: SkillLifecycleVersion publishedVersion?: SkillLifecycleVersion diff --git a/web/src/features/skill/skill-card.tsx b/web/src/features/skill/skill-card.tsx index cf6d7214..9259ec70 100644 --- a/web/src/features/skill/skill-card.tsx +++ b/web/src/features/skill/skill-card.tsx @@ -5,7 +5,7 @@ import { Card } from '@/shared/ui/card' import { NamespaceBadge } from '@/shared/components/namespace-badge' import { getHeadlineVersion } from '@/shared/lib/skill-lifecycle' import { formatCompactCount } from '@/shared/lib/number-format' -import { Bookmark, ShieldCheck } from 'lucide-react' +import { Bookmark, ShieldCheck, User, Clock } from 'lucide-react' interface SkillCardProps { skill: SkillSummary @@ -13,6 +13,25 @@ interface SkillCardProps { highlightStarred?: boolean } +function formatRelativeTime(dateString: string): string { + const date = new Date(dateString) + const now = new Date() + const diffMs = now.getTime() - date.getTime() + const diffSeconds = Math.floor(diffMs / 1000) + const diffMinutes = Math.floor(diffSeconds / 60) + const diffHours = Math.floor(diffMinutes / 60) + const diffDays = Math.floor(diffHours / 24) + const diffMonths = Math.floor(diffDays / 30) + const diffYears = Math.floor(diffDays / 365) + + if (diffSeconds < 60) return '刚刚' + if (diffMinutes < 60) return `${diffMinutes}分钟前` + if (diffHours < 24) return `${diffHours}小时前` + if (diffDays < 30) return `${diffDays}天前` + if (diffMonths < 12) return `${diffMonths}个月前` + return `${diffYears}年前` +} + /** * Reusable card for displaying one skill in lists such as landing, namespace, search, and stars. */ @@ -108,6 +127,22 @@ export function SkillCard({ skill, onClick, highlightStarred = true }: SkillCard )} + {(skill.ownerDisplayName || skill.updatedAt) && ( +
+ {skill.ownerDisplayName && ( + + + {skill.ownerDisplayName} + + )} + {skill.updatedAt && ( + + + {formatRelativeTime(skill.updatedAt)} + + )} +
+ )} )