add backend source code
This commit is contained in:
341
backend/app/services/performance_plan_service.py
Normal file
341
backend/app/services/performance_plan_service.py
Normal file
@@ -0,0 +1,341 @@
|
||||
"""
|
||||
绩效计划服务层
|
||||
"""
|
||||
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
|
||||
Reference in New Issue
Block a user