342 lines
10 KiB
Python
342 lines
10 KiB
Python
"""
|
|
绩效计划服务层
|
|
"""
|
|
import json
|
|
from typing import Optional, List, Dict, Any
|
|
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 PerformancePlan, PlanKpiRelation, PlanStatus, PlanLevel, Indicator
|
|
from app.schemas.schemas import PerformancePlanCreate, PerformancePlanUpdate, PlanKpiRelationCreate
|
|
|
|
|
|
class PerformancePlanService:
|
|
"""绩效计划服务"""
|
|
|
|
@staticmethod
|
|
async def get_list(
|
|
db: AsyncSession,
|
|
plan_level: Optional[str] = None,
|
|
plan_year: Optional[int] = None,
|
|
department_id: Optional[int] = None,
|
|
status: Optional[str] = None,
|
|
page: int = 1,
|
|
page_size: int = 20
|
|
) -> tuple[List[PerformancePlan], int]:
|
|
"""获取绩效计划列表"""
|
|
query = select(PerformancePlan).options(
|
|
selectinload(PerformancePlan.department),
|
|
selectinload(PerformancePlan.staff)
|
|
)
|
|
|
|
# 构建查询条件
|
|
conditions = []
|
|
if plan_level:
|
|
conditions.append(PerformancePlan.plan_level == plan_level)
|
|
if plan_year:
|
|
conditions.append(PerformancePlan.plan_year == plan_year)
|
|
if department_id:
|
|
conditions.append(PerformancePlan.department_id == department_id)
|
|
if status:
|
|
conditions.append(PerformancePlan.status == status)
|
|
|
|
if conditions:
|
|
query = query.where(and_(*conditions))
|
|
|
|
# 统计总数
|
|
count_query = select(func.count()).select_from(query.subquery())
|
|
total = await db.scalar(count_query)
|
|
|
|
# 分页
|
|
query = query.order_by(PerformancePlan.created_at.desc())
|
|
query = query.offset((page - 1) * page_size).limit(page_size)
|
|
|
|
result = await db.execute(query)
|
|
plans = result.scalars().all()
|
|
|
|
return plans, total or 0
|
|
|
|
@staticmethod
|
|
async def get_by_id(db: AsyncSession, plan_id: int) -> Optional[PerformancePlan]:
|
|
"""根据 ID 获取绩效计划详情"""
|
|
result = await db.execute(
|
|
select(PerformancePlan)
|
|
.options(
|
|
selectinload(PerformancePlan.department),
|
|
selectinload(PerformancePlan.staff),
|
|
selectinload(PerformancePlan.kpi_relations).selectinload(PlanKpiRelation.indicator)
|
|
)
|
|
.where(PerformancePlan.id == plan_id)
|
|
)
|
|
return result.scalar_one_or_none()
|
|
|
|
@staticmethod
|
|
async def create(
|
|
db: AsyncSession,
|
|
plan_data: PerformancePlanCreate,
|
|
submitter_id: Optional[int] = None
|
|
) -> PerformancePlan:
|
|
"""创建绩效计划"""
|
|
# 创建计划
|
|
plan_dict = plan_data.model_dump(exclude={'kpi_relations'})
|
|
plan = PerformancePlan(**plan_dict)
|
|
plan.submitter_id = submitter_id
|
|
|
|
db.add(plan)
|
|
await db.flush()
|
|
await db.refresh(plan)
|
|
|
|
# 创建指标关联
|
|
if plan_data.kpi_relations:
|
|
for kpi_data in plan_data.kpi_relations:
|
|
kpi_relation = PlanKpiRelation(
|
|
plan_id=plan.id,
|
|
**kpi_data.model_dump()
|
|
)
|
|
db.add(kpi_relation)
|
|
|
|
await db.commit()
|
|
await db.refresh(plan)
|
|
return plan
|
|
|
|
@staticmethod
|
|
async def update(
|
|
db: AsyncSession,
|
|
plan_id: int,
|
|
plan_data: PerformancePlanUpdate
|
|
) -> Optional[PerformancePlan]:
|
|
"""更新绩效计划"""
|
|
plan = await PerformancePlanService.get_by_id(db, plan_id)
|
|
if not plan:
|
|
return None
|
|
|
|
update_data = plan_data.model_dump(exclude_unset=True)
|
|
for key, value in update_data.items():
|
|
if hasattr(plan, key):
|
|
setattr(plan, key, value)
|
|
|
|
# 处理审批相关
|
|
if plan_data.status == PlanStatus.APPROVED:
|
|
plan.approve_time = datetime.utcnow()
|
|
elif plan_data.status == PlanStatus.REJECTED:
|
|
plan.approve_time = datetime.utcnow()
|
|
|
|
await db.commit()
|
|
await db.refresh(plan)
|
|
return plan
|
|
|
|
@staticmethod
|
|
async def submit(db: AsyncSession, plan_id: int) -> Optional[PerformancePlan]:
|
|
"""提交绩效计划"""
|
|
plan = await PerformancePlanService.get_by_id(db, plan_id)
|
|
if not plan or plan.status != PlanStatus.DRAFT:
|
|
return None
|
|
|
|
plan.status = PlanStatus.PENDING
|
|
plan.submit_time = datetime.utcnow()
|
|
|
|
await db.commit()
|
|
await db.refresh(plan)
|
|
return plan
|
|
|
|
@staticmethod
|
|
async def approve(
|
|
db: AsyncSession,
|
|
plan_id: int,
|
|
approver_id: int,
|
|
approved: bool,
|
|
remark: Optional[str] = None
|
|
) -> Optional[PerformancePlan]:
|
|
"""审批绩效计划"""
|
|
plan = await PerformancePlanService.get_by_id(db, plan_id)
|
|
if not plan or plan.status != PlanStatus.PENDING:
|
|
return None
|
|
|
|
plan.approver_id = approver_id
|
|
plan.approve_time = datetime.utcnow()
|
|
plan.approve_remark = remark
|
|
|
|
if approved:
|
|
plan.status = PlanStatus.APPROVED
|
|
else:
|
|
plan.status = PlanStatus.REJECTED
|
|
|
|
await db.commit()
|
|
await db.refresh(plan)
|
|
return plan
|
|
|
|
@staticmethod
|
|
async def activate(db: AsyncSession, plan_id: int) -> Optional[PerformancePlan]:
|
|
"""激活绩效计划"""
|
|
plan = await PerformancePlanService.get_by_id(db, plan_id)
|
|
if not plan or plan.status != PlanStatus.APPROVED:
|
|
return None
|
|
|
|
plan.status = PlanStatus.ACTIVE
|
|
plan.is_active = True
|
|
|
|
await db.commit()
|
|
await db.refresh(plan)
|
|
return plan
|
|
|
|
@staticmethod
|
|
async def delete(db: AsyncSession, plan_id: int) -> bool:
|
|
"""删除绩效计划"""
|
|
plan = await PerformancePlanService.get_by_id(db, plan_id)
|
|
if not plan:
|
|
return False
|
|
|
|
await db.delete(plan)
|
|
await db.commit()
|
|
return True
|
|
|
|
@staticmethod
|
|
async def add_kpi_relation(
|
|
db: AsyncSession,
|
|
plan_id: int,
|
|
kpi_data: PlanKpiRelationCreate
|
|
) -> Optional[PlanKpiRelation]:
|
|
"""添加计划指标关联"""
|
|
plan = await PerformancePlanService.get_by_id(db, plan_id)
|
|
if not plan:
|
|
return None
|
|
|
|
kpi_relation = PlanKpiRelation(
|
|
plan_id=plan_id,
|
|
**kpi_data.model_dump()
|
|
)
|
|
db.add(kpi_relation)
|
|
await db.commit()
|
|
await db.refresh(kpi_relation)
|
|
return kpi_relation
|
|
|
|
@staticmethod
|
|
async def update_kpi_relation(
|
|
db: AsyncSession,
|
|
relation_id: int,
|
|
kpi_data: dict
|
|
) -> Optional[PlanKpiRelation]:
|
|
"""更新计划指标关联"""
|
|
result = await db.execute(
|
|
select(PlanKpiRelation).where(PlanKpiRelation.id == relation_id)
|
|
)
|
|
kpi_relation = result.scalar_one_or_none()
|
|
if not kpi_relation:
|
|
return None
|
|
|
|
for key, value in kpi_data.items():
|
|
if hasattr(kpi_relation, key) and value is not None:
|
|
setattr(kpi_relation, key, value)
|
|
|
|
await db.commit()
|
|
await db.refresh(kpi_relation)
|
|
return kpi_relation
|
|
|
|
@staticmethod
|
|
async def delete_kpi_relation(db: AsyncSession, relation_id: int) -> bool:
|
|
"""删除计划指标关联"""
|
|
result = await db.execute(
|
|
select(PlanKpiRelation).where(PlanKpiRelation.id == relation_id)
|
|
)
|
|
kpi_relation = result.scalar_one_or_none()
|
|
if not kpi_relation:
|
|
return False
|
|
|
|
await db.delete(kpi_relation)
|
|
await db.commit()
|
|
return True
|
|
|
|
@staticmethod
|
|
async def get_stats(
|
|
db: AsyncSession,
|
|
plan_year: Optional[int] = None
|
|
) -> Dict[str, int]:
|
|
"""获取绩效计划统计"""
|
|
conditions = []
|
|
if plan_year:
|
|
conditions.append(PerformancePlan.plan_year == plan_year)
|
|
|
|
query = select(
|
|
PerformancePlan.status,
|
|
func.count().label('count')
|
|
)
|
|
if conditions:
|
|
query = query.where(and_(*conditions))
|
|
query = query.group_by(PerformancePlan.status)
|
|
|
|
result = await db.execute(query)
|
|
rows = result.fetchall()
|
|
|
|
stats = {
|
|
'total_plans': 0,
|
|
'draft_count': 0,
|
|
'pending_count': 0,
|
|
'approved_count': 0,
|
|
'active_count': 0,
|
|
'completed_count': 0
|
|
}
|
|
|
|
for row in rows:
|
|
status = row.status
|
|
count = row.count
|
|
stats['total_plans'] += count
|
|
if status == PlanStatus.DRAFT:
|
|
stats['draft_count'] = count
|
|
elif status == PlanStatus.PENDING:
|
|
stats['pending_count'] = count
|
|
elif status == PlanStatus.APPROVED:
|
|
stats['approved_count'] = count
|
|
elif status == PlanStatus.ACTIVE:
|
|
stats['active_count'] = count
|
|
elif status == PlanStatus.COMPLETED:
|
|
stats['completed_count'] = count
|
|
|
|
return stats
|
|
|
|
@staticmethod
|
|
async def get_tree(
|
|
db: AsyncSession,
|
|
plan_year: Optional[int] = None
|
|
) -> List[Dict[str, Any]]:
|
|
"""获取绩效计划树形结构"""
|
|
conditions = []
|
|
if plan_year:
|
|
conditions.append(PerformancePlan.plan_year == plan_year)
|
|
|
|
query = select(PerformancePlan).options(
|
|
selectinload(PerformancePlan.department),
|
|
selectinload(PerformancePlan.staff)
|
|
)
|
|
if conditions:
|
|
query = query.where(and_(*conditions))
|
|
query = query.order_by(PerformancePlan.plan_level, PerformancePlan.id)
|
|
|
|
result = await db.execute(query)
|
|
plans = result.scalars().all()
|
|
|
|
# 构建树形结构
|
|
plan_dict = {}
|
|
root_plans = []
|
|
|
|
for plan in plans:
|
|
plan_dict[plan.id] = {
|
|
'id': plan.id,
|
|
'plan_name': plan.plan_name,
|
|
'plan_code': plan.plan_code,
|
|
'plan_level': plan.plan_level,
|
|
'status': plan.status,
|
|
'department_name': plan.department.name if plan.department else None,
|
|
'staff_name': plan.staff.name if plan.staff else None,
|
|
'children': []
|
|
}
|
|
|
|
if plan.parent_plan_id:
|
|
if plan.parent_plan_id in plan_dict:
|
|
plan_dict[plan.parent_plan_id]['children'].append(plan_dict[plan.id])
|
|
else:
|
|
root_plans.append(plan_dict[plan.id])
|
|
|
|
return root_plans
|