gongxiangp2/lib/work-review-metrics.ts
2026-08-19 12:08:38 +08:00

66 lines
1.9 KiB
TypeScript

export type FirstPassReviewStatus = 'approved' | 'revision_required' | 'pending_review' | 'unknown'
export type FirstPassReviewStatusCount = {
status: string
count: number
}
export type FirstPassReviewSummary = {
totalUploads: number
firstPassApproved: number
rate: number | null
statusBreakdown: {
approved: number
revisionRequired: number
pendingReview: number
unknown: number
}
readAt: string
}
export function normalizeFirstPassReviewStatus(status: string): FirstPassReviewStatus {
if (status === 'approved' || status === 'revision_required' || status === 'pending_review') {
return status
}
return 'unknown'
}
/**
* 首轮通过率 = V1 审核通过作品数 / 全部 V1 上传作品数。
*
* 评审中、首轮未通过以及未知历史状态都属于已上传作品,因此必须保留在分母中。
*/
export function buildFirstPassReviewSummary(
rows: FirstPassReviewStatusCount[],
readAt = new Date(),
): FirstPassReviewSummary {
const statusBreakdown = {
approved: 0,
revisionRequired: 0,
pendingReview: 0,
unknown: 0,
}
for (const row of rows) {
const count = Math.max(0, Number(row.count) || 0)
const normalizedStatus = normalizeFirstPassReviewStatus(row.status)
if (normalizedStatus === 'approved') statusBreakdown.approved += count
else if (normalizedStatus === 'revision_required') statusBreakdown.revisionRequired += count
else if (normalizedStatus === 'pending_review') statusBreakdown.pendingReview += count
else statusBreakdown.unknown += count
}
const totalUploads = Object.values(statusBreakdown).reduce((total, count) => total + count, 0)
const firstPassApproved = statusBreakdown.approved
return {
totalUploads,
firstPassApproved,
rate: totalUploads === 0
? null
: Math.round((firstPassApproved / totalUploads) * 1000) / 10,
statusBreakdown,
readAt: readAt.toISOString(),
}
}