""" 员工同步API接口 提供从钉钉员工表同步员工数据的功能 """ from typing import Any, Dict from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.ext.asyncio import AsyncSession from app.core.logger import get_logger from app.core.deps import get_current_user, get_db from app.services.employee_sync_service import EmployeeSyncService from app.models.user import User logger = get_logger(__name__) router = APIRouter() @router.post("/sync", summary="执行员工同步") async def sync_employees( *, db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user) ) -> Dict[str, Any]: """ 从钉钉员工表同步在职员工数据到考培练系统 权限要求: 仅管理员可执行 同步内容: - 创建用户账号(用户名=手机号,初始密码=123456) - 创建部门团队 - 创建岗位并关联用户 - 设置领导为团队负责人 Returns: 同步结果统计 """ # 权限检查:仅管理员可执行 if current_user.role != 'admin': raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="只有管理员可以执行员工同步" ) logger.info(f"管理员 {current_user.username} 开始执行员工同步") try: async with EmployeeSyncService(db) as sync_service: stats = await sync_service.sync_employees() return { "success": True, "message": "员工同步完成", "data": stats } except Exception as e: logger.error(f"员工同步失败: {str(e)}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"员工同步失败: {str(e)}" ) @router.get("/preview", summary="预览待同步员工数据") async def preview_sync_data( *, db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user) ) -> Dict[str, Any]: """ 预览待同步的员工数据(不执行实际同步) 权限要求: 仅管理员可查看 Returns: 预览数据,包括员工列表、部门列表、岗位列表等 """ # 权限检查:仅管理员可查看 if current_user.role != 'admin': raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="只有管理员可以预览员工数据" ) logger.info(f"管理员 {current_user.username} 预览员工同步数据") try: async with EmployeeSyncService(db) as sync_service: preview_data = await sync_service.preview_sync_data() return { "success": True, "message": "预览数据获取成功", "data": preview_data } except Exception as e: logger.error(f"预览数据获取失败: {str(e)}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"预览数据获取失败: {str(e)}" ) @router.post("/incremental-sync", summary="增量同步员工") async def incremental_sync_employees( *, db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user) ) -> Dict[str, Any]: """ 增量同步钉钉员工数据 功能说明: - 新增:钉钉有但系统没有的员工 - 删除:系统有但钉钉没有的员工(物理删除) - 跳过:两边都存在的员工(不修改任何信息) 权限要求: 管理员(admin 或 manager)可执行 Returns: 同步结果统计 """ # 权限检查:管理员或经理可执行 if current_user.role not in ['admin', 'manager']: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="只有管理员可以执行员工同步" ) logger.info(f"用户 {current_user.username} ({current_user.role}) 开始执行增量员工同步") try: async with EmployeeSyncService(db) as sync_service: stats = await sync_service.incremental_sync_employees() return { "success": True, "message": "增量同步完成", "data": { "added_count": stats['added_count'], "deleted_count": stats['deleted_count'], "skipped_count": stats['skipped_count'], "added_users": stats['added_users'], "deleted_users": stats['deleted_users'], "errors": stats['errors'], "duration": stats['duration'] } } except Exception as e: logger.error(f"增量同步失败: {str(e)}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"增量同步失败: {str(e)}" ) @router.get("/status", summary="查询同步状态") async def get_sync_status( *, db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user) ) -> Dict[str, Any]: """ 查询当前系统的用户、团队、岗位统计信息 Returns: 统计信息 """ from sqlalchemy import select, func from app.models.user import User, Team from app.models.position import Position try: # 统计用户数量 user_count_stmt = select(func.count(User.id)).where(User.is_deleted == False) user_result = await db.execute(user_count_stmt) total_users = user_result.scalar() # 统计各角色用户数量 admin_count_stmt = select(func.count(User.id)).where( User.is_deleted == False, User.role == 'admin' ) admin_result = await db.execute(admin_count_stmt) admin_count = admin_result.scalar() manager_count_stmt = select(func.count(User.id)).where( User.is_deleted == False, User.role == 'manager' ) manager_result = await db.execute(manager_count_stmt) manager_count = manager_result.scalar() trainee_count_stmt = select(func.count(User.id)).where( User.is_deleted == False, User.role == 'trainee' ) trainee_result = await db.execute(trainee_count_stmt) trainee_count = trainee_result.scalar() # 统计团队数量 team_count_stmt = select(func.count(Team.id)).where(Team.is_deleted == False) team_result = await db.execute(team_count_stmt) total_teams = team_result.scalar() # 统计岗位数量 position_count_stmt = select(func.count(Position.id)).where(Position.is_deleted == False) position_result = await db.execute(position_count_stmt) total_positions = position_result.scalar() return { "success": True, "data": { "users": { "total": total_users, "admin": admin_count, "manager": manager_count, "trainee": trainee_count }, "teams": total_teams, "positions": total_positions } } except Exception as e: logger.error(f"查询统计信息失败: {str(e)}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"查询统计信息失败: {str(e)}" ) @router.get("/scheduler/jobs", summary="查看定时同步任务") async def get_scheduler_jobs( *, current_user: User = Depends(get_current_user) ) -> Dict[str, Any]: """ 查看定时同步任务列表 权限要求: 仅管理员可查看 Returns: 定时任务列表,包含下次执行时间等信息 """ if current_user.role != 'admin': raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="只有管理员可以查看定时任务" ) try: from app.core.scheduler import scheduler_manager jobs = scheduler_manager.get_jobs() return { "success": True, "message": "获取定时任务成功", "data": { "auto_sync_enabled": scheduler_manager.AUTO_SYNC_ENABLED, "incremental_sync_interval_minutes": scheduler_manager.INCREMENTAL_SYNC_INTERVAL_MINUTES, "full_sync_hour": scheduler_manager.FULL_SYNC_HOUR, "jobs": jobs } } except Exception as e: logger.error(f"获取定时任务失败: {str(e)}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"获取定时任务失败: {str(e)}" ) @router.post("/scheduler/trigger/{job_id}", summary="手动触发定时任务") async def trigger_scheduler_job( *, job_id: str, current_user: User = Depends(get_current_user) ) -> Dict[str, Any]: """ 手动触发指定的定时任务 权限要求: 仅管理员可执行 Args: job_id: 任务ID (employee_incremental_sync 或 employee_full_sync) Returns: 任务执行结果 """ if current_user.role != 'admin': raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="只有管理员可以触发定时任务" ) logger.info(f"管理员 {current_user.username} 手动触发定时任务: {job_id}") try: from app.core.scheduler import scheduler_manager result = await scheduler_manager.trigger_job(job_id) return { "success": True, "message": f"任务 {job_id} 执行完成", "data": result } except ValueError as e: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=str(e) ) except Exception as e: logger.error(f"触发定时任务失败: {str(e)}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"触发定时任务失败: {str(e)}" )