- 从服务器拉取完整代码 - 按框架规范整理项目结构 - 配置 Drone CI 测试环境部署 - 包含后端(FastAPI)、前端(Vue3)、管理端 技术栈: Vue3 + TypeScript + FastAPI + MySQL
237 lines
7.4 KiB
Python
237 lines
7.4 KiB
Python
"""
|
||
员工同步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)}"
|
||
)
|
||
|