422 lines
14 KiB
Python
422 lines
14 KiB
Python
from typing import Optional, List
|
|
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
|
from sqlalchemy.orm import Session
|
|
from pydantic import BaseModel, Field
|
|
from datetime import date
|
|
from decimal import Decimal
|
|
|
|
from app.database import get_db
|
|
from app.models import User, Employee, OperationLog
|
|
from app.routers.auth import get_current_user, get_current_admin
|
|
|
|
router = APIRouter(prefix="/employees", tags=["员工管理"])
|
|
|
|
|
|
# Pydantic模型
|
|
class EmployeeTarget(BaseModel):
|
|
monthly_target: Decimal = Field(default=0)
|
|
quarterly_target: Decimal = Field(default=0)
|
|
half_year_target: Decimal = Field(default=0)
|
|
yearly_target: Decimal = Field(default=0)
|
|
|
|
|
|
class EmployeeBase(BaseModel):
|
|
name: str
|
|
phone: Optional[str] = None
|
|
email: Optional[str] = None
|
|
department: Optional[str] = None
|
|
position: Optional[str] = None
|
|
base_salary: Decimal = Field(default=4000)
|
|
hire_date: Optional[date] = None
|
|
|
|
|
|
class EmployeeCreate(EmployeeBase):
|
|
username: str
|
|
password: str
|
|
targets: Optional[EmployeeTarget] = None
|
|
|
|
|
|
class EmployeeUpdate(BaseModel):
|
|
name: Optional[str] = None
|
|
phone: Optional[str] = None
|
|
email: Optional[str] = None
|
|
department: Optional[str] = None
|
|
position: Optional[str] = None
|
|
base_salary: Optional[Decimal] = None
|
|
hire_date: Optional[date] = None
|
|
status: Optional[int] = None
|
|
|
|
|
|
class EmployeeResponse(BaseModel):
|
|
id: int
|
|
user_id: int
|
|
username: str
|
|
name: str
|
|
phone: Optional[str]
|
|
email: Optional[str]
|
|
department: Optional[str]
|
|
position: Optional[str]
|
|
base_salary: Decimal
|
|
monthly_target: Decimal
|
|
quarterly_target: Decimal
|
|
half_year_target: Decimal
|
|
yearly_target: Decimal
|
|
hire_date: Optional[date]
|
|
status: int
|
|
created_at: str
|
|
updated_at: str
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class EmployeeListResponse(BaseModel):
|
|
items: List[EmployeeResponse]
|
|
total: int
|
|
page: int
|
|
page_size: int
|
|
|
|
|
|
def log_operation(db: Session, user_id: int, action: str, target_type: str, target_id: int,
|
|
old_value: Optional[str] = None, new_value: Optional[str] = None):
|
|
"""记录操作日志"""
|
|
log = OperationLog(
|
|
user_id=user_id,
|
|
action=action,
|
|
target_type=target_type,
|
|
target_id=target_id,
|
|
old_value=old_value,
|
|
new_value=new_value
|
|
)
|
|
db.add(log)
|
|
db.commit()
|
|
|
|
|
|
@router.get("", response_model=dict)
|
|
def get_employees(
|
|
page: int = Query(1, ge=1),
|
|
page_size: int = Query(20, ge=1, le=1000),
|
|
search: Optional[str] = None,
|
|
department: Optional[str] = None,
|
|
status: Optional[int] = None,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""获取员工列表(支持分页、搜索)"""
|
|
query = db.query(Employee).join(User)
|
|
|
|
# 搜索条件
|
|
if search:
|
|
query = query.filter(
|
|
(User.name.contains(search)) |
|
|
(User.username.contains(search)) |
|
|
(User.phone.contains(search))
|
|
)
|
|
|
|
# 部门筛选
|
|
if department:
|
|
query = query.filter(Employee.department == department)
|
|
|
|
# 状态筛选
|
|
if status is not None:
|
|
query = query.filter(User.status == status)
|
|
|
|
# 计算总数
|
|
total = query.count()
|
|
|
|
# 分页
|
|
employees = query.offset((page - 1) * page_size).limit(page_size).all()
|
|
|
|
# 构建响应数据
|
|
items = []
|
|
for emp in employees:
|
|
items.append({
|
|
"id": emp.id,
|
|
"user_id": emp.user_id,
|
|
"username": emp.user.username,
|
|
"name": emp.user.name,
|
|
"phone": emp.user.phone,
|
|
"email": emp.user.email,
|
|
"department": emp.department,
|
|
"position": emp.position,
|
|
"base_salary": str(emp.base_salary),
|
|
"monthly_target": str(emp.monthly_target),
|
|
"quarterly_target": str(emp.quarterly_target),
|
|
"half_year_target": str(emp.half_year_target),
|
|
"yearly_target": str(emp.yearly_target),
|
|
"hire_date": emp.hire_date.isoformat() if emp.hire_date else None,
|
|
"status": emp.user.status,
|
|
"created_at": emp.created_at.isoformat() if emp.created_at else None,
|
|
"updated_at": emp.updated_at.isoformat() if emp.updated_at else None
|
|
})
|
|
|
|
return {
|
|
"code": 200,
|
|
"message": "success",
|
|
"data": {
|
|
"items": items,
|
|
"total": total,
|
|
"page": page,
|
|
"page_size": page_size
|
|
}
|
|
}
|
|
|
|
|
|
@router.get("/{employee_id}", response_model=dict)
|
|
def get_employee(
|
|
employee_id: int,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""获取员工详情"""
|
|
employee = db.query(Employee).filter(Employee.id == employee_id).first()
|
|
|
|
if not employee:
|
|
raise HTTPException(status_code=404, detail="员工不存在")
|
|
|
|
return {
|
|
"code": 200,
|
|
"message": "success",
|
|
"data": {
|
|
"id": employee.id,
|
|
"user_id": employee.user_id,
|
|
"username": employee.user.username,
|
|
"name": employee.user.name,
|
|
"phone": employee.user.phone,
|
|
"email": employee.user.email,
|
|
"department": employee.department,
|
|
"position": employee.position,
|
|
"base_salary": str(employee.base_salary),
|
|
"monthly_target": str(employee.monthly_target),
|
|
"quarterly_target": str(employee.quarterly_target),
|
|
"half_year_target": str(employee.half_year_target),
|
|
"yearly_target": str(employee.yearly_target),
|
|
"hire_date": employee.hire_date.isoformat() if employee.hire_date else None,
|
|
"status": employee.user.status,
|
|
"created_at": employee.created_at.isoformat() if employee.created_at else None,
|
|
"updated_at": employee.updated_at.isoformat() if employee.updated_at else None
|
|
}
|
|
}
|
|
|
|
|
|
@router.post("", response_model=dict)
|
|
def create_employee(
|
|
data: EmployeeCreate,
|
|
db: Session = Depends(get_db),
|
|
current_admin: User = Depends(get_current_admin)
|
|
):
|
|
"""创建员工(同时创建用户账号)"""
|
|
# 检查用户名是否已存在
|
|
existing_user = db.query(User).filter(User.username == data.username).first()
|
|
if existing_user:
|
|
raise HTTPException(status_code=400, detail="用户名已存在")
|
|
|
|
# 检查手机号是否已存在
|
|
if data.phone:
|
|
existing_phone = db.query(User).filter(User.phone == data.phone).first()
|
|
if existing_phone:
|
|
raise HTTPException(status_code=400, detail="手机号已存在")
|
|
|
|
# 创建用户
|
|
from app.auth import get_password_hash
|
|
user = User(
|
|
username=data.username,
|
|
password_hash=get_password_hash(data.password),
|
|
role="employee",
|
|
name=data.name,
|
|
phone=data.phone,
|
|
email=data.email,
|
|
status=1
|
|
)
|
|
db.add(user)
|
|
db.flush() # 获取user.id
|
|
|
|
# 创建员工记录
|
|
targets = data.targets or EmployeeTarget()
|
|
employee = Employee(
|
|
user_id=user.id,
|
|
base_salary=data.base_salary,
|
|
monthly_target=targets.monthly_target,
|
|
quarterly_target=targets.quarterly_target,
|
|
half_year_target=targets.half_year_target,
|
|
yearly_target=targets.yearly_target,
|
|
hire_date=data.hire_date,
|
|
department=data.department,
|
|
position=data.position
|
|
)
|
|
db.add(employee)
|
|
db.commit()
|
|
db.refresh(employee)
|
|
|
|
# 记录操作日志
|
|
log_operation(db, current_admin.id, "CREATE_EMPLOYEE", "employee", employee.id,
|
|
new_value=f"创建员工: {data.name}, 用户名: {data.username}")
|
|
|
|
return {
|
|
"code": 200,
|
|
"message": "员工创建成功",
|
|
"data": {
|
|
"id": employee.id,
|
|
"user_id": employee.user_id,
|
|
"username": user.username,
|
|
"name": user.name,
|
|
"phone": user.phone,
|
|
"email": user.email,
|
|
"department": employee.department,
|
|
"position": employee.position,
|
|
"base_salary": str(employee.base_salary),
|
|
"monthly_target": str(employee.monthly_target),
|
|
"quarterly_target": str(employee.quarterly_target),
|
|
"half_year_target": str(employee.half_year_target),
|
|
"yearly_target": str(employee.yearly_target),
|
|
"hire_date": employee.hire_date.isoformat() if employee.hire_date else None,
|
|
"status": user.status,
|
|
"created_at": employee.created_at.isoformat() if employee.created_at else None
|
|
}
|
|
}
|
|
|
|
|
|
@router.put("/{employee_id}", response_model=dict)
|
|
def update_employee(
|
|
employee_id: int,
|
|
data: EmployeeUpdate,
|
|
db: Session = Depends(get_db),
|
|
current_admin: User = Depends(get_current_admin)
|
|
):
|
|
"""更新员工信息"""
|
|
employee = db.query(Employee).filter(Employee.id == employee_id).first()
|
|
|
|
if not employee:
|
|
raise HTTPException(status_code=404, detail="员工不存在")
|
|
|
|
user = employee.user
|
|
|
|
# 记录旧值
|
|
old_value = f"name={user.name}, phone={user.phone}, department={employee.department}, position={employee.position}"
|
|
|
|
# 更新用户信息
|
|
if data.name is not None:
|
|
user.name = data.name
|
|
if data.phone is not None:
|
|
user.phone = data.phone
|
|
if data.email is not None:
|
|
user.email = data.email
|
|
if data.status is not None:
|
|
user.status = data.status
|
|
|
|
# 更新员工信息
|
|
if data.department is not None:
|
|
employee.department = data.department
|
|
if data.position is not None:
|
|
employee.position = data.position
|
|
if data.base_salary is not None:
|
|
employee.base_salary = data.base_salary
|
|
if data.hire_date is not None:
|
|
employee.hire_date = data.hire_date
|
|
|
|
db.commit()
|
|
db.refresh(employee)
|
|
|
|
# 记录操作日志
|
|
new_value = f"name={user.name}, phone={user.phone}, department={employee.department}, position={employee.position}"
|
|
log_operation(db, current_admin.id, "UPDATE_EMPLOYEE", "employee", employee.id,
|
|
old_value=old_value, new_value=new_value)
|
|
|
|
return {
|
|
"code": 200,
|
|
"message": "员工信息更新成功",
|
|
"data": {
|
|
"id": employee.id,
|
|
"user_id": employee.user_id,
|
|
"username": user.username,
|
|
"name": user.name,
|
|
"phone": user.phone,
|
|
"email": user.email,
|
|
"department": employee.department,
|
|
"position": employee.position,
|
|
"base_salary": str(employee.base_salary),
|
|
"monthly_target": str(employee.monthly_target),
|
|
"quarterly_target": str(employee.quarterly_target),
|
|
"half_year_target": str(employee.half_year_target),
|
|
"yearly_target": str(employee.yearly_target),
|
|
"hire_date": employee.hire_date.isoformat() if employee.hire_date else None,
|
|
"status": user.status,
|
|
"updated_at": employee.updated_at.isoformat() if employee.updated_at else None
|
|
}
|
|
}
|
|
|
|
|
|
@router.delete("/{employee_id}", response_model=dict)
|
|
def delete_employee(
|
|
employee_id: int,
|
|
db: Session = Depends(get_db),
|
|
current_admin: User = Depends(get_current_admin)
|
|
):
|
|
"""删除员工"""
|
|
employee = db.query(Employee).filter(Employee.id == employee_id).first()
|
|
|
|
if not employee:
|
|
raise HTTPException(status_code=404, detail="员工不存在")
|
|
|
|
user_id = employee.user_id
|
|
user_name = employee.user.name
|
|
|
|
# 删除员工(级联删除用户)
|
|
db.delete(employee)
|
|
db.commit()
|
|
|
|
# 记录操作日志
|
|
log_operation(db, current_admin.id, "DELETE_EMPLOYEE", "employee", employee_id,
|
|
old_value=f"删除员工: {user_name}, user_id={user_id}")
|
|
|
|
return {
|
|
"code": 200,
|
|
"message": "员工删除成功",
|
|
"data": None
|
|
}
|
|
|
|
|
|
@router.put("/{employee_id}/targets", response_model=dict)
|
|
def update_employee_targets(
|
|
employee_id: int,
|
|
data: EmployeeTarget,
|
|
db: Session = Depends(get_db),
|
|
current_admin: User = Depends(get_current_admin)
|
|
):
|
|
"""更新员工目标"""
|
|
employee = db.query(Employee).filter(Employee.id == employee_id).first()
|
|
|
|
if not employee:
|
|
raise HTTPException(status_code=404, detail="员工不存在")
|
|
|
|
# 记录旧值
|
|
old_value = f"monthly={employee.monthly_target}, quarterly={employee.quarterly_target}, half_year={employee.half_year_target}, yearly={employee.yearly_target}"
|
|
|
|
# 更新目标
|
|
employee.monthly_target = data.monthly_target
|
|
employee.quarterly_target = data.quarterly_target
|
|
employee.half_year_target = data.half_year_target
|
|
employee.yearly_target = data.yearly_target
|
|
|
|
db.commit()
|
|
db.refresh(employee)
|
|
|
|
# 记录操作日志
|
|
new_value = f"monthly={data.monthly_target}, quarterly={data.quarterly_target}, half_year={data.half_year_target}, yearly={data.yearly_target}"
|
|
log_operation(db, current_admin.id, "UPDATE_EMPLOYEE_TARGETS", "employee", employee_id,
|
|
old_value=old_value, new_value=new_value)
|
|
|
|
return {
|
|
"code": 200,
|
|
"message": "员工目标更新成功",
|
|
"data": {
|
|
"id": employee.id,
|
|
"name": employee.user.name,
|
|
"monthly_target": str(employee.monthly_target),
|
|
"quarterly_target": str(employee.quarterly_target),
|
|
"half_year_target": str(employee.half_year_target),
|
|
"yearly_target": str(employee.yearly_target),
|
|
"updated_at": employee.updated_at.isoformat() if employee.updated_at else None
|
|
}
|
|
}
|