add backend source code
This commit is contained in:
262
backend/app/services/assessment_service.py
Normal file
262
backend/app/services/assessment_service.py
Normal file
@@ -0,0 +1,262 @@
|
||||
"""
|
||||
绩效考核服务层
|
||||
"""
|
||||
from typing import Optional, List
|
||||
from datetime import datetime
|
||||
from sqlalchemy import select, func, and_
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import selectinload
|
||||
|
||||
from app.models.models import Assessment, AssessmentDetail, Staff, Indicator, AssessmentStatus
|
||||
from app.schemas.schemas import AssessmentCreate, AssessmentUpdate
|
||||
|
||||
|
||||
class AssessmentService:
|
||||
"""绩效考核服务"""
|
||||
|
||||
@staticmethod
|
||||
async def get_list(
|
||||
db: AsyncSession,
|
||||
staff_id: Optional[int] = None,
|
||||
department_id: Optional[int] = None,
|
||||
period_year: Optional[int] = None,
|
||||
period_month: Optional[int] = None,
|
||||
status: Optional[str] = None,
|
||||
page: int = 1,
|
||||
page_size: int = 20
|
||||
) -> tuple[List[Assessment], int]:
|
||||
"""获取考核列表"""
|
||||
query = select(Assessment).options(
|
||||
selectinload(Assessment.staff).selectinload(Staff.department)
|
||||
)
|
||||
|
||||
if staff_id:
|
||||
query = query.where(Assessment.staff_id == staff_id)
|
||||
if department_id:
|
||||
query = query.join(Staff).where(Staff.department_id == department_id)
|
||||
if period_year:
|
||||
query = query.where(Assessment.period_year == period_year)
|
||||
if period_month:
|
||||
query = query.where(Assessment.period_month == period_month)
|
||||
if status:
|
||||
query = query.where(Assessment.status == status)
|
||||
|
||||
# 统计总数
|
||||
count_query = select(func.count()).select_from(query.subquery())
|
||||
total = await db.scalar(count_query)
|
||||
|
||||
# 分页
|
||||
query = query.order_by(Assessment.period_year.desc(), Assessment.period_month.desc(), Assessment.id.desc())
|
||||
query = query.offset((page - 1) * page_size).limit(page_size)
|
||||
|
||||
result = await db.execute(query)
|
||||
assessments = result.scalars().all()
|
||||
|
||||
return assessments, total or 0
|
||||
|
||||
@staticmethod
|
||||
async def get_by_id(db: AsyncSession, assessment_id: int) -> Optional[Assessment]:
|
||||
"""根据ID获取考核详情"""
|
||||
result = await db.execute(
|
||||
select(Assessment)
|
||||
.options(
|
||||
selectinload(Assessment.staff).selectinload(Staff.department),
|
||||
selectinload(Assessment.details).selectinload(AssessmentDetail.indicator)
|
||||
)
|
||||
.where(Assessment.id == assessment_id)
|
||||
)
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
@staticmethod
|
||||
async def create(db: AsyncSession, assessment_data: AssessmentCreate, assessor_id: Optional[int] = None) -> Assessment:
|
||||
"""创建考核记录"""
|
||||
# 计算总分
|
||||
total_score = sum(d.score for d in assessment_data.details)
|
||||
|
||||
# 获取指标权重计算加权得分
|
||||
weighted_score = 0.0
|
||||
for detail in assessment_data.details:
|
||||
indicator = await db.execute(
|
||||
select(Indicator).where(Indicator.id == detail.indicator_id)
|
||||
)
|
||||
ind = indicator.scalar_one_or_none()
|
||||
if ind:
|
||||
weighted_score += detail.score * float(ind.weight)
|
||||
|
||||
assessment = Assessment(
|
||||
staff_id=assessment_data.staff_id,
|
||||
period_year=assessment_data.period_year,
|
||||
period_month=assessment_data.period_month,
|
||||
period_type=assessment_data.period_type,
|
||||
total_score=total_score,
|
||||
weighted_score=weighted_score,
|
||||
assessor_id=assessor_id,
|
||||
)
|
||||
db.add(assessment)
|
||||
await db.flush()
|
||||
|
||||
# 创建明细
|
||||
for detail_data in assessment_data.details:
|
||||
detail = AssessmentDetail(
|
||||
assessment_id=assessment.id,
|
||||
**detail_data.model_dump()
|
||||
)
|
||||
db.add(detail)
|
||||
|
||||
await db.flush()
|
||||
await db.refresh(assessment)
|
||||
return assessment
|
||||
|
||||
@staticmethod
|
||||
async def update(db: AsyncSession, assessment_id: int, assessment_data: AssessmentUpdate) -> Optional[Assessment]:
|
||||
"""更新考核记录"""
|
||||
assessment = await AssessmentService.get_by_id(db, assessment_id)
|
||||
if not assessment:
|
||||
return None
|
||||
|
||||
if assessment.status not in ["draft", "rejected"]:
|
||||
return None
|
||||
|
||||
if assessment_data.details is not None:
|
||||
# 删除旧明细
|
||||
await db.execute(
|
||||
select(AssessmentDetail).where(AssessmentDetail.assessment_id == assessment_id)
|
||||
)
|
||||
for detail in assessment.details:
|
||||
await db.delete(detail)
|
||||
|
||||
# 创建新明细
|
||||
total_score = 0.0
|
||||
weighted_score = 0.0
|
||||
|
||||
for detail_data in assessment_data.details:
|
||||
detail = AssessmentDetail(
|
||||
assessment_id=assessment_id,
|
||||
**detail_data.model_dump()
|
||||
)
|
||||
db.add(detail)
|
||||
total_score += detail_data.score
|
||||
|
||||
# 获取权重
|
||||
indicator = await db.execute(
|
||||
select(Indicator).where(Indicator.id == detail_data.indicator_id)
|
||||
)
|
||||
ind = indicator.scalar_one_or_none()
|
||||
if ind:
|
||||
weighted_score += detail_data.score * float(ind.weight)
|
||||
|
||||
assessment.total_score = total_score
|
||||
assessment.weighted_score = weighted_score
|
||||
|
||||
if assessment_data.remark is not None:
|
||||
assessment.remark = assessment_data.remark
|
||||
|
||||
await db.flush()
|
||||
await db.refresh(assessment)
|
||||
return assessment
|
||||
|
||||
@staticmethod
|
||||
async def submit(db: AsyncSession, assessment_id: int) -> Optional[Assessment]:
|
||||
"""提交考核"""
|
||||
assessment = await AssessmentService.get_by_id(db, assessment_id)
|
||||
if not assessment or assessment.status != AssessmentStatus.DRAFT:
|
||||
return None
|
||||
|
||||
assessment.status = AssessmentStatus.SUBMITTED
|
||||
assessment.submit_time = datetime.utcnow()
|
||||
|
||||
await db.flush()
|
||||
await db.refresh(assessment)
|
||||
return assessment
|
||||
|
||||
@staticmethod
|
||||
async def review(db: AsyncSession, assessment_id: int, reviewer_id: int, approved: bool, remark: Optional[str] = None) -> Optional[Assessment]:
|
||||
"""审核考核"""
|
||||
assessment = await AssessmentService.get_by_id(db, assessment_id)
|
||||
if not assessment or assessment.status != AssessmentStatus.SUBMITTED:
|
||||
return None
|
||||
|
||||
assessment.reviewer_id = reviewer_id
|
||||
assessment.review_time = datetime.utcnow()
|
||||
|
||||
if approved:
|
||||
assessment.status = AssessmentStatus.REVIEWED
|
||||
else:
|
||||
assessment.status = AssessmentStatus.REJECTED
|
||||
|
||||
if remark:
|
||||
assessment.remark = remark
|
||||
|
||||
await db.flush()
|
||||
await db.refresh(assessment)
|
||||
return assessment
|
||||
|
||||
@staticmethod
|
||||
async def finalize(db: AsyncSession, assessment_id: int) -> Optional[Assessment]:
|
||||
"""确认考核"""
|
||||
assessment = await AssessmentService.get_by_id(db, assessment_id)
|
||||
if not assessment or assessment.status != AssessmentStatus.REVIEWED:
|
||||
return None
|
||||
|
||||
assessment.status = AssessmentStatus.FINALIZED
|
||||
|
||||
await db.flush()
|
||||
await db.refresh(assessment)
|
||||
return assessment
|
||||
|
||||
@staticmethod
|
||||
async def batch_create_for_department(
|
||||
db: AsyncSession,
|
||||
department_id: int,
|
||||
period_year: int,
|
||||
period_month: int,
|
||||
indicators: List[int]
|
||||
) -> List[Assessment]:
|
||||
"""为科室批量创建考核"""
|
||||
# 获取科室所有在职员工
|
||||
staff_result = await db.execute(
|
||||
select(Staff).where(
|
||||
Staff.department_id == department_id,
|
||||
Staff.status == "active"
|
||||
)
|
||||
)
|
||||
staff_list = staff_result.scalars().all()
|
||||
|
||||
assessments = []
|
||||
for staff in staff_list:
|
||||
# 检查是否已存在
|
||||
existing = await db.execute(
|
||||
select(Assessment).where(
|
||||
Assessment.staff_id == staff.id,
|
||||
Assessment.period_year == period_year,
|
||||
Assessment.period_month == period_month
|
||||
)
|
||||
)
|
||||
if existing.scalar_one_or_none():
|
||||
continue
|
||||
|
||||
# 创建考核记录
|
||||
assessment = Assessment(
|
||||
staff_id=staff.id,
|
||||
period_year=period_year,
|
||||
period_month=period_month,
|
||||
period_type="monthly",
|
||||
total_score=0,
|
||||
weighted_score=0,
|
||||
)
|
||||
db.add(assessment)
|
||||
await db.flush()
|
||||
|
||||
# 创建明细
|
||||
for indicator_id in indicators:
|
||||
detail = AssessmentDetail(
|
||||
assessment_id=assessment.id,
|
||||
indicator_id=indicator_id,
|
||||
score=0
|
||||
)
|
||||
db.add(detail)
|
||||
|
||||
assessments.append(assessment)
|
||||
|
||||
await db.flush()
|
||||
return assessments
|
||||
Reference in New Issue
Block a user