mirror of
https://github.com/Cccc-owo/CheckInApp.git
synced 2026-06-17 05:56:29 +00:00
style(backend): apply ruff format
This commit is contained in:
+64
-73
@@ -18,6 +18,7 @@ router = APIRouter()
|
||||
|
||||
class BatchToggleTasksRequest(BaseModel):
|
||||
"""批量启用/禁用任务请求"""
|
||||
|
||||
task_ids: List[int]
|
||||
is_active: bool
|
||||
|
||||
@@ -26,7 +27,7 @@ class BatchToggleTasksRequest(BaseModel):
|
||||
async def batch_toggle_tasks(
|
||||
request: BatchToggleTasksRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_admin_user)
|
||||
current_user: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""
|
||||
批量启用或禁用任务的自动打卡功能(需要管理员权限)
|
||||
@@ -47,12 +48,11 @@ async def batch_toggle_tasks(
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"已{'启用' if request.is_active else '禁用'} {count} 个任务",
|
||||
"count": count
|
||||
"count": count,
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"批量操作失败: {str(e)}"
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"批量操作失败: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ async def batch_toggle_tasks(
|
||||
async def batch_check_in(
|
||||
request: BatchCheckInRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_admin_user)
|
||||
current_user: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""
|
||||
批量触发任务打卡(需要管理员权限)
|
||||
@@ -74,15 +74,14 @@ async def batch_check_in(
|
||||
return result
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"批量打卡失败: {str(e)}"
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"批量打卡失败: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/logs", summary="获取系统日志")
|
||||
async def get_system_logs(
|
||||
lines: int = Query(200, ge=1, le=2000, description="读取的日志行数"),
|
||||
current_user: User = Depends(get_current_admin_user)
|
||||
current_user: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""
|
||||
获取系统日志(需要管理员权限)
|
||||
@@ -95,40 +94,34 @@ async def get_system_logs(
|
||||
log_file = settings.LOG_FILE
|
||||
|
||||
if not log_file.exists():
|
||||
return {
|
||||
"success": True,
|
||||
"message": "日志文件不存在",
|
||||
"logs": "日志文件不存在"
|
||||
}
|
||||
return {"success": True, "message": "日志文件不存在", "logs": "日志文件不存在"}
|
||||
|
||||
# 使用 deque 高效读取最后 N 行,避免将整个文件加载到内存
|
||||
from collections import deque
|
||||
|
||||
with open(log_file, 'r', encoding='utf-8', errors='ignore') as f:
|
||||
with open(log_file, "r", encoding="utf-8", errors="ignore") as f:
|
||||
# 使用 deque 保持最后 N 行,内存占用固定
|
||||
last_lines = deque(f, maxlen=lines)
|
||||
|
||||
# 返回字符串格式(不是数组)
|
||||
log_content = ''.join(last_lines)
|
||||
log_content = "".join(last_lines)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"读取了最后 {len(last_lines)} 行日志",
|
||||
"logs": log_content
|
||||
"logs": log_content,
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"读取日志失败: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"读取日志失败: {str(e)}"
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"读取日志失败: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/stats", summary="获取系统统计")
|
||||
async def get_system_stats(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_admin_user)
|
||||
db: Session = Depends(get_db), current_user: User = Depends(get_current_admin_user)
|
||||
):
|
||||
"""
|
||||
获取系统统计信息(需要管理员权限)
|
||||
@@ -159,33 +152,39 @@ async def get_system_stats(
|
||||
|
||||
# 今日打卡记录数
|
||||
today_start = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
today_records = db.query(CheckInRecord).filter(
|
||||
CheckInRecord.check_in_time >= today_start
|
||||
).count()
|
||||
today_records = (
|
||||
db.query(CheckInRecord).filter(CheckInRecord.check_in_time >= today_start).count()
|
||||
)
|
||||
|
||||
# 今日成功打卡数
|
||||
today_success = db.query(CheckInRecord).filter(
|
||||
CheckInRecord.check_in_time >= today_start,
|
||||
CheckInRecord.status == "success"
|
||||
).count()
|
||||
today_success = (
|
||||
db.query(CheckInRecord)
|
||||
.filter(CheckInRecord.check_in_time >= today_start, CheckInRecord.status == "success")
|
||||
.count()
|
||||
)
|
||||
|
||||
# 今日失败打卡数
|
||||
today_failure = db.query(CheckInRecord).filter(
|
||||
CheckInRecord.check_in_time >= today_start,
|
||||
CheckInRecord.status == "failure"
|
||||
).count()
|
||||
today_failure = (
|
||||
db.query(CheckInRecord)
|
||||
.filter(CheckInRecord.check_in_time >= today_start, CheckInRecord.status == "failure")
|
||||
.count()
|
||||
)
|
||||
|
||||
# 今日时间范围外打卡数
|
||||
today_out_of_time = db.query(CheckInRecord).filter(
|
||||
CheckInRecord.check_in_time >= today_start,
|
||||
CheckInRecord.status == "out_of_time"
|
||||
).count()
|
||||
today_out_of_time = (
|
||||
db.query(CheckInRecord)
|
||||
.filter(
|
||||
CheckInRecord.check_in_time >= today_start, CheckInRecord.status == "out_of_time"
|
||||
)
|
||||
.count()
|
||||
)
|
||||
|
||||
# 今日异常打卡数
|
||||
today_unknown = db.query(CheckInRecord).filter(
|
||||
CheckInRecord.check_in_time >= today_start,
|
||||
CheckInRecord.status == "unknown"
|
||||
).count()
|
||||
today_unknown = (
|
||||
db.query(CheckInRecord)
|
||||
.filter(CheckInRecord.check_in_time >= today_start, CheckInRecord.status == "unknown")
|
||||
.count()
|
||||
)
|
||||
|
||||
# Token 即将过期的用户数(7天内)
|
||||
# 使用 SQL 直接查询,避免 N+1 问题
|
||||
@@ -198,28 +197,32 @@ async def get_system_stats(
|
||||
# 条件:authorization 不为空、jwt_exp 不为 "0"、且在未来 7 天内过期
|
||||
from sqlalchemy import cast, Integer, and_
|
||||
|
||||
expiring_users = db.query(User).filter(
|
||||
and_(
|
||||
User.authorization.isnot(None),
|
||||
User.authorization != "",
|
||||
User.jwt_exp.isnot(None),
|
||||
User.jwt_exp != "0",
|
||||
cast(User.jwt_exp, Integer) > current_timestamp, # 未过期
|
||||
cast(User.jwt_exp, Integer) < expiring_soon_timestamp # 7天内过期
|
||||
expiring_users = (
|
||||
db.query(User)
|
||||
.filter(
|
||||
and_(
|
||||
User.authorization.isnot(None),
|
||||
User.authorization != "",
|
||||
User.jwt_exp.isnot(None),
|
||||
User.jwt_exp != "0",
|
||||
cast(User.jwt_exp, Integer) > current_timestamp, # 未过期
|
||||
cast(User.jwt_exp, Integer) < expiring_soon_timestamp, # 7天内过期
|
||||
)
|
||||
)
|
||||
).count()
|
||||
.count()
|
||||
)
|
||||
|
||||
return {
|
||||
"users": {
|
||||
"total": total_users,
|
||||
"admin": admin_users,
|
||||
"regular": total_users - admin_users,
|
||||
"active": approved_users # 使用已审批用户数
|
||||
"active": approved_users, # 使用已审批用户数
|
||||
},
|
||||
"tasks": {
|
||||
"total": total_tasks,
|
||||
"active": active_tasks,
|
||||
"inactive": total_tasks - active_tasks
|
||||
"inactive": total_tasks - active_tasks,
|
||||
},
|
||||
"check_in_records": {
|
||||
"total": total_records,
|
||||
@@ -227,24 +230,20 @@ async def get_system_stats(
|
||||
"today_success": today_success,
|
||||
"today_failure": today_failure,
|
||||
"today_out_of_time": today_out_of_time,
|
||||
"today_unknown": today_unknown
|
||||
"today_unknown": today_unknown,
|
||||
},
|
||||
"tokens": {
|
||||
"expiring_soon": expiring_users
|
||||
}
|
||||
"tokens": {"expiring_soon": expiring_users},
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"获取统计失败: {str(e)}"
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"获取统计失败: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/users/pending", response_model=List[UserResponse], summary="获取待审批用户")
|
||||
async def get_pending_users(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_admin_user)
|
||||
db: Session = Depends(get_db), current_user: User = Depends(get_current_admin_user)
|
||||
):
|
||||
"""
|
||||
获取所有待审批的用户(需要管理员权限)
|
||||
@@ -255,7 +254,7 @@ async def get_pending_users(
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"获取待审批用户失败: {str(e)}"
|
||||
detail=f"获取待审批用户失败: {str(e)}",
|
||||
)
|
||||
|
||||
|
||||
@@ -263,7 +262,7 @@ async def get_pending_users(
|
||||
async def approve_user(
|
||||
user_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_admin_user)
|
||||
current_user: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""
|
||||
审批通过指定用户(需要管理员权限)
|
||||
@@ -272,18 +271,14 @@ async def approve_user(
|
||||
result = AdminService.approve_user(user_id, db)
|
||||
|
||||
if not result["success"]:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=result["message"]
|
||||
)
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=result["message"])
|
||||
|
||||
return result
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"审批用户失败: {str(e)}"
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"审批用户失败: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@@ -291,7 +286,7 @@ async def approve_user(
|
||||
async def reject_user(
|
||||
user_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_admin_user)
|
||||
current_user: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""
|
||||
拒绝并删除指定用户(需要管理员权限)
|
||||
@@ -300,16 +295,12 @@ async def reject_user(
|
||||
result = AdminService.reject_user(user_id, db)
|
||||
|
||||
if not result["success"]:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=result["message"]
|
||||
)
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=result["message"])
|
||||
|
||||
return result
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"拒绝用户失败: {str(e)}"
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"拒绝用户失败: {str(e)}"
|
||||
)
|
||||
|
||||
+12
-28
@@ -21,10 +21,7 @@ router = APIRouter()
|
||||
@router.post("/request_qrcode", response_model=dict, summary="请求 QQ 扫码二维码")
|
||||
@limiter.limit("10/minute") # 每分钟最多10次请求
|
||||
async def request_qrcode(
|
||||
request_obj: QRCodeRequest,
|
||||
request: Request,
|
||||
response: Response,
|
||||
db: Session = Depends(get_db)
|
||||
request_obj: QRCodeRequest, request: Request, response: Response, db: Session = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
请求 QQ 扫码二维码
|
||||
@@ -44,7 +41,7 @@ async def request_qrcode(
|
||||
raise BusinessLogicError(
|
||||
message="注册过于频繁,请 10 分钟后再试",
|
||||
error_code="RATE_LIMIT_EXCEEDED",
|
||||
status_code=429
|
||||
status_code=429,
|
||||
)
|
||||
else:
|
||||
# 生成新的 Cookie
|
||||
@@ -67,22 +64,18 @@ async def request_qrcode(
|
||||
value=reg_cookie,
|
||||
max_age=600, # 10 分钟
|
||||
httponly=True,
|
||||
samesite="lax"
|
||||
samesite="lax",
|
||||
)
|
||||
|
||||
return result
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"创建扫码会话失败: {str(e)}"
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"创建扫码会话失败: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/qrcode_status/{session_id}", response_model=dict, summary="检查二维码扫描状态")
|
||||
async def get_qrcode_status(
|
||||
session_id: str,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
async def get_qrcode_status(session_id: str, db: Session = Depends(get_db)):
|
||||
"""
|
||||
检查二维码扫描状态
|
||||
|
||||
@@ -104,15 +97,12 @@ async def get_qrcode_status(
|
||||
return result
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"查询扫码状态失败: {str(e)}"
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"查询扫码状态失败: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.delete("/qrcode_session/{session_id}", response_model=dict, summary="取消二维码登录会话")
|
||||
async def cancel_qrcode_session(
|
||||
session_id: str
|
||||
):
|
||||
async def cancel_qrcode_session(session_id: str):
|
||||
"""
|
||||
取消二维码登录会话
|
||||
|
||||
@@ -125,16 +115,12 @@ async def cancel_qrcode_session(
|
||||
return result
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"取消会话失败: {str(e)}"
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"取消会话失败: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.post("/verify_token", response_model=dict, summary="验证 JWT Token 有效性")
|
||||
async def verify_token(
|
||||
request: TokenVerifyRequest,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
async def verify_token(request: TokenVerifyRequest, db: Session = Depends(get_db)):
|
||||
"""
|
||||
验证 JWT Token 有效性(网站登录认证)
|
||||
|
||||
@@ -152,8 +138,7 @@ async def verify_token(
|
||||
return result
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"验证 Token 失败: {str(e)}"
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"验证 Token 失败: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@@ -162,7 +147,7 @@ async def verify_token(
|
||||
async def alias_login(
|
||||
login_data: AliasLoginRequest,
|
||||
request: Request, # slowapi需要的request参数
|
||||
db: Session = Depends(get_db)
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
别名+密码登录(仅限已设置密码的用户)
|
||||
@@ -187,6 +172,5 @@ async def alias_login(
|
||||
return result
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"别名登录失败: {str(e)}"
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"别名登录失败: {str(e)}"
|
||||
)
|
||||
|
||||
@@ -18,9 +18,7 @@ router = APIRouter()
|
||||
|
||||
@router.post("/manual/{task_id}", summary="手动触发打卡(异步)")
|
||||
async def manual_check_in(
|
||||
task_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
task_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""
|
||||
手动触发指定任务的打卡(异步方式,立即返回)
|
||||
@@ -31,33 +29,24 @@ async def manual_check_in(
|
||||
"""
|
||||
# 验证任务归属
|
||||
if not TaskService.verify_task_ownership(task_id, current_user.id, db):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="无权访问此任务"
|
||||
)
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="无权访问此任务")
|
||||
|
||||
task = TaskService.get_task(task_id, db)
|
||||
if not task:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="任务不存在"
|
||||
)
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="任务不存在")
|
||||
|
||||
try:
|
||||
result = CheckInService.start_async_check_in(task, "manual", db)
|
||||
return result
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"启动打卡任务失败: {str(e)}"
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"启动打卡任务失败: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/record/{record_id}/status", summary="查询打卡记录状态")
|
||||
async def get_check_in_record_status(
|
||||
record_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
record_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""
|
||||
查询指定打卡记录的状态
|
||||
@@ -73,10 +62,7 @@ async def get_check_in_record_status(
|
||||
|
||||
# 验证记录归属(通过任务归属)
|
||||
if not TaskService.verify_task_ownership(record.task_id, current_user.id, db):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="无权访问此记录"
|
||||
)
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="无权访问此记录")
|
||||
|
||||
return {
|
||||
"record_id": record.id,
|
||||
@@ -85,19 +71,25 @@ async def get_check_in_record_status(
|
||||
"response_text": record.response_text,
|
||||
"error_message": record.error_message,
|
||||
"trigger_type": record.trigger_type,
|
||||
"check_in_time": record.check_in_time
|
||||
"check_in_time": record.check_in_time,
|
||||
}
|
||||
|
||||
|
||||
@router.get("/task/{task_id}/records", response_model=PaginatedResponse[CheckInRecordResponse], summary="查看任务的打卡记录")
|
||||
@router.get(
|
||||
"/task/{task_id}/records",
|
||||
response_model=PaginatedResponse[CheckInRecordResponse],
|
||||
summary="查看任务的打卡记录",
|
||||
)
|
||||
async def get_task_check_in_records(
|
||||
task_id: int,
|
||||
skip: int = Query(0, ge=0, description="跳过记录数"),
|
||||
limit: int = Query(100, ge=1, le=500, description="限制记录数"),
|
||||
status_filter: Optional[str] = Query(None, alias="status", description="过滤状态 (success/failure)"),
|
||||
status_filter: Optional[str] = Query(
|
||||
None, alias="status", description="过滤状态 (success/failure)"
|
||||
),
|
||||
trigger_type: Optional[str] = Query(None, description="过滤触发类型 (scheduler/manual)"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
查看指定任务的打卡记录
|
||||
@@ -112,36 +104,33 @@ async def get_task_check_in_records(
|
||||
"""
|
||||
# 验证任务归属
|
||||
if not TaskService.verify_task_ownership(task_id, current_user.id, db):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="无权访问此任务"
|
||||
)
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="无权访问此任务")
|
||||
|
||||
try:
|
||||
records, total = CheckInService.get_task_records(
|
||||
task_id, db, skip, limit, status_filter, trigger_type
|
||||
)
|
||||
return PaginatedResponse(
|
||||
records=records,
|
||||
total=total,
|
||||
skip=skip,
|
||||
limit=limit
|
||||
)
|
||||
return PaginatedResponse(records=records, total=total, skip=skip, limit=limit)
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"获取打卡记录失败: {str(e)}"
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"获取打卡记录失败: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/my-records", response_model=PaginatedResponse[CheckInRecordResponse], summary="查看当前用户的所有打卡记录")
|
||||
@router.get(
|
||||
"/my-records",
|
||||
response_model=PaginatedResponse[CheckInRecordResponse],
|
||||
summary="查看当前用户的所有打卡记录",
|
||||
)
|
||||
async def get_my_check_in_records(
|
||||
skip: int = Query(0, ge=0, description="跳过记录数"),
|
||||
limit: int = Query(100, ge=1, le=500, description="限制记录数"),
|
||||
status_filter: Optional[str] = Query(None, alias="status", description="过滤状态 (success/failure)"),
|
||||
status_filter: Optional[str] = Query(
|
||||
None, alias="status", description="过滤状态 (success/failure)"
|
||||
),
|
||||
trigger_type: Optional[str] = Query(None, description="过滤触发类型 (scheduler/manual)"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
查看当前用户所有任务的打卡记录
|
||||
@@ -155,28 +144,27 @@ async def get_my_check_in_records(
|
||||
records, total = CheckInService.get_user_records(
|
||||
current_user.id, db, skip, limit, status_filter, trigger_type
|
||||
)
|
||||
return PaginatedResponse(
|
||||
records=records,
|
||||
total=total,
|
||||
skip=skip,
|
||||
limit=limit
|
||||
)
|
||||
return PaginatedResponse(records=records, total=total, skip=skip, limit=limit)
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"获取打卡记录失败: {str(e)}"
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"获取打卡记录失败: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
|
||||
@router.get("/records", response_model=PaginatedResponse[CheckInRecordResponse], summary="查看所有打卡记录(管理员)")
|
||||
@router.get(
|
||||
"/records",
|
||||
response_model=PaginatedResponse[CheckInRecordResponse],
|
||||
summary="查看所有打卡记录(管理员)",
|
||||
)
|
||||
async def get_all_check_in_records(
|
||||
skip: int = Query(0, ge=0, description="跳过记录数"),
|
||||
limit: int = Query(100, ge=1, le=500, description="限制记录数"),
|
||||
task_id: Optional[int] = Query(None, description="过滤任务 ID"),
|
||||
status_filter: Optional[str] = Query(None, alias="status", description="过滤状态 (success/failure)"),
|
||||
status_filter: Optional[str] = Query(
|
||||
None, alias="status", description="过滤状态 (success/failure)"
|
||||
),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_admin_user)
|
||||
current_user: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""
|
||||
查看所有打卡记录(需要管理员权限)
|
||||
@@ -189,26 +177,24 @@ async def get_all_check_in_records(
|
||||
try:
|
||||
records, total = CheckInService.get_all_records(db, skip, limit, task_id, status_filter)
|
||||
# 为每条记录添加用户和任务信息
|
||||
enriched_records = [CheckInService.enrich_record_with_user_task_info(record, db) for record in records]
|
||||
return PaginatedResponse(
|
||||
records=enriched_records,
|
||||
total=total,
|
||||
skip=skip,
|
||||
limit=limit
|
||||
)
|
||||
enriched_records = [
|
||||
CheckInService.enrich_record_with_user_task_info(record, db) for record in records
|
||||
]
|
||||
return PaginatedResponse(records=enriched_records, total=total, skip=skip, limit=limit)
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"获取打卡记录失败: {str(e)}"
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"获取打卡记录失败: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/records/count", summary="获取打卡记录统计(管理员)")
|
||||
async def get_check_in_records_count(
|
||||
task_id: Optional[int] = Query(None, description="过滤任务 ID"),
|
||||
status_filter: Optional[str] = Query(None, alias="status", description="过滤状态 (success/failure)"),
|
||||
status_filter: Optional[str] = Query(
|
||||
None, alias="status", description="过滤状态 (success/failure)"
|
||||
),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_admin_user)
|
||||
current_user: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""
|
||||
获取打卡记录统计(需要管理员权限)
|
||||
@@ -229,6 +215,5 @@ async def get_check_in_records_count(
|
||||
return {"total": total}
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"获取统计失败: {str(e)}"
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"获取统计失败: {str(e)}"
|
||||
)
|
||||
|
||||
+19
-34
@@ -14,15 +14,18 @@ router = APIRouter()
|
||||
|
||||
class CronValidateRequest(BaseModel):
|
||||
"""Cron 表达式验证请求"""
|
||||
|
||||
cron_expression: str = Field(..., min_length=9, description="Crontab 表达式")
|
||||
|
||||
|
||||
# create_task_from_template: 已在 templates.py 中定义
|
||||
|
||||
|
||||
@router.get("/", response_model=List[TaskResponse], summary="获取当前用户的任务列表")
|
||||
async def get_tasks(
|
||||
include_inactive: bool = True,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db)
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
获取当前用户的所有打卡任务
|
||||
@@ -36,16 +39,13 @@ async def get_tasks(
|
||||
return enriched_tasks
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"获取任务列表失败: {str(e)}"
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"获取任务列表失败: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/{task_id}", response_model=TaskResponse, summary="获取任务详情")
|
||||
async def get_task(
|
||||
task_id: int,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db)
|
||||
task_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
获取指定任务的详情
|
||||
@@ -66,7 +66,7 @@ async def update_task(
|
||||
task_id: int,
|
||||
task_data: TaskUpdate,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db)
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
更新指定任务的信息
|
||||
@@ -82,19 +82,14 @@ async def update_task(
|
||||
task = TaskService.update_task(task_id, task_data, db)
|
||||
|
||||
if not task:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="任务不存在"
|
||||
)
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="任务不存在")
|
||||
|
||||
return task
|
||||
|
||||
|
||||
@router.delete("/{task_id}", status_code=status.HTTP_204_NO_CONTENT, summary="删除任务")
|
||||
async def delete_task(
|
||||
task_id: int,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db)
|
||||
task_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
删除指定任务
|
||||
@@ -110,17 +105,12 @@ async def delete_task(
|
||||
success = TaskService.delete_task(task_id, db)
|
||||
|
||||
if not success:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="任务不存在"
|
||||
)
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="任务不存在")
|
||||
|
||||
|
||||
@router.post("/{task_id}/toggle", response_model=TaskResponse, summary="切换任务启用状态")
|
||||
async def toggle_task(
|
||||
task_id: int,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db)
|
||||
task_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
切换任务的启用/禁用状态
|
||||
@@ -136,10 +126,7 @@ async def toggle_task(
|
||||
task = TaskService.toggle_task(task_id, db)
|
||||
|
||||
if not task:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="任务不存在"
|
||||
)
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="任务不存在")
|
||||
|
||||
return task
|
||||
|
||||
@@ -167,8 +154,7 @@ async def validate_cron_expression(request: CronValidateRequest):
|
||||
|
||||
if not cron_expr:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="cron_expression 是必需的"
|
||||
status_code=status.HTTP_400_BAD_REQUEST, detail="cron_expression 是必需的"
|
||||
)
|
||||
|
||||
try:
|
||||
@@ -179,18 +165,17 @@ async def validate_cron_expression(request: CronValidateRequest):
|
||||
|
||||
# 生成接下来的 5 个执行时间
|
||||
cron = croniter(cron_expr, datetime.now())
|
||||
next_times = [cron.get_next(datetime).strftime('%Y-%m-%d %H:%M:%S') for _ in range(5)]
|
||||
next_times = [cron.get_next(datetime).strftime("%Y-%m-%d %H:%M:%S") for _ in range(5)]
|
||||
|
||||
return {
|
||||
"valid": True,
|
||||
"message": "有效的 Crontab 表达式",
|
||||
"next_times": next_times,
|
||||
"description": generate_cron_description(cron_expr)
|
||||
"description": generate_cron_description(cron_expr),
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"无效的 Crontab 表达式: {str(e)}"
|
||||
status_code=status.HTTP_400_BAD_REQUEST, detail=f"无效的 Crontab 表达式: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@@ -203,11 +188,11 @@ def generate_cron_description(cron_expr: str) -> str:
|
||||
minute, hour, day, month, dow = parts
|
||||
|
||||
descriptions = []
|
||||
if hour == '*' and minute == '*':
|
||||
if hour == "*" and minute == "*":
|
||||
descriptions.append("每分钟")
|
||||
elif hour == '*':
|
||||
elif hour == "*":
|
||||
descriptions.append(f"每小时的第 {minute} 分钟")
|
||||
elif day == '*' and month == '*' and dow == '*':
|
||||
elif day == "*" and month == "*" and dow == "*":
|
||||
descriptions.append(f"每天 {hour}:{minute:0>2}")
|
||||
else:
|
||||
descriptions.append(f"复杂的时间表: {cron_expr}")
|
||||
|
||||
@@ -9,7 +9,7 @@ from backend.schemas.template import (
|
||||
TemplateUpdate,
|
||||
TemplateResponse,
|
||||
TaskFromTemplateRequest,
|
||||
TemplatePreviewResponse
|
||||
TemplatePreviewResponse,
|
||||
)
|
||||
from backend.schemas.task import TaskResponse
|
||||
from backend.services.template_service import TemplateService
|
||||
@@ -23,7 +23,7 @@ async def get_all_templates(
|
||||
limit: int = Query(100, ge=1, le=500, description="限制记录数"),
|
||||
is_active: Optional[bool] = Query(None, description="过滤启用状态"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
获取所有模板列表(普通用户可访问)
|
||||
@@ -37,8 +37,7 @@ async def get_all_templates(
|
||||
return templates
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"获取模板列表失败: {str(e)}"
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"获取模板列表失败: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@@ -47,7 +46,7 @@ async def get_active_templates(
|
||||
skip: int = Query(0, ge=0, description="跳过记录数"),
|
||||
limit: int = Query(100, ge=1, le=500, description="限制记录数"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
获取所有启用的模板(用户创建任务时使用)
|
||||
@@ -60,16 +59,13 @@ async def get_active_templates(
|
||||
return templates
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"获取模板列表失败: {str(e)}"
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"获取模板列表失败: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/{template_id}", response_model=TemplateResponse, summary="获取单个模板详情")
|
||||
async def get_template(
|
||||
template_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
template_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""
|
||||
获取单个模板的详细信息(普通用户只能访问启用的模板)
|
||||
@@ -78,26 +74,22 @@ async def get_template(
|
||||
"""
|
||||
template = TemplateService.get_template(template_id, db)
|
||||
if not template:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="模板不存在"
|
||||
)
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="模板不存在")
|
||||
|
||||
# 普通用户只能访问启用的模板
|
||||
if not current_user.is_admin and template.is_active is not True:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="无权访问此模板"
|
||||
)
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="无权访问此模板")
|
||||
|
||||
return template
|
||||
|
||||
|
||||
@router.get("/{template_id}/preview", response_model=TemplatePreviewResponse, summary="预览模板生成的 payload")
|
||||
@router.get(
|
||||
"/{template_id}/preview",
|
||||
response_model=TemplatePreviewResponse,
|
||||
summary="预览模板生成的 payload",
|
||||
)
|
||||
async def preview_template(
|
||||
template_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
template_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""
|
||||
预览模板生成的 payload(使用默认值,普通用户只能访问启用的模板)
|
||||
@@ -106,17 +98,11 @@ async def preview_template(
|
||||
"""
|
||||
template = TemplateService.get_template(template_id, db)
|
||||
if not template:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="模板不存在"
|
||||
)
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="模板不存在")
|
||||
|
||||
# 普通用户只能访问启用的模板
|
||||
if not current_user.is_admin and template.is_active is not True:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="无权访问此模板"
|
||||
)
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="无权访问此模板")
|
||||
|
||||
try:
|
||||
preview_payload = TemplateService.generate_preview_payload(template, db)
|
||||
@@ -127,12 +113,11 @@ async def preview_template(
|
||||
"template_id": template.id,
|
||||
"template_name": template.name,
|
||||
"preview_payload": preview_payload,
|
||||
"field_config": merged_config
|
||||
"field_config": merged_config,
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"生成预览失败: {str(e)}"
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"生成预览失败: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@@ -140,7 +125,7 @@ async def preview_template(
|
||||
async def create_template(
|
||||
template_data: TemplateCreate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_admin_user)
|
||||
current_user: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""
|
||||
创建新的打卡任务模板(仅管理员)
|
||||
@@ -158,7 +143,7 @@ async def update_template(
|
||||
template_id: int,
|
||||
template_data: TemplateUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_admin_user)
|
||||
current_user: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""
|
||||
更新模板信息(仅管理员)
|
||||
@@ -176,7 +161,7 @@ async def update_template(
|
||||
async def delete_template(
|
||||
template_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_admin_user)
|
||||
current_user: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""
|
||||
删除模板(仅管理员)
|
||||
@@ -191,7 +176,7 @@ async def delete_template(
|
||||
async def create_task_from_template(
|
||||
request: TaskFromTemplateRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
从模板创建打卡任务
|
||||
@@ -209,6 +194,6 @@ async def create_task_from_template(
|
||||
user_id=current_user.id,
|
||||
task_name=request.task_name,
|
||||
db=db,
|
||||
cron_expression=request.cron_expression
|
||||
cron_expression=request.cron_expression,
|
||||
)
|
||||
return task
|
||||
|
||||
+36
-40
@@ -3,7 +3,13 @@ from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from backend.models import get_db, User
|
||||
from backend.schemas.user import UserCreate, UserUpdate, UserResponse, TokenStatus, UserUpdateProfile
|
||||
from backend.schemas.user import (
|
||||
UserCreate,
|
||||
UserUpdate,
|
||||
UserResponse,
|
||||
TokenStatus,
|
||||
UserUpdateProfile,
|
||||
)
|
||||
from backend.schemas.task import TaskResponse
|
||||
from backend.services.user_service import UserService
|
||||
from backend.services.task_service import TaskService
|
||||
@@ -13,11 +19,16 @@ from backend.exceptions import ValidationError, AuthorizationError, ResourceNotF
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.post("", response_model=UserResponse, status_code=status.HTTP_201_CREATED, summary="创建用户(管理员)")
|
||||
@router.post(
|
||||
"",
|
||||
response_model=UserResponse,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
summary="创建用户(管理员)",
|
||||
)
|
||||
async def create_user(
|
||||
user_data: UserCreate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_admin_user)
|
||||
current_user: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""
|
||||
创建用户(需要管理员权限)
|
||||
@@ -33,15 +44,12 @@ async def create_user(
|
||||
raise ValidationError(str(e))
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"创建用户失败: {str(e)}"
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"创建用户失败: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/me", response_model=UserResponse, summary="获取当前用户信息")
|
||||
async def get_current_user_info(
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
async def get_current_user_info(current_user: User = Depends(get_current_user)):
|
||||
"""
|
||||
获取当前登录用户的信息
|
||||
"""
|
||||
@@ -61,9 +69,7 @@ async def get_current_user_info(
|
||||
|
||||
|
||||
@router.get("/me/status", response_model=dict, summary="获取当前用户审批状态")
|
||||
async def get_user_status(
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
async def get_user_status(current_user: User = Depends(get_current_user)):
|
||||
"""
|
||||
获取用户审批状态(不要求审批通过)
|
||||
"""
|
||||
@@ -71,7 +77,7 @@ async def get_user_status(
|
||||
"user_id": current_user.id,
|
||||
"alias": current_user.alias,
|
||||
"is_approved": current_user.is_approved,
|
||||
"created_at": current_user.created_at.isoformat() if current_user.created_at else None
|
||||
"created_at": current_user.created_at.isoformat() if current_user.created_at else None,
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +85,7 @@ async def get_user_status(
|
||||
async def update_current_user_profile(
|
||||
profile_data: UserUpdateProfile,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
更新当前用户的个人信息
|
||||
@@ -99,15 +105,12 @@ async def update_current_user_profile(
|
||||
raise ValidationError(str(e))
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"更新个人信息失败: {str(e)}"
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"更新个人信息失败: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/me/token_status", response_model=TokenStatus, summary="获取当前用户打卡 Token 状态")
|
||||
async def get_current_user_token_status(
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
async def get_current_user_token_status(current_user: User = Depends(get_current_user)):
|
||||
"""
|
||||
获取当前用户的打卡 Token 状态(authorization token,非 JWT)
|
||||
|
||||
@@ -123,7 +126,7 @@ async def get_current_user_token_status(
|
||||
"jwt_exp": current_user.jwt_exp,
|
||||
"expires_at": result.get("expires_at"),
|
||||
"days_until_expiry": result.get("days_remaining"),
|
||||
"expiring_soon": result.get("expiring_soon", False)
|
||||
"expiring_soon": result.get("expiring_soon", False),
|
||||
}
|
||||
|
||||
|
||||
@@ -131,7 +134,7 @@ async def get_current_user_token_status(
|
||||
async def get_current_user_tasks(
|
||||
include_inactive: bool = Query(True, description="是否包含未启用的任务"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
获取当前登录用户的所有打卡任务
|
||||
@@ -143,8 +146,7 @@ async def get_current_user_tasks(
|
||||
return tasks
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"获取任务列表失败: {str(e)}"
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"获取任务列表失败: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@@ -155,7 +157,7 @@ async def get_all_users(
|
||||
search: Optional[str] = Query(None, description="搜索关键词(alias)"),
|
||||
role: Optional[str] = Query(None, description="过滤角色 (user/admin)"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_admin_user)
|
||||
current_user: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""
|
||||
获取所有用户列表(需要管理员权限)
|
||||
@@ -170,16 +172,13 @@ async def get_all_users(
|
||||
return users
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"获取用户列表失败: {str(e)}"
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"获取用户列表失败: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/{user_id}", response_model=UserResponse, summary="获取指定用户")
|
||||
async def get_user(
|
||||
user_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
user_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""
|
||||
获取指定用户信息
|
||||
@@ -203,7 +202,7 @@ async def update_user(
|
||||
user_id: int,
|
||||
user_data: UserUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
更新用户信息
|
||||
@@ -236,28 +235,26 @@ async def update_user(
|
||||
new_approved_value = user.is_approved
|
||||
is_approved_now = True if new_approved_value else False
|
||||
|
||||
is_admin = (current_user.role == "admin")
|
||||
needs_notification = (is_admin and (not was_approved_before) and is_approved_now)
|
||||
is_admin = current_user.role == "admin"
|
||||
needs_notification = is_admin and (not was_approved_before) and is_approved_now
|
||||
|
||||
if needs_notification:
|
||||
try:
|
||||
from backend.services.email_service import EmailService
|
||||
|
||||
EmailService.notify_user_approved(user)
|
||||
except Exception as e:
|
||||
# 邮件发送失败不影响审批操作
|
||||
import logging
|
||||
|
||||
logging.getLogger(__name__).error(f"发送审批通过邮件失败: {e}")
|
||||
|
||||
return user
|
||||
except ValueError as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=str(e)
|
||||
)
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"更新用户失败: {str(e)}"
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"更新用户失败: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@@ -265,7 +262,7 @@ async def update_user(
|
||||
async def delete_user(
|
||||
user_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_admin_user)
|
||||
current_user: User = Depends(get_current_admin_user),
|
||||
):
|
||||
"""
|
||||
删除用户(需要管理员权限)
|
||||
@@ -277,6 +274,5 @@ async def delete_user(
|
||||
raise ResourceNotFoundError(str(e))
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"删除用户失败: {str(e)}"
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"删除用户失败: {str(e)}"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user