Files
NextEdu/src/shared/lib/track-event.ts
SpecialX f62b8c0f86 refactor(attendance,elective): 审计第二轮 — 全量完成 P0/P1 改进项
P0 修复:
- 页面层 i18n 全量补齐(admin/teacher/parent/student × attendance/elective)
- types.ts 状态标签常量迁移至 constants.ts(i18n key + Badge variant)
- 修复 getTranslations 导入路径(next-intl → next-intl/server)

P1 改进:
- 解耦 parent 模块对 attendance 类型的直接依赖(本地 view-model 类型)
- 导出纯函数(computeStats/buildWarnings/buildLotteryRankCase 等)
- 统一空状态为 EmptyState 组件
- 清理死代码读 Action(attendance 5 个 + elective 3 个)
- 预留监控埋点接口(trackEvent 13 个新事件名)
- 补齐骨架屏 loading.tsx(8 个页面)
- AlertDialog 替换 window.confirm(student-selection-view)
- a11y 改进(aria-label/role/键盘导航)

修复:
- AttendanceStatus 从 constants.ts 重导出,消除 types/constants 双源混乱
- buildWarnings 的 Translator 类型改用 ReturnType<typeof useTranslations>
2026-06-22 17:33:29 +08:00

93 lines
2.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import "server-only"
/**
* 监控埋点接口(预留)
*
* 在关键 Server Action 中调用 trackEvent 记录业务事件,用于:
* - 公告阅读率、消息回复率等关键指标统计
* - 通知发送失败告警
* - 用户行为漏斗分析
*
* 当前实现:输出到 console.info不阻塞主流程。
* 后续扩展:可接入外部监控服务(如 Sentry / PostHog / 自建埋点系统),
* 只需在 trackEventToSink 中替换实现即可,调用方无需改动。
*/
/** 事件名称(使用点号分隔的命名空间,如 "announcement.published" */
export type EventName =
| "announcement.created"
| "announcement.updated"
| "announcement.published"
| "announcement.archived"
| "announcement.deleted"
| "message.sent"
| "message.deleted"
| "message.marked_read"
| "notification.marked_read"
| "notification.marked_all_read"
| "notification.sent"
| "notification.send_failed"
| "attendance.recorded"
| "attendance.batch_recorded"
| "attendance.updated"
| "attendance.deleted"
| "attendance.rules_saved"
| "elective.course_created"
| "elective.course_updated"
| "elective.course_deleted"
| "elective.selection_opened"
| "elective.selection_closed"
| "elective.course_selected"
| "elective.course_dropped"
| "elective.lottery_completed"
/** 埋点事件负载 */
export interface TrackEventPayload {
/** 事件名称 */
event: EventName
/** 当前用户 ID可选未登录场景为 undefined */
userId?: string
/** 目标对象 ID如公告 ID、消息 ID */
targetId?: string
/** 目标对象类型(如 "announcement"、"message" */
targetType?: string
/** 附加属性(如受众人数、渠道类型) */
properties?: Record<string, unknown>
}
/**
* 将事件发送到外部监控服务。
*
* 当前为占位实现:仅输出到 console.info。
* 接入真实服务时替换此函数体即可。
*/
function trackEventToSink(payload: TrackEventPayload): void {
console.info(
`[TrackEvent] ${payload.event} userId=${payload.userId ?? "-"} targetId=${payload.targetId ?? "-"}${payload.properties ? ` properties=${JSON.stringify(payload.properties)}` : ""}`
)
}
/**
* 记录一个监控事件。
*
* 非阻塞:任何异常都被吞掉,确保不影响主业务流程。
*
* @example
* ```ts
* await trackEvent({
* event: "announcement.published",
* userId: ctx.userId,
* targetId: id,
* targetType: "announcement",
* properties: { audienceSize: userIds.length },
* })
* ```
*/
export async function trackEvent(payload: TrackEventPayload): Promise<void> {
try {
trackEventToSink(payload)
} catch {
// 埋点失败不影响主流程
}
}