feat: 围栏Tab布局重构、低精度过滤、蓝牙考勤去重、考勤删除API
- 围栏管理页面Tab移至顶部,设备绑定Tab隐藏地图全屏展示绑定矩阵 - 位置追踪新增"低精度"按钮,隐藏LBS/WiFi点(地图+折线+表格联动) - 移除LBS/WiFi精度半径圆圈,仅通过标记颜色区分定位类型 - 蓝牙打卡(0xB2)自动创建考勤记录,含去重和WebSocket广播 - 新增考勤批量删除和单条删除API - fence_checker补充json导入 via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run>
This commit is contained in:
@@ -192,6 +192,36 @@ async def device_attendance(
|
||||
)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/batch-delete",
|
||||
response_model=APIResponse[dict],
|
||||
summary="批量删除考勤记录 / Batch delete attendance records",
|
||||
)
|
||||
async def batch_delete_attendance(
|
||||
body: dict,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
批量删除考勤记录,通过 body 传递 attendance_ids 列表,最多 500 条。
|
||||
Batch delete attendance records by IDs (max 500).
|
||||
"""
|
||||
attendance_ids = body.get("attendance_ids", [])
|
||||
if not attendance_ids:
|
||||
raise HTTPException(status_code=400, detail="attendance_ids is required")
|
||||
if len(attendance_ids) > 500:
|
||||
raise HTTPException(status_code=400, detail="Max 500 records per request")
|
||||
|
||||
result = await db.execute(
|
||||
select(AttendanceRecord).where(AttendanceRecord.id.in_(attendance_ids))
|
||||
)
|
||||
records = list(result.scalars().all())
|
||||
for r in records:
|
||||
await db.delete(r)
|
||||
await db.flush()
|
||||
|
||||
return APIResponse(data={"deleted": len(records)})
|
||||
|
||||
|
||||
# NOTE: /{attendance_id} must be after /stats and /device/{device_id} to avoid route conflicts
|
||||
@router.get(
|
||||
"/{attendance_id}",
|
||||
@@ -207,3 +237,21 @@ async def get_attendance(attendance_id: int, db: AsyncSession = Depends(get_db))
|
||||
if record is None:
|
||||
raise HTTPException(status_code=404, detail=f"Attendance {attendance_id} not found")
|
||||
return APIResponse(data=AttendanceRecordResponse.model_validate(record))
|
||||
|
||||
|
||||
@router.delete(
|
||||
"/{attendance_id}",
|
||||
response_model=APIResponse[dict],
|
||||
summary="删除单条考勤记录 / Delete attendance record",
|
||||
)
|
||||
async def delete_attendance(attendance_id: int, db: AsyncSession = Depends(get_db)):
|
||||
"""按ID删除单条考勤记录 / Delete a single attendance record by ID."""
|
||||
result = await db.execute(
|
||||
select(AttendanceRecord).where(AttendanceRecord.id == attendance_id)
|
||||
)
|
||||
record = result.scalar_one_or_none()
|
||||
if record is None:
|
||||
raise HTTPException(status_code=404, detail=f"Attendance {attendance_id} not found")
|
||||
await db.delete(record)
|
||||
await db.flush()
|
||||
return APIResponse(data={"deleted": 1})
|
||||
|
||||
Reference in New Issue
Block a user