111 lines
4.0 KiB
Python
111 lines
4.0 KiB
Python
"""就业 DAO(需求 2.3)。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from sqlalchemy import Select, func, or_, select
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.dao.base_dao import BaseDao
|
|
from app.model import Clazz, Employment, Student
|
|
|
|
|
|
class EmploymentDao(BaseDao[Employment]):
|
|
model = Employment
|
|
|
|
@classmethod
|
|
def build_stmt(
|
|
cls,
|
|
stu_id: int | None = None,
|
|
class_id: int | None = None,
|
|
company: str | None = None,
|
|
salary_min: float | None = None,
|
|
salary_max: float | None = None,
|
|
has_offer: bool | None = None,
|
|
has_open: bool | None = None,
|
|
keyword: str | None = None,
|
|
order_by: str = "id",
|
|
order: str = "desc",
|
|
) -> Select:
|
|
stmt = select(Employment).join(Student, Student.id == Employment.stu_id).where(
|
|
Employment.alive(), Student.alive()
|
|
)
|
|
|
|
if stu_id:
|
|
stmt = stmt.where(Employment.stu_id == stu_id)
|
|
if class_id:
|
|
stmt = stmt.where(Employment.class_id == class_id)
|
|
if company:
|
|
stmt = stmt.where(Employment.company.like(f"%{company.strip()}%"))
|
|
if salary_min is not None:
|
|
stmt = stmt.where(Employment.salary >= salary_min)
|
|
if salary_max is not None:
|
|
stmt = stmt.where(Employment.salary <= salary_max)
|
|
if has_offer is True:
|
|
stmt = stmt.where(Employment.offer_date.is_not(None))
|
|
elif has_offer is False:
|
|
stmt = stmt.where(Employment.offer_date.is_(None))
|
|
if has_open is True:
|
|
stmt = stmt.where(Employment.open_date.is_not(None))
|
|
elif has_open is False:
|
|
stmt = stmt.where(Employment.open_date.is_(None))
|
|
if keyword:
|
|
like = f"%{keyword.strip()}%"
|
|
stmt = stmt.where(
|
|
or_(
|
|
Student.name.like(like),
|
|
Student.stu_no.like(like),
|
|
Employment.company.like(like),
|
|
Employment.city.like(like),
|
|
)
|
|
)
|
|
|
|
sortable = {
|
|
"id": Employment.id,
|
|
"salary": Employment.salary,
|
|
"open_date": Employment.open_date,
|
|
"offer_date": Employment.offer_date,
|
|
"company": Employment.company,
|
|
"student_name": Student.name,
|
|
"class_name": Clazz.name,
|
|
}
|
|
column = sortable.get(order_by or "id", Employment.id)
|
|
stmt = stmt.order_by(column.desc() if (order or "desc").lower() == "desc" else column.asc())
|
|
return stmt
|
|
|
|
@classmethod
|
|
def get_by_stu_id(cls, db: Session, stu_id: int, with_deleted: bool = False) -> Employment | None:
|
|
stmt = select(Employment).where(Employment.stu_id == stu_id)
|
|
if not with_deleted:
|
|
stmt = stmt.where(Employment.alive())
|
|
return db.scalars(stmt).unique().first()
|
|
|
|
@classmethod
|
|
def employed_count_by_class(cls, db: Session) -> dict[int, int]:
|
|
"""已就业(拿到 offer)人数按班级汇总。"""
|
|
stmt = (
|
|
select(Employment.class_id, func.count(Employment.id))
|
|
.where(Employment.alive(), Employment.offer_date.is_not(None))
|
|
.group_by(Employment.class_id)
|
|
)
|
|
return {row[0]: row[1] for row in db.execute(stmt).all()}
|
|
|
|
@classmethod
|
|
def salary_stats(cls, db: Session, high_line: float) -> dict[str, float | int | None]:
|
|
stmt = select(
|
|
func.count(Employment.id),
|
|
func.avg(Employment.salary),
|
|
func.max(Employment.salary),
|
|
).where(Employment.alive(), Employment.salary.is_not(None))
|
|
total, avg, mx = db.execute(stmt).first()
|
|
high = db.scalar(
|
|
select(func.count(Employment.id)).where(
|
|
Employment.alive(), Employment.salary >= high_line
|
|
)
|
|
)
|
|
return {
|
|
"count": int(total or 0),
|
|
"avg": float(avg) if avg is not None else None,
|
|
"max": float(mx) if mx is not None else None,
|
|
"high": int(high or 0),
|
|
}
|