release: freeze 2026-10-15 validated release baseline
CI/CD Pipeline / UI Contracts (source-structure gates) (push) Canceled after 0s
CI/CD Pipeline / E2E (live stack) (push) Canceled after 0s
CI/CD Pipeline / Backend Build & Test (push) Canceled after 0s
CI/CD Pipeline / Admin Web Build (push) Canceled after 0s
CI/CD Pipeline / Mini Program Compile Check (push) Canceled after 0s
CI/CD Pipeline / Migration Validation (push) Canceled after 0s
CI/CD Pipeline / Test Shell Guard (Phase 23 Gate) (push) Canceled after 0s

This commit is contained in:
WorkBuddy
2026-10-15 09:47:31 +08:00
parent 827c23db1d
commit 3c2b967274
149 changed files with 4324 additions and 348 deletions
+52 -10
View File
@@ -18,6 +18,20 @@ interface ApiEnvelope<T> {
const REQUEST_TIMEOUT = 8000
export function summarizeMatchmakerServices(rows: JsonRecord[]) {
const day = new Intl.DateTimeFormat('zh-CN', { timeZone: 'Asia/Shanghai', year: 'numeric', month: '2-digit', day: '2-digit' })
const today = day.format(new Date())
return {
pending: rows.filter(row => ['submitted', 'assigned', 'in_service', 'waiting_user'].includes(String(row.status))).length,
completed: rows.filter(row => row.status === 'completed').length,
total: rows.filter(row => ['createdAt', 'assignedAt', 'completedAt', 'latestFollowupAt'].some(key => {
if (!row[key]) return false
const date = new Date(String(row[key]))
return !Number.isNaN(date.getTime()) && day.format(date) === today
})).length,
}
}
const numberOf = (source: JsonRecord | undefined, key: string) => {
const value = source?.[key]
return typeof value === 'number' && Number.isFinite(value) ? value : 0
@@ -45,9 +59,10 @@ const emptyMetric = (
export function createEmptyDashboard(notice = '当前账号暂无可展示的工作台数据'): DashboardModel {
return {
followups: [],
metrics: [
emptyMetric('pending_reviews', '待审核资料', 'rose', '/review'),
emptyMetric('matchmaker_todos', '红娘服务待办', 'amber', '/referral'),
emptyMetric('matchmaker_todos', '红娘服务待办', 'amber', '/matchmaker'),
emptyMetric('counseling_todos', '咨询服务待办', 'blue', '/counseling'),
emptyMetric('station_appointments', '服务站预约', 'green', '/service-appointment'),
emptyMetric('effective_matches', '有效匹配', 'purple', '/analytics'),
@@ -65,8 +80,11 @@ export function createEmptyDashboard(notice = '当前账号暂无可展示的工
export async function loadDashboard(permissions: string[]): Promise<DashboardModel> {
const canViewAnalytics = permissions.includes('analytics.view')
const canViewActivities = permissions.includes('activity.view')
const canViewMatchmaker = permissions.includes('matchmaker.view')
const canManageMatchmaker = permissions.includes('matchmaker.assign') || permissions.includes('matchmaker.team.assign')
const canViewReviews = permissions.includes('review.view')
const [coreResult, serviceResult, activityResult] = await Promise.allSettled([
const [coreResult, serviceResult, activityResult, matchmakerResult, reviewResult, managementResult] = await Promise.allSettled([
canViewAnalytics
? client.get('/operator/analytics/dashboard', { timeout: REQUEST_TIMEOUT })
: Promise.resolve(null),
@@ -76,14 +94,33 @@ export async function loadDashboard(permissions: string[]): Promise<DashboardMod
canViewActivities
? client.get('/operator/activities', { timeout: REQUEST_TIMEOUT })
: Promise.resolve(null),
canViewMatchmaker
? client.get('/operator/matchmaker/dashboard', { timeout: REQUEST_TIMEOUT })
: Promise.resolve(null),
canViewReviews ? client.get('/operator/reviews/stats', { timeout: REQUEST_TIMEOUT }) : Promise.resolve(null),
canManageMatchmaker ? client.get('/operator/matchmaker/management/services', { timeout: REQUEST_TIMEOUT }) : Promise.resolve(null),
])
const core = fulfilledData<JsonRecord>(coreResult)
const service = fulfilledData<JsonRecord>(serviceResult)
const activityRows = fulfilledData<JsonRecord[]>(activityResult)
const matchmaker = fulfilledData<JsonRecord>(matchmakerResult)
const reviews = fulfilledData<JsonRecord>(reviewResult)
const managedServices = fulfilledData<JsonRecord[]>(managementResult)
const managedStats = summarizeMatchmakerServices(managedServices || [])
const matchmakerAvailable = canManageMatchmaker ? Boolean(managedServices) : Boolean(matchmaker)
const matchmakerPending = canManageMatchmaker
? managedStats.pending
: numberOf(matchmaker || undefined, 'pendingCount') + numberOf(matchmaker || undefined, 'inProgressCount')
const matchmakerCompleted = canManageMatchmaker
? managedStats.completed
: numberOf(matchmaker || undefined, 'completedCount')
const followups = (managedServices || []).filter(row => row.latestFollowupContent)
.sort((a, b) => String(b.latestFollowupAt).localeCompare(String(a.latestFollowupAt)))
.slice(0, 5).map(row => ({ serviceId: String(row.serviceId), userDisplayNumber: String(row.userDisplayNumber || '未生成编号'),
content: String(row.latestFollowupContent), createdAt: String(row.latestFollowupAt || '') }))
const analyticsAvailable = Boolean(core || service)
const eligibility = recordOf(service || undefined, 'localEligibility')
const counseling = recordOf(service || undefined, 'counseling')
const appointments = recordOf(service || undefined, 'serviceAppointments')
const effectiveConnections = recordOf(service || undefined, 'effectiveConnections')
@@ -92,12 +129,13 @@ export async function loadDashboard(permissions: string[]): Promise<DashboardMod
const metrics: DashboardMetric[] = [
{
key: 'pending_reviews', label: '待审核资料', value: numberOf(eligibility, 'pending'),
trendText: service ? '本地资格待核验' : '暂无授权数据', tone: 'rose', available: Boolean(service), route: '/review',
key: 'pending_reviews', label: '待审核资料', value: numberOf(reviews || undefined, 'pending') + numberOf(reviews || undefined, 'processing'),
trendText: reviews ? '待领取与审核中资料' : '暂无授权数据', tone: 'rose', available: Boolean(reviews), route: '/review',
},
{
key: 'matchmaker_todos', label: '红娘服务待办', value: 0,
trendText: '当前接口未提供待办数', tone: 'amber', available: false, route: '/referral',
key: 'matchmaker_todos', label: '红娘服务待办',
value: matchmakerPending,
trendText: matchmakerAvailable ? (canManageMatchmaker ? '管理范围内待完成服务' : '本人服务对象待跟进') : '暂无授权数据', tone: 'amber', available: matchmakerAvailable, route: '/matchmaker',
},
{
key: 'counseling_todos', label: '咨询服务待办',
@@ -120,9 +158,10 @@ export async function loadDashboard(permissions: string[]): Promise<DashboardMod
]
const todos = ([
{ key: 'review', title: '资料审核', description: '本地资格材料等待核验', count: metrics[0].value, actionText: '立即审核', route: '/review', tone: 'rose', available: metrics[0].available },
{ key: 'review', title: '资料审核', description: '待领取与审核中资料', count: metrics[0].value, actionText: '立即审核', route: '/review', tone: 'rose', available: metrics[0].available },
{ key: 'counseling', title: '咨询预约', description: '咨询申请等待受理或跟进', count: metrics[2].value, actionText: '查看服务', route: '/counseling', tone: 'blue', available: metrics[2].available },
{ key: 'appointment', title: '到站预约', description: '线下服务预约等待确认', count: metrics[3].value, actionText: '管理预约', route: '/service-appointment', tone: 'green', available: metrics[3].available },
{ key: 'matchmaker', title: '红娘服务', description: metrics[1].trendText, count: metrics[1].value, actionText: '查看服务', route: '/matchmaker', tone: 'amber', available: metrics[1].available },
{ key: 'risk', title: '举报处理', description: '风险举报等待工作人员处理', count: numberOf(core || undefined, 'pendingReports'), actionText: '去处理', route: '/risk/reports', tone: 'amber', available: Boolean(core) },
] as DashboardTodo[]).filter((item) => item.available)
@@ -142,6 +181,7 @@ export async function loadDashboard(permissions: string[]): Promise<DashboardMod
{ key: 'approved', label: '资料审核通过', value: numberOf(core || undefined, 'approvedProfiles'), compareText: '已通过资料', tone: 'blue', available: Boolean(core) },
{ key: 'served', label: '公益服务用户', value: numberOf(uniqueUsers, 'totalUniqueServiceUsers'), compareText: '去重服务人数', tone: 'green', available: Boolean(service) },
{ key: 'success', label: '成功牵手', value: numberOf(core || undefined, 'successPairs'), compareText: '双方确认成功', tone: 'amber', available: Boolean(core) },
{ key: 'matchmaker_completed', label: '红娘服务完成', value: matchmakerCompleted, compareText: canManageMatchmaker ? '管理范围内已完成服务' : '本人已完成服务', tone: 'amber', available: matchmakerAvailable },
] as DashboardOverviewItem[]).filter((item) => item.available)
const effectiveness = ([
@@ -152,12 +192,14 @@ export async function loadDashboard(permissions: string[]): Promise<DashboardMod
{ key: 'offline_success', label: '线下牵手成功', value: numberOf(effectiveConnections, 'successConnections'), max: numberOf(effectiveConnections, 'activeMatches'), unit: '对', available: Boolean(service) },
] as DashboardEffectivenessItem[]).filter((item) => item.available)
const rejectedCount = [coreResult, serviceResult, activityResult].filter((item) => item.status === 'rejected').length
const notice = !analyticsAvailable && !activityRows
const rejectedCount = [coreResult, serviceResult, activityResult, matchmakerResult, reviewResult, managementResult].filter((item) => item.status === 'rejected').length
const anyData = analyticsAvailable || Boolean(activityRows) || matchmakerAvailable || Boolean(reviews)
const notice = !anyData
? (rejectedCount ? '工作台数据加载失败,请稍后重试' : '当前账号暂无工作台统计权限')
: (rejectedCount ? '部分模块暂时不可用,其余数据已正常加载' : '')
return {
followups,
metrics,
todos,
activities,