feat: 初始化考培练系统项目
- 从服务器拉取完整代码 - 按框架规范整理项目结构 - 配置 Drone CI 测试环境部署 - 包含后端(FastAPI)、前端(Vue3)、管理端 技术栈: Vue3 + TypeScript + FastAPI + MySQL
This commit is contained in:
236
backend/app/api/v1/endpoints/employee_sync.py
Normal file
236
backend/app/api/v1/endpoints/employee_sync.py
Normal file
@@ -0,0 +1,236 @@
|
||||
"""
|
||||
员工同步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)}"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user