就业模块合并
This commit is contained in:
@@ -0,0 +1,56 @@
|
|||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from dao.wl_emp_dao import EmpDAO
|
||||||
|
from database import get_db
|
||||||
|
from scheme.wl_emp_scheme import EmpCreate, EmpOut, EmpUpdate
|
||||||
|
|
||||||
|
router = APIRouter(prefix="/emp", tags=["就业管理"])
|
||||||
|
|
||||||
|
|
||||||
|
# 查询就业列表
|
||||||
|
@router.get("/", response_model=list[EmpOut])
|
||||||
|
def query_list(
|
||||||
|
skip: int = 0,
|
||||||
|
limit: int = 100,
|
||||||
|
company_name: Optional[str] = None,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
):
|
||||||
|
return EmpDAO.list_emps(db, skip, limit, company_name)
|
||||||
|
|
||||||
|
|
||||||
|
# 根据学号查询就业信息
|
||||||
|
@router.get("/{stu_no}", response_model=EmpOut)
|
||||||
|
def query_one(stu_no: str, db: Session = Depends(get_db)):
|
||||||
|
emp = EmpDAO.get_emp(db, stu_no)
|
||||||
|
if not emp:
|
||||||
|
raise HTTPException(status_code=404, detail="就业信息不存在")
|
||||||
|
return emp
|
||||||
|
|
||||||
|
|
||||||
|
# 新增就业信息
|
||||||
|
@router.post("/", response_model=EmpOut, status_code=201)
|
||||||
|
def add_emp(body: EmpCreate, db: Session = Depends(get_db)):
|
||||||
|
try:
|
||||||
|
return EmpDAO.create_emp(db, body)
|
||||||
|
except ValueError as e:
|
||||||
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
|
# 修改就业信息
|
||||||
|
@router.put("/{stu_no}", response_model=EmpOut)
|
||||||
|
def edit_emp(stu_no: str, body: EmpUpdate, db: Session = Depends(get_db)):
|
||||||
|
emp = EmpDAO.update_emp(db, stu_no, body)
|
||||||
|
if not emp:
|
||||||
|
raise HTTPException(status_code=404, detail="就业信息不存在")
|
||||||
|
return emp
|
||||||
|
|
||||||
|
|
||||||
|
# 逻辑删除就业信息
|
||||||
|
@router.delete("/{stu_no}")
|
||||||
|
def remove_emp(stu_no: str, db: Session = Depends(get_db)):
|
||||||
|
if not EmpDAO.delete_emp(db, stu_no):
|
||||||
|
raise HTTPException(status_code=404, detail="就业信息不存在")
|
||||||
|
return {"code": 200, "msg": "删除成功"}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from model.wl_emp_model import Emp
|
||||||
|
from model.wl_student_model import Student
|
||||||
|
from scheme.wl_emp_scheme import EmpCreate, EmpUpdate
|
||||||
|
|
||||||
|
|
||||||
|
class EmpDAO:
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_student(db: Session, stu_no: str):
|
||||||
|
"""按学号查未删除的学生"""
|
||||||
|
return db.query(Student).filter(
|
||||||
|
Student.stu_no == stu_no,
|
||||||
|
Student.is_deleted == 0,
|
||||||
|
).first()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def list_emps(db: Session, skip: int = 0, limit: int = 100, company_name: str = None):
|
||||||
|
"""查询就业列表,顺带把学生姓名带上"""
|
||||||
|
q = db.query(Emp).filter(Emp.is_deleted == 0)
|
||||||
|
if company_name:
|
||||||
|
q = q.filter(Emp.company_name.like(f"%{company_name}%"))
|
||||||
|
|
||||||
|
emp_list = q.offset(skip).limit(limit).all()
|
||||||
|
for e in emp_list:
|
||||||
|
stu = db.query(Student).filter(
|
||||||
|
Student.stu_no == e.stu_no,
|
||||||
|
Student.is_deleted == 0,
|
||||||
|
).first()
|
||||||
|
e.stu_name = stu.stu_name if stu else None
|
||||||
|
return emp_list
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_emp(db: Session, stu_no: str):
|
||||||
|
"""按学号查就业信息"""
|
||||||
|
emp = db.query(Emp).filter(
|
||||||
|
Emp.stu_no == stu_no,
|
||||||
|
Emp.is_deleted == 0,
|
||||||
|
).first()
|
||||||
|
if emp:
|
||||||
|
stu = EmpDAO.get_student(db, stu_no)
|
||||||
|
emp.stu_name = stu.stu_name if stu else None
|
||||||
|
return emp
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_emp(db: Session, emp_data: EmpCreate):
|
||||||
|
"""新增就业信息;如果这个学生之前被删过,就把那条记录复活"""
|
||||||
|
# stu_no 是主键,已删除的记录也占着这个主键,所以查的时候要连已删除的一起查
|
||||||
|
exist = db.query(Emp).filter(Emp.stu_no == emp_data.stu_no).first()
|
||||||
|
if exist and exist.is_deleted == 0:
|
||||||
|
raise ValueError("该学生的就业信息已存在")
|
||||||
|
|
||||||
|
# exclude_none:没传的字段不写进去,交给数据库默认值
|
||||||
|
data = emp_data.model_dump(exclude_none=True)
|
||||||
|
stu = EmpDAO.get_student(db, emp_data.stu_no)
|
||||||
|
|
||||||
|
if exist:
|
||||||
|
# 复活旧记录
|
||||||
|
for k, v in data.items():
|
||||||
|
setattr(exist, k, v)
|
||||||
|
exist.is_deleted = 0
|
||||||
|
db.commit()
|
||||||
|
db.refresh(exist)
|
||||||
|
exist.stu_name = stu.stu_name if stu else None
|
||||||
|
return exist
|
||||||
|
|
||||||
|
db_emp = Emp(**data)
|
||||||
|
db.add(db_emp)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(db_emp)
|
||||||
|
db_emp.stu_name = stu.stu_name if stu else None
|
||||||
|
return db_emp
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def update_emp(db: Session, stu_no: str, emp_data: EmpUpdate):
|
||||||
|
"""修改就业信息"""
|
||||||
|
db_emp = EmpDAO.get_emp(db, stu_no)
|
||||||
|
if not db_emp:
|
||||||
|
return None
|
||||||
|
|
||||||
|
update_dict = emp_data.model_dump(exclude_unset=True)
|
||||||
|
for k, v in update_dict.items():
|
||||||
|
setattr(db_emp, k, v)
|
||||||
|
|
||||||
|
db.commit()
|
||||||
|
db.refresh(db_emp)
|
||||||
|
return db_emp
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def delete_emp(db: Session, stu_no: str):
|
||||||
|
"""逻辑删除,修改 is_deleted=1"""
|
||||||
|
db_emp = EmpDAO.get_emp(db, stu_no)
|
||||||
|
if not db_emp:
|
||||||
|
return None
|
||||||
|
|
||||||
|
db_emp.is_deleted = 1
|
||||||
|
db.commit()
|
||||||
|
return db_emp
|
||||||
@@ -136,7 +136,7 @@ class StatisticDao:
|
|||||||
Emp.company_name
|
Emp.company_name
|
||||||
).select_from(Student) \
|
).select_from(Student) \
|
||||||
.join(Student.emp)) \
|
.join(Student.emp)) \
|
||||||
.filter(Student.is_deleted==0) \
|
.filter(Student.is_deleted==0, Emp.is_deleted==0) \
|
||||||
.order_by(Emp.salary.desc())\
|
.order_by(Emp.salary.desc())\
|
||||||
.limit(rank)
|
.limit(rank)
|
||||||
|
|
||||||
@@ -151,11 +151,12 @@ class StatisticDao:
|
|||||||
0,
|
0,
|
||||||
).label('emp_total_time')
|
).label('emp_total_time')
|
||||||
|
|
||||||
|
# 逻辑删除条件写在 ON 里,写进 where 会变成 INNER JOIN,未就业学生会被滤掉
|
||||||
q=db.query(
|
q=db.query(
|
||||||
Student.stu_name,
|
Student.stu_name,
|
||||||
emp_total_time,
|
emp_total_time,
|
||||||
).select_from(Student) \
|
).select_from(Student) \
|
||||||
.outerjoin(Student.emp) \
|
.outerjoin(Emp, and_(Student.stu_no == Emp.stu_no, Emp.is_deleted == 0)) \
|
||||||
.filter(Student.is_deleted==0)
|
.filter(Student.is_deleted==0)
|
||||||
|
|
||||||
return q.all()
|
return q.all()
|
||||||
@@ -188,7 +189,8 @@ class StatisticDao:
|
|||||||
.outerjoin(
|
.outerjoin(
|
||||||
Emp,
|
Emp,
|
||||||
and_(
|
and_(
|
||||||
Emp.stu_id == Student.stu_id,
|
Emp.stu_no == Student.stu_no,
|
||||||
|
Emp.is_deleted == 0,
|
||||||
Emp.emp_open_time.isnot(None),
|
Emp.emp_open_time.isnot(None),
|
||||||
Emp.offer_time.isnot(None),
|
Emp.offer_time.isnot(None),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
#项目初始化入口
|
#项目初始化入口
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
# 周学灵、熊浩钦、圣国伟、刘盼、彭少的路由
|
# 周学灵、熊浩钦、圣国伟、刘盼、彭少、赵康宁的路由
|
||||||
from api import wl_student_api,wl_class_api,wl_score_api,wl_teacher_api,statistics
|
from api import wl_student_api,wl_class_api,wl_score_api,wl_teacher_api,statistics,wl_emp_api
|
||||||
from database import Base, engine
|
from database import Base, engine
|
||||||
from model import wl_student_model,wl_class_model,wl_advisor_model,wl_score_model,wl_teacher_model
|
# wl_emp_model 必须显式导入:否则 wl_emp 表只能靠 statistics 的传递导入进 metadata,
|
||||||
|
# 一旦统计路由被拿掉,create_all 会漏建 wl_emp,Student.emp 关系随即报错
|
||||||
|
from model import wl_student_model,wl_class_model,wl_advisor_model,wl_score_model,wl_teacher_model,wl_emp_model
|
||||||
|
|
||||||
Base.metadata.drop_all(engine)
|
Base.metadata.drop_all(engine)
|
||||||
Base.metadata.create_all(bind=engine)
|
Base.metadata.create_all(bind=engine)
|
||||||
@@ -14,6 +16,7 @@ app.include_router(wl_class_api.router)
|
|||||||
app.include_router(wl_score_api.router)
|
app.include_router(wl_score_api.router)
|
||||||
app.include_router(wl_teacher_api.router)
|
app.include_router(wl_teacher_api.router)
|
||||||
app.include_router(statistics.router)
|
app.include_router(statistics.router)
|
||||||
|
app.include_router(wl_emp_api.router)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from sqlalchemy import *
|
from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, func
|
||||||
from sqlalchemy.orm import *
|
from sqlalchemy.orm import relationship
|
||||||
|
|
||||||
from database import Base
|
from database import Base
|
||||||
|
|
||||||
@@ -7,15 +7,17 @@ from database import Base
|
|||||||
class Emp(Base):
|
class Emp(Base):
|
||||||
__tablename__ = "wl_emp"
|
__tablename__ = "wl_emp"
|
||||||
|
|
||||||
stu_id = Column(Integer,ForeignKey('wl_student.stu_id'), primary_key=True, autoincrement=True)
|
# 学号做主键,同时做外键指向学生表,长度跟 Student.stu_no 保持一致
|
||||||
|
stu_no = Column(String(20), ForeignKey('wl_student.stu_no'), primary_key=True)
|
||||||
emp_open_time = Column(DateTime, default="2026-11-30 18:18:18")
|
emp_open_time = Column(DateTime, default="2026-11-30 18:18:18")
|
||||||
offer_time = Column(DateTime, default=func.now())
|
offer_time = Column(DateTime, default=func.now())
|
||||||
company_name = Column(String(100), nullable=False)
|
company_name = Column(String(100), nullable=False)
|
||||||
salary = Column(Integer, nullable=False, default=15000)
|
salary = Column(Integer, nullable=False, default=15000)
|
||||||
|
|
||||||
# Student.emp 用的是 back_populates="student",这个反向属性必须保留,否则 mapper 配置失败
|
|
||||||
student = relationship("Student", back_populates="emp")
|
student = relationship("Student", back_populates="emp")
|
||||||
|
|
||||||
def __repr__(self):
|
# 逻辑删除:0 正常 1 已删除
|
||||||
pass
|
is_deleted = Column(Integer, default=0, comment='逻辑删除: 0正常 1删除')
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"<Emp(stu_no={self.stu_no}, company='{self.company_name}')>"
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class Student(Base):
|
|||||||
|
|
||||||
class_ = relationship("Class_", back_populates="students")
|
class_ = relationship("Class_", back_populates="students")
|
||||||
scores=relationship("Score",back_populates="student")
|
scores=relationship("Score",back_populates="student")
|
||||||
emp=relationship("Emp",back_populates="student")
|
emp = relationship("Emp", back_populates="student")
|
||||||
# advisor = relationship("Advisor", back_populates="students")
|
# advisor = relationship("Advisor", back_populates="students")
|
||||||
|
|
||||||
is_deleted = Column(Integer, default=0, comment='逻辑删除: 0正常 1删除')
|
is_deleted = Column(Integer, default=0, comment='逻辑删除: 0正常 1删除')
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
from pydantic import BaseModel, Field, ConfigDict
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
# ---------- 请求模型 ----------
|
||||||
|
class EmpCreate(BaseModel):
|
||||||
|
stu_no: str = Field(..., max_length=20, description="学号")
|
||||||
|
emp_open_time: Optional[datetime] = Field(None, description="不传用表默认值")
|
||||||
|
offer_time: Optional[datetime] = Field(None, description="不传用数据库当前时间")
|
||||||
|
company_name: str = Field(..., max_length=100, description="公司名称")
|
||||||
|
salary: Optional[int] = Field(None, description="不传用默认值 15000")
|
||||||
|
|
||||||
|
class EmpUpdate(BaseModel):
|
||||||
|
emp_open_time: Optional[datetime] = None
|
||||||
|
offer_time: Optional[datetime] = None
|
||||||
|
company_name: Optional[str] = Field(None, max_length=100)
|
||||||
|
salary: Optional[int] = None
|
||||||
|
|
||||||
|
# ---------- 响应模型 ----------
|
||||||
|
class EmpOut(BaseModel):
|
||||||
|
stu_no: str
|
||||||
|
stu_name: Optional[str] = None
|
||||||
|
emp_open_time: Optional[datetime] = None
|
||||||
|
offer_time: Optional[datetime] = None
|
||||||
|
company_name: Optional[str] = None
|
||||||
|
salary: Optional[int] = None
|
||||||
|
|
||||||
|
model_config = ConfigDict(from_attributes=True)
|
||||||
@@ -30,18 +30,6 @@ class ScoreResp(ScoreCreate):
|
|||||||
class Config:
|
class Config:
|
||||||
from_attributes = True
|
from_attributes = True
|
||||||
|
|
||||||
# ----------------就业----------------
|
|
||||||
class EmploymentCreate(BaseModel):
|
|
||||||
stu_id: int
|
|
||||||
emp_open_time: date
|
|
||||||
offer_time: Optional[date] = None
|
|
||||||
company_name: Optional[str] = None
|
|
||||||
salary: Optional[float] = None
|
|
||||||
|
|
||||||
class EmploymentResp(EmploymentCreate):
|
|
||||||
class Config:
|
|
||||||
from_attributes = True
|
|
||||||
|
|
||||||
# ----------------班级----------------
|
# ----------------班级----------------
|
||||||
class ClassCreate(BaseModel):
|
class ClassCreate(BaseModel):
|
||||||
start_time: Optional[date] = None
|
start_time: Optional[date] = None
|
||||||
|
|||||||
Reference in New Issue
Block a user