diff --git a/apps/backend/api/check_in.py b/apps/backend/api/check_in.py index 1af13b4..2e76684 100644 --- a/apps/backend/api/check_in.py +++ b/apps/backend/api/check_in.py @@ -171,6 +171,7 @@ async def get_all_check_in_records( status_filter: Optional[str] = Query( None, alias="status", description="过滤状态 (success/failure)" ), + trigger_type: Optional[str] = Query(None, description="过滤触发类型 (scheduled/manual/admin)"), db: Session = Depends(get_db), current_user: User = Depends(get_current_admin_user), ): @@ -181,9 +182,12 @@ async def get_all_check_in_records( - **limit**: 限制记录数 - **task_id**: 过滤指定任务的记录 - **status**: 过滤指定状态的记录 + - **trigger_type**: 过滤触发类型 (scheduled/manual/admin) """ try: - records, total = CheckInService.get_all_records(db, skip, limit, task_id, status_filter) + records, total = CheckInService.get_all_records( + db, skip, limit, task_id, status_filter, trigger_type + ) # 为每条记录添加用户和任务信息 enriched_records = [ CheckInService.enrich_record_with_user_task_info(record, db) for record in records diff --git a/apps/backend/schemas/check_in.py b/apps/backend/schemas/check_in.py index e4cbad3..bc4de95 100644 --- a/apps/backend/schemas/check_in.py +++ b/apps/backend/schemas/check_in.py @@ -33,6 +33,7 @@ class CheckInRecordResponse(BaseModel): # 新增字段:用户和任务信息(用于管理员查看) user_id: Optional[int] = Field(None, description="用户 ID") + user_alias: Optional[str] = Field(None, description="用户别名") user_email: Optional[str] = Field(None, description="用户邮箱") task_name: Optional[str] = Field(None, description="任务名称") thread_id: Optional[str] = Field(None, description="接龙 ID") diff --git a/apps/backend/services/check_in_service.py b/apps/backend/services/check_in_service.py index ff39260..e57ff0f 100644 --- a/apps/backend/services/check_in_service.py +++ b/apps/backend/services/check_in_service.py @@ -471,6 +471,7 @@ class CheckInService: limit: int = 100, task_id: Optional[int] = None, status: Optional[str] = None, + trigger_type: Optional[str] = None, ) -> tuple[List[CheckInRecord], int]: """ 获取所有打卡记录(管理员)- 使用联表查询优化性能 @@ -481,6 +482,7 @@ class CheckInService: limit: 限制记录数 task_id: 过滤任务 ID status: 过滤状态 + trigger_type: 过滤触发类型 Returns: (打卡记录列表, 总记录数) @@ -498,6 +500,9 @@ class CheckInService: if status: query = query.filter(CheckInRecord.status == status) + if trigger_type: + query = query.filter(CheckInRecord.trigger_type == trigger_type) + # 获取总数 total = query.count() @@ -557,6 +562,7 @@ class CheckInService: "trigger_type": record.trigger_type, "check_in_time": record.check_in_time, "user_id": user.id if user else None, + "user_alias": user.alias if user else None, "user_email": user.email if user else None, "task_name": task_name, "thread_id": thread_id, diff --git a/apps/frontend/src/views/admin/AdminRecordsView.vue b/apps/frontend/src/views/admin/AdminRecordsView.vue index c01e015..b076709 100644 --- a/apps/frontend/src/views/admin/AdminRecordsView.vue +++ b/apps/frontend/src/views/admin/AdminRecordsView.vue @@ -1,27 +1,73 @@ diff --git a/apps/frontend/src/views/admin/admin-records.ts b/apps/frontend/src/views/admin/admin-records.ts new file mode 100644 index 0000000..56e38fb --- /dev/null +++ b/apps/frontend/src/views/admin/admin-records.ts @@ -0,0 +1,46 @@ +import type { CheckInRecord } from '@/api' + +export function visibleRecordRange(total: number, skip: number, limit: number) { + if (total <= 0) return '当前 0 - 0' + const start = Math.min(skip + 1, total) + const end = Math.min(skip + limit, total) + return `当前 ${start} - ${end}` +} + +export function formatRecordDetailContent(value?: string | null) { + const text = value?.trim() + if (!text) return '无内容' + + try { + return JSON.stringify(JSON.parse(text), null, 2) + } catch { + return text + } +} + +function parsedRecordMessage(value?: string | null) { + if (!value) return '' + + try { + const payload = JSON.parse(value) as unknown + if (typeof payload !== 'object' || payload === null) return '' + const data = payload as Record + const candidates = [data.Data, data.Description, data.message, data.error] + const hit = candidates.find((candidate) => typeof candidate === 'string' && candidate.trim()) + return typeof hit === 'string' ? hit.trim() : '' + } catch { + return '' + } +} + +export function recordResponseSummary( + record: Pick, + maxLength = 96, +) { + const source = record.response_text || record.error_message || '' + const parsed = parsedRecordMessage(source) + const raw = parsed || source.trim() || '无响应内容' + const normalized = raw.replace(/\s+/g, ' ') + if (normalized.length <= maxLength) return normalized + return `${normalized.slice(0, Math.max(0, maxLength - 1))}…` +}