mirror of
https://github.com/Cccc-owo/CheckInApp.git
synced 2026-06-17 14:06:28 +00:00
827c9198ae
- Updated Vite configuration to manually chunk Ant Design Vue for improved dependency management. - Added a comprehensive migration testing checklist for transitioning from Element Plus 2.13.0 to Ant Design Vue 4.x, covering various components and functionalities.
258 lines
7.5 KiB
Python
258 lines
7.5 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, status
|
||
from sqlalchemy.orm import Session
|
||
from typing import List
|
||
from datetime import datetime, timedelta
|
||
from pydantic import BaseModel, Field
|
||
|
||
from backend.models import get_db, User
|
||
from backend.schemas.task import TaskCreate, TaskUpdate, TaskResponse
|
||
from backend.services.task_service import TaskService
|
||
from backend.dependencies import get_current_user
|
||
|
||
router = APIRouter()
|
||
|
||
|
||
class CronValidateRequest(BaseModel):
|
||
"""Cron 表达式验证请求"""
|
||
cron_expression: str = Field(..., min_length=9, description="Crontab 表达式")
|
||
|
||
|
||
@router.post("/", response_model=TaskResponse, status_code=status.HTTP_201_CREATED, summary="创建打卡任务")
|
||
async def create_task(
|
||
task_data: TaskCreate,
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db)
|
||
):
|
||
"""
|
||
创建新的打卡任务(基于模板)
|
||
|
||
现在的任务创建流程:
|
||
1. 管理员在后台创建模板(包含完整的 payload_config)
|
||
2. 用户基于模板创建任务,填写字段值
|
||
3. 系统自动生成完整的 payload_config
|
||
|
||
注意:直接创建任务的方式已废弃,请使用模板接口。
|
||
"""
|
||
try:
|
||
task = TaskService.create_task(current_user.id, task_data, db)
|
||
return task
|
||
except ValueError as 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)}"
|
||
)
|
||
|
||
|
||
@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)
|
||
):
|
||
"""
|
||
获取当前用户的所有打卡任务
|
||
|
||
- **include_inactive**: 是否包含未启用的任务(默认 true)
|
||
"""
|
||
try:
|
||
tasks = TaskService.get_user_tasks(current_user.id, db, include_inactive)
|
||
# 为每个任务添加额外信息
|
||
enriched_tasks = [TaskService.enrich_task_with_check_in_info(task, db) for task in tasks]
|
||
return enriched_tasks
|
||
except Exception as e:
|
||
raise HTTPException(
|
||
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)
|
||
):
|
||
"""
|
||
获取指定任务的详情
|
||
|
||
需要验证任务属于当前用户
|
||
"""
|
||
# 验证任务归属
|
||
if not TaskService.verify_task_ownership(task_id, current_user.id, db):
|
||
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="任务不存在"
|
||
)
|
||
|
||
return task
|
||
|
||
|
||
@router.put("/{task_id}", response_model=TaskResponse, summary="更新任务")
|
||
async def update_task(
|
||
task_id: int,
|
||
task_data: TaskUpdate,
|
||
current_user: User = Depends(get_current_user),
|
||
db: Session = Depends(get_db)
|
||
):
|
||
"""
|
||
更新指定任务的信息
|
||
|
||
需要验证任务属于当前用户
|
||
"""
|
||
# 验证任务归属
|
||
if not TaskService.verify_task_ownership(task_id, current_user.id, db):
|
||
raise HTTPException(
|
||
status_code=status.HTTP_403_FORBIDDEN,
|
||
detail="无权访问此任务"
|
||
)
|
||
|
||
task = TaskService.update_task(task_id, task_data, db)
|
||
|
||
if not task:
|
||
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)
|
||
):
|
||
"""
|
||
删除指定任务
|
||
|
||
需要验证任务属于当前用户,删除后会同时删除所有关联的打卡记录
|
||
"""
|
||
# 验证任务归属
|
||
if not TaskService.verify_task_ownership(task_id, current_user.id, db):
|
||
raise HTTPException(
|
||
status_code=status.HTTP_403_FORBIDDEN,
|
||
detail="无权访问此任务"
|
||
)
|
||
|
||
success = TaskService.delete_task(task_id, db)
|
||
|
||
if not success:
|
||
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)
|
||
):
|
||
"""
|
||
切换任务的启用/禁用状态
|
||
|
||
需要验证任务属于当前用户
|
||
"""
|
||
# 验证任务归属
|
||
if not TaskService.verify_task_ownership(task_id, current_user.id, db):
|
||
raise HTTPException(
|
||
status_code=status.HTTP_403_FORBIDDEN,
|
||
detail="无权访问此任务"
|
||
)
|
||
|
||
task = TaskService.toggle_task(task_id, db)
|
||
|
||
if not task:
|
||
raise HTTPException(
|
||
status_code=status.HTTP_404_NOT_FOUND,
|
||
detail="任务不存在"
|
||
)
|
||
|
||
return task
|
||
|
||
|
||
@router.post("/validate-cron", summary="验证 Crontab 表达式")
|
||
async def validate_cron_expression(request: CronValidateRequest):
|
||
"""
|
||
验证 Crontab 表达式并预览下一个执行时间
|
||
|
||
请求体: {"cron_expression": "0 20 * * *"}
|
||
|
||
返回:
|
||
{
|
||
"valid": true,
|
||
"message": "有效的 Crontab 表达式",
|
||
"next_times": [
|
||
"2024-01-02 20:00:00",
|
||
"2024-01-03 20:00:00",
|
||
...
|
||
],
|
||
"description": "每天 20:00"
|
||
}
|
||
"""
|
||
cron_expr = request.cron_expression.strip()
|
||
|
||
if not cron_expr:
|
||
raise HTTPException(
|
||
status_code=status.HTTP_400_BAD_REQUEST,
|
||
detail="cron_expression 是必需的"
|
||
)
|
||
|
||
try:
|
||
from croniter import croniter
|
||
|
||
if not croniter.is_valid(cron_expr):
|
||
raise ValueError("无效的格式")
|
||
|
||
# 生成接下来的 5 个执行时间
|
||
cron = croniter(cron_expr, datetime.now())
|
||
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)
|
||
}
|
||
except Exception as e:
|
||
raise HTTPException(
|
||
status_code=status.HTTP_400_BAD_REQUEST,
|
||
detail=f"无效的 Crontab 表达式: {str(e)}"
|
||
)
|
||
|
||
|
||
def generate_cron_description(cron_expr: str) -> str:
|
||
"""生成 Crontab 表达式的人类可读描述"""
|
||
parts = cron_expr.split()
|
||
if len(parts) != 5:
|
||
return cron_expr
|
||
|
||
minute, hour, day, month, dow = parts
|
||
|
||
descriptions = []
|
||
if hour == '*' and minute == '*':
|
||
descriptions.append("每分钟")
|
||
elif hour == '*':
|
||
descriptions.append(f"每小时的第 {minute} 分钟")
|
||
elif day == '*' and month == '*' and dow == '*':
|
||
descriptions.append(f"每天 {hour}:{minute:0>2}")
|
||
else:
|
||
descriptions.append(f"复杂的时间表: {cron_expr}")
|
||
|
||
return ", ".join(descriptions)
|