diff --git a/src/crates/contracts/product-domains/src/appearance_market.rs b/src/crates/contracts/product-domains/src/appearance_market.rs index ec9f4ae39..2c6673fa7 100644 --- a/src/crates/contracts/product-domains/src/appearance_market.rs +++ b/src/crates/contracts/product-domains/src/appearance_market.rs @@ -196,6 +196,9 @@ pub struct AppearanceMarketSubmissionDraftRequest { #[serde(rename_all = "camelCase")] pub struct AppearanceAdminSubmissionDetail { pub submission: AppearanceMarketSubmission, + /// GitHub account that owns the submission, so reviewers can see who sent it. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub submitter: Option, #[serde(skip_serializing_if = "Option::is_none")] pub manifest: Option, #[serde(skip_serializing_if = "Option::is_none")] diff --git a/src/crates/services/skin-market-service/src/lib.rs b/src/crates/services/skin-market-service/src/lib.rs index 7ef4cb819..238731bc7 100644 --- a/src/crates/services/skin-market-service/src/lib.rs +++ b/src/crates/services/skin-market-service/src/lib.rs @@ -436,6 +436,8 @@ mod tests { assert_eq!(response.status(), StatusCode::OK); let approved = json_body(response).await; assert_eq!(approved["submission"]["status"], "approved"); + assert_eq!(approved["submitter"]["login"], "owner"); + assert_eq!(approved["submitter"]["githubId"], 41); assert!(approved["reviewBundleHash"].as_str().is_some()); let response = app diff --git a/src/crates/services/skin-market-service/src/routes.rs b/src/crates/services/skin-market-service/src/routes.rs index 241b30916..0ab70def5 100644 --- a/src/crates/services/skin-market-service/src/routes.rs +++ b/src/crates/services/skin-market-service/src/routes.rs @@ -1521,8 +1521,11 @@ async fn admin_submission_detail( submission_id: &str, ) -> SkinMarketResult { let row = sqlx::query( - "SELECT manifest_json, package_sha256, preview_sha256, draft_json, package_meta_json - FROM submissions WHERE id = ?", + "SELECT s.manifest_json, s.package_sha256, s.preview_sha256, s.draft_json, + s.package_meta_json, u.github_id, u.login, u.avatar_url + FROM submissions s + LEFT JOIN users u ON u.id = s.owner_user_id + WHERE s.id = ?", ) .bind(submission_id) .fetch_optional(state.database.pool()) @@ -1559,8 +1562,22 @@ async fn admin_submission_detail( } _ => None, }; + let submitter = row + .try_get::, _>("github_id") + .map_err(SkinMarketError::internal)? + .map(|github_id| { + Ok::<_, SkinMarketError>(AppearanceMarketUserSummary { + github_id, + login: row.try_get("login").map_err(SkinMarketError::internal)?, + avatar_url: row + .try_get("avatar_url") + .map_err(SkinMarketError::internal)?, + }) + }) + .transpose()?; Ok(AppearanceAdminSubmissionDetail { submission: submission_by_id(state, submission_id, None).await?, + submitter, manifest: manifest_json .map(|value| parse_json(value, "appearance manifest")) .transpose()?, diff --git a/src/skin-market-web/src/AdminPage.tsx b/src/skin-market-web/src/AdminPage.tsx index 396448fb8..596348be4 100644 --- a/src/skin-market-web/src/AdminPage.tsx +++ b/src/skin-market-web/src/AdminPage.tsx @@ -173,6 +173,22 @@ export function AdminPage({ account, accountResolved, locale, t }: AdminPageProp )}
+
+
{t('reviewSubmitter')}
+
+ {detail.submitter ? ( + + + @{detail.submitter.login} + + ) : t('reviewSubmitterUnknown')} +
+
{t('packageIdentity')}
{detail.submission.packageId || t('notDeclared')}
{t('version')}
{detail.submission.packageVersion || t('notDeclared')}
{t('compatibility')}
{detail.submission.minBitfunVersion}
diff --git a/src/skin-market-web/src/i18n.ts b/src/skin-market-web/src/i18n.ts index ccafaf083..533455320 100644 --- a/src/skin-market-web/src/i18n.ts +++ b/src/skin-market-web/src/i18n.ts @@ -118,6 +118,8 @@ const messages = { reviewEmptyBody: 'There are no submitted appearance packages waiting for review.', reviewQueueLabel: 'Submissions awaiting review', reviewDetailLoading: 'Loading submission details…', + reviewSubmitter: 'Submitted by', + reviewSubmitterUnknown: 'Unknown account', reviewPackageHash: 'Package SHA-256', reviewPreviewHash: 'Preview SHA-256', reviewBundleHash: 'Review bundle hash', @@ -249,6 +251,8 @@ const messages = { reviewEmptyBody: '当前没有等待审核的外观包投稿。', reviewQueueLabel: '待审核投稿', reviewDetailLoading: '正在加载投稿详情…', + reviewSubmitter: '投稿账号', + reviewSubmitterUnknown: '未知账号', reviewPackageHash: '包 SHA-256', reviewPreviewHash: '预览图 SHA-256', reviewBundleHash: '审核包哈希', diff --git a/src/skin-market-web/src/styles.css b/src/skin-market-web/src/styles.css index 6212f6214..f72045e96 100644 --- a/src/skin-market-web/src/styles.css +++ b/src/skin-market-web/src/styles.css @@ -1790,6 +1790,34 @@ img { white-space: nowrap; } +.review-submitter { + display: inline-flex; + align-items: center; + gap: 7px; + max-width: 100%; + color: inherit; + text-decoration: none; +} + +.review-submitter:hover span { + text-decoration: underline; +} + +.review-submitter img { + width: 18px; + height: 18px; + flex: 0 0 auto; + border: 1px solid var(--border); + border-radius: 50%; + object-fit: cover; +} + +.review-submitter span { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + .review-section, .hash-list, .manifest-panel, diff --git a/src/skin-market-web/src/types.ts b/src/skin-market-web/src/types.ts index c8d389ed0..aa536f548 100644 --- a/src/skin-market-web/src/types.ts +++ b/src/skin-market-web/src/types.ts @@ -97,6 +97,7 @@ export interface AppearanceSubmission { export interface AppearanceAdminSubmissionDetail { submission: AppearanceSubmission; + submitter?: AppearanceMarketUser; manifest?: unknown; packageSha256?: string; previewSha256?: string;