Compare commits
65
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2ccae757c | ||
|
|
8f39d5456a | ||
|
|
f6e4cdedfc | ||
|
|
1b00ea1fad | ||
|
|
99a3f70349 | ||
|
|
f736845b6f | ||
|
|
4033dd86c1 | ||
|
|
ee548328d5 | ||
|
|
4e929295e3 | ||
|
|
37a5de11b8 | ||
|
|
fabe78cbcc | ||
|
|
81e9a2c534 | ||
|
|
48080554a7 | ||
|
|
23e29d7f1a | ||
|
|
2cc3829c88 | ||
|
|
cae04fd04a | ||
|
|
5ced78a009 | ||
|
|
73da38f445 | ||
|
|
68105a39d5 | ||
|
|
8ea5fcb5b8 | ||
|
|
ac545d0782 | ||
|
|
763fd4cb45 | ||
|
|
8a1022390f | ||
|
|
be2bbcb5ee | ||
|
|
fee9e14768 | ||
|
|
29dd7e1e04 | ||
|
|
67c3cd0452 | ||
|
|
b4be2b083c | ||
|
|
66ca2cc8b7 | ||
|
|
ed151b55ae | ||
|
|
c8961950ea | ||
|
|
fe7c95fa78 | ||
|
|
9a97c7d2a8 | ||
|
|
7cdad9a9fe | ||
|
|
1f87616004 | ||
|
|
3dcfaa5500 | ||
|
|
e88306a555 | ||
|
|
e71ac77c76 | ||
|
|
77de323412 | ||
|
|
3edce70bbb | ||
|
|
37054128bd | ||
|
|
71c7a3db2f | ||
|
|
acf5eb1441 | ||
|
|
6e526d9f6b | ||
|
|
2bc1e58047 | ||
|
|
def4b1d2c3 | ||
|
|
3976d8c20e | ||
|
|
1b9e5b8a57 | ||
|
|
c45cae80c7 | ||
|
|
e9507fef26 | ||
|
|
72434a78bc | ||
|
|
1f4fa614e5 | ||
|
|
e9288e3496 | ||
|
|
95714309c9 | ||
|
|
6bf636e76b | ||
|
|
33a6ab75da | ||
|
|
4627b4a57a | ||
|
|
53a48e0b21 | ||
|
|
519e9b180a | ||
|
|
962a793d13 | ||
|
|
55252780e2 | ||
|
|
868f100b84 | ||
|
|
60d4066b99 | ||
|
|
d0dbf696af | ||
|
|
d9a41a8738 |
@@ -0,0 +1,7 @@
|
||||
# .env(放项目根目录,每个人自己建,不提交 Git)
|
||||
# 用来修改数据库账户密码
|
||||
DB_USER=root
|
||||
DB_PASSWORD=420821
|
||||
DB_HOST=localhost
|
||||
DB_PORT=3306
|
||||
DB_NAME=student_manage_system
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
.venv/
|
||||
.idea
|
||||
__pycache__/
|
||||
*.pyc
|
||||
.pytest_cache/
|
||||
.mypy_cache/
|
||||
.ruff_cache/
|
||||
tests/
|
||||
.git/
|
||||
.github/
|
||||
*.md
|
||||
!README.md
|
||||
.env
|
||||
.env.*
|
||||
Dockerfile
|
||||
|
||||
Generated
+4
-2
@@ -1,10 +1,12 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# 已忽略包含查询文件的默认文件夹
|
||||
/queries/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
*.xml
|
||||
*.iml
|
||||
Generated
-3
@@ -1,7 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="uv (student_manage_system)" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10" project-jdk-type="Python SDK" />
|
||||
</project>
|
||||
Generated
+1
-1
@@ -2,7 +2,7 @@
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/student_manage_system.iml" filepath="$PROJECT_DIR$/.idea/student_manage_system.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/student-manage-system.iml" filepath="$PROJECT_DIR$/.idea/student-manage-system.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
@@ -0,0 +1 @@
|
||||
3.13
|
||||
@@ -1,29 +1,291 @@
|
||||
# 学生管理系统
|
||||
## 教师篇
|
||||
# 教培系统架构设计与开发
|
||||
|
||||
**main:主程序文件、测试接口**\
|
||||
1 下面所有配置文件的接口全部导入并建表\
|
||||
2 创建fastapi接口并本地运行\
|
||||
**api:接口文件**\
|
||||
*教师表Teacher的功能接口:*\
|
||||
1.教师信息的查询\
|
||||
2.教师信息的新增\
|
||||
3.教师信息的更新\
|
||||
4.教师信息的逻辑删除\
|
||||
**dao数据筛选文件**\
|
||||
1.查询 get_tea_dao:\
|
||||
2.新增 add_tea_dao:\
|
||||
3.更新 update_tea_dao:\
|
||||
4.逻辑删除 delete_tea_dao:\
|
||||
**tea_model数据库建表文件**\
|
||||
字段名:id、name、sex、class_id、sub(学科)\
|
||||
home_place specialty(专业)、start_time(入职时间)\
|
||||
work_experience、collage、creat_date、update_date\
|
||||
is_delete、delete_time\
|
||||
**schema 请求体、响应体**\
|
||||
请求体
|
||||
响应体
|
||||
*qingxie* \
|
||||
***zhongdian***
|
||||
本项目旨在开发一个基于FastAPI的学生管理系统,提供**学生基本信息管理、考核成绩管理和就业管理**三大核心功能模块。
|
||||
|
||||
系统将采用RESTful API设计,支持前后端分离架构。
|
||||
|
||||
|
||||
|
||||
## 需求分期
|
||||
|
||||
团队对需求管理按照模块化解耦拆分开发,按照三个阶段开发
|
||||
|
||||
1. 基础需求:按照开发任务,完成5 + 1 统计模块的开发
|
||||
> todo: 每个人补充一下自己对本模块的理解
|
||||
|
||||
|
||||
2. 特色需求:
|
||||
|
||||
- 数据源配置
|
||||
|
||||
- 鉴权管理
|
||||
|
||||
- 添加部门管理,顾问管理模块
|
||||
|
||||
- 添加用户认证模块
|
||||
|
||||
- 开发前端应用
|
||||
|
||||
- 开发更多的分析查询接口
|
||||
|
||||
- 增加AI智能体和知识库开发
|
||||
|
||||
3. 脚手架集成
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 第一阶段 - 基础需求
|
||||
|
||||
按照开发任务,完成5 + 1 统计模块的开发
|
||||
|
||||
- 学生
|
||||
- 班级
|
||||
- 老师
|
||||
- 成绩
|
||||
- 就业
|
||||
- 统计分析
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 第二阶段 - 特色需求
|
||||
|
||||
- 数据源配置
|
||||
|
||||
- 鉴权管理
|
||||
|
||||
- 添加部门管理,顾问管理模块
|
||||
|
||||
- 添加用户认证模块
|
||||
|
||||
- 开发前端应用
|
||||
|
||||
- 开发更多的分析查询接口
|
||||
|
||||
- 增加AI智能体和知识库开发
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 第三阶段 - 脚手架集成
|
||||
|
||||
核心组件
|
||||
|
||||
- 服务状态监控 - utc时区[还有哪些可替代?]
|
||||
- db ,中间件等组件是否活跃
|
||||
- 自身服务是否活跃
|
||||
- 认证模块: oauth2_scheme
|
||||
-
|
||||
-
|
||||
|
||||
|
||||
|
||||
## 业务拆分
|
||||
|
||||
> 业务范围划分
|
||||
>
|
||||
> - 实体
|
||||
> - 学生基本信息管理
|
||||
> - 考核成绩管理
|
||||
> - 就业管理
|
||||
> - 班级
|
||||
> - 老师
|
||||
> - 统计分析
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 数据架构设计
|
||||
|
||||
数据关系
|
||||
|
||||
- 学生:班级 = N: 1, 班级:老师 = 1 : 1 / [N:1], 学生: 成绩 = 1:N , 学生:就业 = 1:1,
|
||||
- 统计分析:
|
||||
|
||||
|
||||
|
||||
表结构优化
|
||||
|
||||
- 索引设置
|
||||
-
|
||||
|
||||
|
||||
|
||||
```sql
|
||||
create database student_management_system;
|
||||
use student_management_system;
|
||||
|
||||
-- students
|
||||
create table if not exists students(
|
||||
id int auto_increment primary key,
|
||||
snum int not null comment '学生编号',
|
||||
sname varchar(30) not null,
|
||||
age int not null,
|
||||
sex int not null,
|
||||
|
||||
home_place varchar(50) null comment '家乡',
|
||||
college varchar(50) null comment '毕业院校',
|
||||
specialty varchar(50) null comment '专业',
|
||||
enrollment_time datetime null comment '入学时间',
|
||||
graduate_time datetime null comment '毕业时间',
|
||||
education varchar(50) null comment '学历',
|
||||
|
||||
class_id int not null,
|
||||
|
||||
advisor_id int null comment '顾问id',
|
||||
advisor_num int null comment '顾问编号',
|
||||
|
||||
-- 通用字段
|
||||
create_time datetime not null,
|
||||
update_time datetime not null,
|
||||
is_detleted int not null comment '0 未删除,1'
|
||||
);
|
||||
|
||||
-- teachers
|
||||
create table if not exists teachers(
|
||||
id int auto_increment primary key,
|
||||
tnum int not null comment '老师编号',
|
||||
tname int not null,
|
||||
age int not null,
|
||||
sex int not null,
|
||||
|
||||
home_place varchar(50) null comment '家乡',
|
||||
college varchar(50) null comment '毕业院校',
|
||||
specialty varchar(50) null comment '专业',
|
||||
enrollment_time datetime null comment '入学时间',
|
||||
graduate_time datetime null comment '毕业时间',
|
||||
education varchar(50) null comment '学历',
|
||||
|
||||
-- 额外信息:工作经验/教学评级 ,教学方向:应用,项目,算法;
|
||||
work_experience text null comment '工作经验',
|
||||
coach_area text null comment '教学方向:应用,项目,算法',
|
||||
|
||||
-- 通用字段
|
||||
create_time datetime not null,
|
||||
update_time datetime not null,
|
||||
is_detleted int not null comment '0 未删除,1'
|
||||
);
|
||||
|
||||
|
||||
-- classes
|
||||
create table if not exists classes(
|
||||
id int auto_increment primary key,
|
||||
class_num int not null comment '班级编号',
|
||||
class_start_time datetime null comment '开课时间',
|
||||
head_teacher_id int null comment '班主任',
|
||||
head_teacher_num int null comment '班主任编号',
|
||||
|
||||
coach_teacher_id int null comment '授课老师',
|
||||
coach_teacher_num int null comment '授课老师编号',
|
||||
|
||||
tutor_teacher_id int null comment '助教老师',
|
||||
tutor_teacher_num int null comment '助教老师编号',
|
||||
|
||||
-- 通用字段
|
||||
create_time datetime not null,
|
||||
update_time datetime not null,
|
||||
is_detleted int not null comment '0 未删除,1'
|
||||
);
|
||||
|
||||
|
||||
-- scores
|
||||
create table if not exists scores(
|
||||
id int auto_increment primary key,
|
||||
sid int not null,
|
||||
snum int not null,
|
||||
score_num int not null comment '考核序次',
|
||||
score decimal(10,2) not null,
|
||||
|
||||
-- 通用字段
|
||||
create_time datetime not null,
|
||||
update_time datetime not null,
|
||||
is_detleted int not null comment '0 未删除,1'
|
||||
);
|
||||
|
||||
|
||||
-- employment
|
||||
create table if not exists employment(
|
||||
id int auto_increment primary key,
|
||||
sid int not null,
|
||||
snum int not null,
|
||||
sname varchar(30) not null,
|
||||
class_id int not null,
|
||||
class_num int not null comment '班级编号',
|
||||
|
||||
employment_open_time datetime null comment '就业开放时间',
|
||||
offer_recived_time datetime null comment 'offer下发时间',
|
||||
employment_company varchar(50) null comment '就业公司',
|
||||
employment_salary decimal(10,2) null comment '就业薪资',
|
||||
|
||||
-- 通用字段
|
||||
create_time datetime not null,
|
||||
update_time datetime not null,
|
||||
is_detleted int not null comment '0 未删除,1'
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 技术架构设计
|
||||
|
||||
### git 协作
|
||||
- 按照业务模块,每个人新建自己的分支,分支名称:user_module
|
||||
- main 分支:发版分支,test分支:做集成测试用
|
||||
|
||||
|
||||
### 构建环境适配
|
||||
|
||||
```bash
|
||||
# uv 统一
|
||||
pip install uv
|
||||
uv init
|
||||
uv add fastapi[standard] pymsql sqlalchemy # 添加依赖
|
||||
|
||||
# 运行测试
|
||||
uv run uvicorn main:app --reload # uv run fastapi dev
|
||||
|
||||
# 其他平台: sync 同步即可
|
||||
uv sync
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 基础架构适配
|
||||
|
||||
- 后端框架:
|
||||
- web route / api docs / 数据校验 : FastAPI
|
||||
- ORM 框架: pymysql, sqlarchemy
|
||||
- 日志系统:loguru,
|
||||
-
|
||||
- 数据库:多数据源适配
|
||||
- dev : sqlite3
|
||||
- prod: MySQL, PG
|
||||
- 前端框架:
|
||||
- vue3
|
||||
-
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 脚手架设计
|
||||
|
||||
|
||||
+4
-14
@@ -6,7 +6,7 @@ from Teachers.dao.tea_dao import *
|
||||
from Teachers.schema.tea_request import TeaRequest,NewTeaRequest
|
||||
from Teachers.schema.tea_response import TeaResponse
|
||||
|
||||
tea_api = APIRouter(tags=['老师相关接口'])
|
||||
tea_api = APIRouter()
|
||||
|
||||
@tea_api.post('/tea',response_model=TeaResponse,summary='新增老师',description='输入新增的老师信息,除去姓名外可以为空')
|
||||
def add_tea(tea: NewTeaRequest,db=Depends(get_db)):
|
||||
@@ -27,20 +27,10 @@ def delete_tea(id:int|None,db=Depends(get_db)):
|
||||
raise HTTPException(status_code=500,detail='删除失败!')
|
||||
return {'detail':'删除成功!'}
|
||||
|
||||
@tea_api.put('/tea/{id}',summary='更新老师信息',description='输入更改的信息,可以为空')
|
||||
@tea_api.put('/tea/{id}',summary='修改老师信息',description='输入更改的信息,老师的姓名和性别不能为空,其余可以为空')
|
||||
def update_tea(id:int,tea:NewTeaRequest,db=Depends(get_db)):
|
||||
t = tea.model_dump(exclude_unset= True)
|
||||
r = update_tea_dao(id=id,t=t,db=db)
|
||||
r = update_tea_dao(id=id,update_data=t,db=db)
|
||||
if not r:
|
||||
raise HTTPException(status_code=404,detail='老师不存在')
|
||||
return {'detail':'更新成功'}
|
||||
|
||||
#根据任意信息删除
|
||||
@tea_api.delete('/tea',summary='根据任意信息逻辑删除',description='输入任意信息删除')
|
||||
def delete_other_tea_dao1(t:TeaRequest,db=Depends(get_db)):
|
||||
row1 = delete_other_tea_dao(t.model_dump(exclude_unset=True),db)
|
||||
if not row1:
|
||||
raise HTTPException(status_code=404,detail='没有找到符合条件的老师')
|
||||
return{'code':200,'data':row1}
|
||||
|
||||
|
||||
return {'detail':'修改成功'}
|
||||
|
||||
+12
-45
@@ -14,15 +14,17 @@ def add_tea_dao(t,db):
|
||||
return True
|
||||
|
||||
#更新
|
||||
def update_tea_dao(id,t, db):
|
||||
def add_tea_dao(t, db):
|
||||
try:
|
||||
row = db.query(Tea).filter(Tea.id == id,Tea.is_delete ==False ).update(t)
|
||||
except:
|
||||
t1 = Tea(**t)
|
||||
db.add(t1)
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
print('新增失败原因:', e)
|
||||
return False
|
||||
else:
|
||||
db.commit()
|
||||
return row
|
||||
return True
|
||||
|
||||
#查询
|
||||
def get_tea_dao(t,db):
|
||||
@@ -34,7 +36,7 @@ def get_tea_dao(t,db):
|
||||
if t.get("sex"):
|
||||
query = query.filter(Tea.sex == t.get('sex') )
|
||||
if t.get('class_id'):
|
||||
query = query.filter(Tea.class_id == t.get('class_id') )
|
||||
query = query.filter(Tea.class_id == t.get('claa_id') )
|
||||
if t.get('sub'):
|
||||
query = query.filter(Tea.sub == t.get('sub') )
|
||||
if t.get('home_place'):
|
||||
@@ -47,11 +49,11 @@ def get_tea_dao(t,db):
|
||||
query = query.filter(Tea.work_experience == t.get('work_experience') )
|
||||
if t.get('education'):
|
||||
query = query.filter(Tea.education == t.get('education') )
|
||||
if t.get('collage'):
|
||||
query = query.filter(Tea.collage == t.get('collage') )
|
||||
if t.get('college'):
|
||||
query = query.filter(Tea.collage == t.get('college') )
|
||||
r = query.all()
|
||||
return [{'id':i.id,'name':i.name,'class_id':i.class_id,'sub':i.sub,'sex':i.sex,'home_place':i.home_place
|
||||
,'specialty':i.specialty,'start_time':i.start_time,'work_experience':i.work_experience,'collage':i.collage} for i in r]
|
||||
return [{'id':i.id,'name':i.name,'class_id':i.class_id,'sub':i.sub,'tea.sex':i.sex,'home_place':i.home_place
|
||||
,'specialty':i.specialty,'start_time':i.start_time,'work_experience':i.work_experience,'college':i.college} for i in r]
|
||||
|
||||
#删除
|
||||
def delete_tea_dao(id,db):
|
||||
@@ -62,44 +64,9 @@ def delete_tea_dao(id,db):
|
||||
except:
|
||||
db.rollback()
|
||||
row = 0
|
||||
return row
|
||||
return
|
||||
else:
|
||||
db.commit()
|
||||
return row
|
||||
|
||||
def delete_other_tea_dao(t,db):
|
||||
if not t:
|
||||
return False
|
||||
try:
|
||||
query1 = db.query(Tea).filter(Tea.is_delete == False)
|
||||
if t.get('id'):
|
||||
query1 = query1.filter(Tea.id == t.get('id') )
|
||||
if t.get('name'):
|
||||
query1 = query1.filter(Tea.name == t.get('name') )
|
||||
if t.get('sex'):
|
||||
query1 = query1.filter(Tea.sex == t.get('sex'))
|
||||
if t.get('class_id'):
|
||||
query1 = query1.filter(Tea.class_id == t.get('class_id'))
|
||||
if t.get('sub'):
|
||||
query1 = query1.filter(Tea.sub == t.get('sub'))
|
||||
if t.get('home_place'):
|
||||
query1 = query1.filter(Tea.home_place == t.get('home_place'))
|
||||
if t.get('collage'):
|
||||
query1 = query1.filter(Tea.collage == t.get('collage'))
|
||||
if t.get('education'):
|
||||
query1 = query1.filter(Tea.education == t.get('education'))
|
||||
if t.get('work_experience'):
|
||||
query1 = query1.filter(Tea.work_experience == t.get('work_experience'))
|
||||
if t.get('start_time'):
|
||||
query1 = query1.filter(Tea.start_time == t.get('start_time'))
|
||||
if t.get('specialty'):
|
||||
query1 = query1.filter(Tea.specialty == t.get('specialty'))
|
||||
row = query1.update({'is_delete':True,'delete_time':datetime.now()})
|
||||
db.commit()
|
||||
return row
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
print('删除失败',e)
|
||||
return '删除失败'
|
||||
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ DB_HOST = os.getenv("DB_HOST", "localhost")
|
||||
DB_PORT = os.getenv("DB_PORT", "3306")
|
||||
DB_NAME = os.getenv("DB_NAME", "student_manage_system")
|
||||
|
||||
db_tea = 'mysql+pymysql://root:123456@127.0.0.1:3306/student_manage_system?charset=utf8mb4'
|
||||
engine1 = create_engine(db_tea)
|
||||
db_url = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}?charset=utf8mb4"
|
||||
engine1 = create_engine(db_url)
|
||||
|
||||
Base = declarative_base()
|
||||
Session = sessionmaker(bind=engine1
|
||||
|
||||
+5
-1
@@ -1,8 +1,12 @@
|
||||
from fastapi import FastAPI,APIRouter
|
||||
from Teachers.model.tea_model import *
|
||||
from api.tea_api import tea_api
|
||||
from Teachers.api.tea_api import tea_api
|
||||
|
||||
# 合并接口
|
||||
Teachers_API = APIRouter()
|
||||
Teachers_API.include_router(tea_api)
|
||||
|
||||
# 自测接口
|
||||
app = FastAPI()
|
||||
app.include_router(tea_api)
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from databases import engine_all,Base_all,Session
|
||||
from Teachers.database import *
|
||||
from datetime import datetime
|
||||
from sqlalchemy import *
|
||||
@@ -7,7 +8,7 @@ class Gender(str,enum.Enum):
|
||||
m = '男'
|
||||
f = '女'
|
||||
|
||||
class Tea(Base):
|
||||
class Tea(Base_all):
|
||||
__tablename__ = 'teachers'
|
||||
id = Column(Integer
|
||||
, primary_key=True
|
||||
|
||||
@@ -16,7 +16,7 @@ class TeaRequest(BaseModel):
|
||||
specialty: str|None = None
|
||||
|
||||
|
||||
#更新、任意逻辑删除、新增使用请求体,没有自增键
|
||||
#更新、新增使用请求体(无法覆盖)
|
||||
class NewTeaRequest(BaseModel):
|
||||
name: str | None = None
|
||||
sex: Gender
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# 教培系统架构设计与开发##
|
||||
## 2
|
||||
### 3
|
||||
@@ -0,0 +1,82 @@
|
||||
from datetime import date
|
||||
from fastapi import APIRouter,Depends,HTTPException,Query
|
||||
from class_management.dao.class_management_dao import add_class_dao, update_class_dao, get_class_dao, delete_class_dao
|
||||
from class_management.database import get_db
|
||||
from class_management.schema.class_management_request import ClassCreate, ClassResponse, ClassUpdate
|
||||
|
||||
class_api = APIRouter(prefix='/classes')
|
||||
# ,tags=['班级管理模块']
|
||||
|
||||
@class_api.post("",summary='新增班级')
|
||||
def add_class(cla:ClassCreate,db=Depends(get_db)):
|
||||
c_dict = cla.model_dump()
|
||||
r = add_class_dao(c=c_dict,db=db)
|
||||
if not r:
|
||||
raise HTTPException(status_code=500,detail='服务器繁忙,请稍后添加!')
|
||||
return ClassResponse(totals=1,data=c_dict)
|
||||
|
||||
|
||||
@class_api.get('/{id}',summary='根据id查询班级')
|
||||
def get_one_class(id:int,db=Depends(get_db)):
|
||||
res = get_class_dao(c={'id':id},db=db)
|
||||
if not res:
|
||||
raise HTTPException(status_code=404, detail="该班级不存在或已删除")
|
||||
return ClassResponse(total=1,data=res[0])
|
||||
|
||||
@class_api.get('',summary='多字段查询班级')
|
||||
def get_some_classes( num:str|None = Query(None,description='班级编号')
|
||||
,name:str|None = Query(None,description='班级名称')
|
||||
,head_teacher_id:int|None = Query(None,description='班主任')
|
||||
,coach_teacher_id:int|None = Query(None,description='授课老师')
|
||||
,tutor_teacher_id:int|None = Query(None,description='助教老师')
|
||||
,class_start_time:date|None = Query(None, description="开班日期,格式YYYY‑MM‑DD")
|
||||
,class_end_time: date|None = Query(None, description="结课日期,格式YYYY‑MM‑DD")
|
||||
,page:int = Query(1,ge=1,description='页码,从1开始')
|
||||
,page_size:int = Query(5,ge=1,le=50,description='每页信息条数')
|
||||
,db=Depends(get_db)
|
||||
):
|
||||
|
||||
c_dict = {
|
||||
'num': num,
|
||||
'name': name,
|
||||
'head_teacher_id': head_teacher_id,
|
||||
'coach_teacher_id': coach_teacher_id,
|
||||
'tutor_teacher_id': tutor_teacher_id,
|
||||
'class_start_time': class_start_time,
|
||||
'class_end_time': class_end_time,
|
||||
}
|
||||
|
||||
c_dict={k:v for k,v in c_dict.items() if v is not None}#过滤掉值为None的key
|
||||
|
||||
res = get_class_dao(c=c_dict,db=db)
|
||||
total = len(res)
|
||||
|
||||
#内存分页
|
||||
start_index = (page-1)*page_size
|
||||
page_data = res[start_index:start_index + page_size]
|
||||
|
||||
return ClassResponse(totals=total,data=page_data)
|
||||
|
||||
|
||||
|
||||
|
||||
@class_api.put("/{class_id}",summary='修改班级信息')
|
||||
def update_class(class_id:int
|
||||
,update_info:ClassUpdate
|
||||
,db=Depends(get_db)
|
||||
):
|
||||
update_dict=update_info.model_dump(exclude_unset=True)
|
||||
if not update_dict:
|
||||
raise HTTPException(status_code=400, detail="没有传入要修改的字段")
|
||||
row_count = update_class_dao(cid=class_id, update_data=update_dict, db=db)
|
||||
if row_count <= 0:
|
||||
raise HTTPException(status_code=404, detail="班级不存在,修改失败")
|
||||
return ClassResponse(detail="修改成功", data={"class_id": id})
|
||||
|
||||
|
||||
@class_api.delete('/{id}',summary='逻辑删除班级')
|
||||
def remove_class(id:int,db=Depends(get_db)):
|
||||
del_rows = delete_class_dao(id,db)
|
||||
if del_rows <= 0:
|
||||
raise HTTPException(status_code=404, detail="班级不存在,删除失败")
|
||||
return ClassResponse(detail='删除成功',data={'id':id})
|
||||
@@ -0,0 +1,96 @@
|
||||
from class_management.model.class_management_model import ClassInfo
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
|
||||
#查询
|
||||
def get_class_dao(c:dict,db:Session) :
|
||||
q = db.query(ClassInfo).filter(ClassInfo.is_delete == 0)#is_delete=0代表未删除
|
||||
if c.get('id'):
|
||||
q = q.filter(ClassInfo.id == c.get('id'))
|
||||
if c.get('num'):
|
||||
q = q.filter(ClassInfo.num == c.get('num'))
|
||||
if c.get('name'):
|
||||
q = q.filter(ClassInfo.name == c.get('name'))
|
||||
if c.get('head_teacher_id'):
|
||||
q = q.filter(ClassInfo.head_teacher_id == c.get('head_teacher_id'))
|
||||
if c.get('coach_teacher_id'):
|
||||
q = q.filter(ClassInfo.coach_teacher_id == c.get('coach_teacher_id'))
|
||||
if c.get('tutor_teacher_id'):
|
||||
q = q.filter(ClassInfo.tutor_teacher_id == c.get('tutor_teacher_id'))
|
||||
if c.get('class_start_time'):
|
||||
q = q.filter(ClassInfo.class_start_time == c.get('class_start_time'))
|
||||
if c.get('class_end_time'):
|
||||
q = q.filter(ClassInfo.class_end_time == c.get('class_end_time'))
|
||||
res = q.all()
|
||||
if not res:
|
||||
return []
|
||||
return[
|
||||
{'id':i.id
|
||||
,'num':i.num
|
||||
,'name':i.name
|
||||
,'head_teacher_id':i.head_teacher_id
|
||||
,'coach_teacher_id':i.coach_teacher_id
|
||||
,'tutor_teacher_id':i.tutor_teacher_id
|
||||
,'class_start_time':i.class_start_time
|
||||
,'class_end_time':i.class_end_time
|
||||
,"is_delete": i.is_delete
|
||||
,"create_time": i.create_time
|
||||
,"update_time": i.update_time
|
||||
} for i in res
|
||||
]
|
||||
|
||||
|
||||
#新增班级
|
||||
def add_class_dao(c:dict,db:Session):
|
||||
try:
|
||||
exist_class = db.query(ClassInfo)\
|
||||
.filter(ClassInfo.name == c['name']
|
||||
,ClassInfo.is_delete == 0)\
|
||||
.first()
|
||||
if exist_class:
|
||||
msg = '班级名称已存在'
|
||||
print(msg)
|
||||
return (False,msg)
|
||||
c1 = ClassInfo(**c)
|
||||
db.add(c1)
|
||||
db.commit()
|
||||
db.refresh(c1)
|
||||
return (True,None)
|
||||
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
err_msg = f'数据库异常:{str(e)}'
|
||||
print(err_msg)
|
||||
return (False,err_msg)
|
||||
|
||||
|
||||
#修改
|
||||
def update_class_dao(cid:int,update_data,db):
|
||||
try:
|
||||
rows = db.query(ClassInfo)\
|
||||
.filter(ClassInfo.id == cid
|
||||
,ClassInfo.is_delete==0)\
|
||||
.update(update_data)
|
||||
db.commit()
|
||||
return rows
|
||||
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
print(f"update_class_dao异常:{e}")
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
#删除(逻辑删除)
|
||||
def delete_class_dao(cid,db):
|
||||
try:
|
||||
rows = db.query(ClassInfo)\
|
||||
.filter(ClassInfo.id == cid,ClassInfo.is_delete==0)\
|
||||
.update({'is_delete':1})
|
||||
db.commit()
|
||||
return rows
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
print(f"delete_class_dao异常:{e}")
|
||||
rows = 0
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker,declarative_base
|
||||
|
||||
# 导入.env参数
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv( )
|
||||
DB_USER = os.getenv("DB_USER", "root")
|
||||
DB_PASSWORD = os.getenv("DB_PASSWORD", "")
|
||||
DB_HOST = os.getenv("DB_HOST", "localhost")
|
||||
DB_PORT = os.getenv("DB_PORT", "3306")
|
||||
DB_NAME = os.getenv("DB_NAME", "student_manage_system")
|
||||
|
||||
class_management_rul = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}?charset=utf8mb4"
|
||||
|
||||
engine = create_engine(class_management_rul)
|
||||
SessionLocal = sessionmaker(bind=engine,autocommit=False, autoflush=False)
|
||||
Base = declarative_base()
|
||||
|
||||
# 获取数据库会话依赖
|
||||
|
||||
def get_db():
|
||||
db = SessionLocal()
|
||||
try :
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
@@ -0,0 +1,19 @@
|
||||
from fastapi import FastAPI,APIRouter
|
||||
from class_management.api.class_management_api import class_api
|
||||
from class_management.model.class_management_model import ClassInfo
|
||||
from class_management.database import Base,engine
|
||||
# import model
|
||||
|
||||
# 合并接口
|
||||
Classes_API = APIRouter()
|
||||
Classes_API.include_router(class_api)
|
||||
|
||||
# 自测接口
|
||||
app = FastAPI()
|
||||
app.include_router(class_api)
|
||||
|
||||
Base.metadata.create_all(engine)
|
||||
|
||||
if __name__ =='__main__':
|
||||
import uvicorn
|
||||
uvicorn.run('main:app',host='127.0.0.1',port=12345)
|
||||
@@ -0,0 +1,62 @@
|
||||
from databases import engine_all,Base_all,Session
|
||||
from sqlalchemy import Column, Integer, String, Date, ForeignKey
|
||||
from sqlalchemy.dialects.mysql import DATETIME
|
||||
from datetime import datetime
|
||||
from class_management.database import Base
|
||||
|
||||
class ClassInfo(Base_all):
|
||||
__tablename__ = 'classes'
|
||||
|
||||
id = Column(Integer
|
||||
,primary_key=True
|
||||
,autoincrement=True
|
||||
,comment='班级id主键'
|
||||
)
|
||||
num = Column(String(50)
|
||||
,nullable=False
|
||||
,comment='班级编号'
|
||||
)
|
||||
name = Column(String(50)
|
||||
,nullable=False
|
||||
,comment='班级名称'
|
||||
)
|
||||
head_teacher_id = Column(Integer
|
||||
# ,ForeignKey('teacher.id')
|
||||
,comment='班主任编号'
|
||||
)
|
||||
|
||||
coach_teacher_id = Column(Integer
|
||||
# ,ForeignKey('teacher.id')
|
||||
,comment='授课老师编号'
|
||||
)
|
||||
tutor_teacher_id = Column(Integer
|
||||
# ,ForeignKey('teacher.id')
|
||||
,comment='助教老师编号'
|
||||
)
|
||||
# class_teacher = Column(String(50)
|
||||
# ,nullable=False
|
||||
# ,comment='班主任'
|
||||
# )
|
||||
# class_lecturer = Column(String(50)
|
||||
# ,nullable=False
|
||||
# ,comment='授课老师'
|
||||
# )
|
||||
class_start_time = Column(Date
|
||||
,comment='开班日期'
|
||||
)
|
||||
class_end_time = Column(Date
|
||||
,comment='结课日期'
|
||||
)
|
||||
is_delete = Column(Integer
|
||||
,default=0
|
||||
,comment='逻辑删除 0未删除 1已删除'
|
||||
)
|
||||
create_time = Column(DATETIME
|
||||
,default=datetime.now
|
||||
,comment='记录创建时间'
|
||||
)
|
||||
update_time = Column(DATETIME
|
||||
,default=datetime.now
|
||||
,onupdate=datetime.now
|
||||
,comment='记录更新时间'
|
||||
)
|
||||
@@ -0,0 +1,32 @@
|
||||
from pydantic import BaseModel,Field
|
||||
from datetime import date,datetime
|
||||
from typing import Optional,List
|
||||
|
||||
#班级基础信息请求体
|
||||
class ClassBase(BaseModel):
|
||||
num:str|None = Field(None,description='班级编号')
|
||||
name:str|None = Field(None,description='班级名称')
|
||||
head_teacher_id:int|None = Field(None,description='班主任编号(teacher表id)')
|
||||
coach_teacher_id:int|None = Field(None,description='授课老师编号(teacher表id)')
|
||||
tutor_teacher_id:int|None = Field(None,description='助教老师编号(teacher表id)')
|
||||
class_start_time:date|None = Field(None,description='开班日期 YYYY‑MM‑DD')
|
||||
class_end_time:date|None = Field(None,description='结课日期 YYYY‑MM‑DD')
|
||||
|
||||
#新增班级请求体:num,name必填,其余继承父类ClassBase
|
||||
class ClassCreate(ClassBase):
|
||||
num:str = Field(...,description='班级编号')
|
||||
name:str = Field(...,description='班级名称')
|
||||
|
||||
#修改班级请求体,继承ClassBase,全部字段可选,传哪个就更新哪个
|
||||
class ClassUpdate(ClassBase):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class ClassResponse(BaseModel):
|
||||
code:int = 200
|
||||
detail:str = 'OK'
|
||||
totals:int = 0
|
||||
data:str|dict|tuple|list
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
# databases链接数据库
|
||||
|
||||
from sqlalchemy import *
|
||||
from sqlalchemy.orm import sessionmaker,declarative_base
|
||||
|
||||
# 导入.env参数
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv( )
|
||||
DB_USER = os.getenv("DB_USER", "root")
|
||||
DB_PASSWORD = os.getenv("DB_PASSWORD", "")
|
||||
DB_HOST = os.getenv("DB_HOST", "localhost")
|
||||
DB_PORT = os.getenv("DB_PORT", "3306")
|
||||
DB_NAME = os.getenv("DB_NAME", "student_manage_system")
|
||||
|
||||
db_url = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}?charset=utf8mb4"
|
||||
|
||||
engine_all = create_engine(db_url)
|
||||
|
||||
Base_all = declarative_base()
|
||||
|
||||
Session = sessionmaker(bind=engine_all
|
||||
,autoflush=False
|
||||
,autocommit=False)
|
||||
|
||||
def get_db():
|
||||
db = Session()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
from fastapi import FastAPI,APIRouter
|
||||
from class_management.main import Classes_API
|
||||
from scores.main import Scores_API
|
||||
from stu_jiuye.main import Shtudent_Jiuye
|
||||
from students.main import Students_API
|
||||
from Teachers.main import Teachers_API
|
||||
from databases import *
|
||||
app = FastAPI(title="学生管理系统")
|
||||
|
||||
Base_all.metadata.create_all(bind=engine_all)
|
||||
|
||||
app.include_router(Classes_API,tags=["classes"])
|
||||
app.include_router(Scores_API,tags=["scores"])
|
||||
app.include_router(Shtudent_Jiuye,tags=["Employments"])
|
||||
app.include_router(Students_API,tags=["students"])
|
||||
app.include_router(Teachers_API,tags=["teachers"])
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
print("Hello from student-manage-system!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run("main:app", host="0.0.0.0", port=59000)
|
||||
"""
|
||||
IPv4 地址 . . . . . . . . . . . . : 192.168.6.40
|
||||
子网掩码 . . . . . . . . . . . . : 255.255.255.0
|
||||
默认网关. . . . . . . . . . . . . : 192.168.6.1
|
||||
"""
|
||||
@@ -0,0 +1,14 @@
|
||||
[project]
|
||||
name = "student-manage-system"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.13"
|
||||
dependencies = [
|
||||
"fastapi[standard]>=0.141.1",
|
||||
"like>=1.7.1",
|
||||
"pip>=26.2.1",
|
||||
"pymysql>=1.2.3",
|
||||
"sqlalchemy>=2.0.54",
|
||||
"uvicorn>=0.53.0",
|
||||
]
|
||||
@@ -0,0 +1,45 @@
|
||||
from fastapi import APIRouter, Depends,HTTPException
|
||||
from scores.database import get_db
|
||||
from scores.dao.score_dao import get_scores_dao,add_scores_dao,update_scores_dao,delete_scores_dao
|
||||
from scores.model.score_model import Score
|
||||
from scores.schema.score_request import ScoreRequest, ScoreResponse,ScoreQuery
|
||||
|
||||
score_api = APIRouter()
|
||||
|
||||
@score_api.get('/Score')
|
||||
def get_scores(score:ScoreQuery=Depends(),db=Depends(get_db)):
|
||||
a = score.model_dump()
|
||||
aa = get_scores_dao(s=a,db=db)
|
||||
if aa:
|
||||
return aa
|
||||
raise HTTPException(status_code=404,detail='不存在')
|
||||
|
||||
@score_api.post('/Score',response_model=ScoreResponse)
|
||||
def add_scores(score:ScoreRequest=Depends(),db=Depends(get_db)):
|
||||
a = score.model_dump()
|
||||
# print(a,type(a))
|
||||
aa = add_scores_dao(b=a,db=db)
|
||||
if aa:
|
||||
return ScoreResponse(data=a)
|
||||
raise HTTPException(status_code=500,detail='没有')
|
||||
|
||||
@score_api.put('/Score')
|
||||
def update_scores(score:ScoreRequest,sid:int,cid:int,num:int,tsub:str,db=Depends(get_db)):
|
||||
a = score.model_dump(exclude_unset=True,exclude={'id','sid','cid','num','tsub'})
|
||||
aa = update_scores_dao(sid = sid,cid = cid,num = num,tsub = tsub,update_data = a,db=db)
|
||||
if aa==-1:
|
||||
raise HTTPException(status_code=500,detail='没有更新')
|
||||
if aa==0:
|
||||
raise HTTPException(status_code=404,detail='不存在')
|
||||
return {'message':'更新成功','data':a}
|
||||
|
||||
@score_api.delete('/Score')
|
||||
def delete_scores(sid:int,cid:int,tsub:str,db=Depends(get_db)):
|
||||
aaa = delete_scores_dao(sid,cid,tsub,db)
|
||||
if aaa==1:
|
||||
raise HTTPException(status_code=500,detail='删除失败')
|
||||
if aaa==0:
|
||||
raise HTTPException(status_code=404,detail='记录不存在')
|
||||
return {'message':'更新成功','detail':aaa}
|
||||
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
from scores.model.score_model import Score
|
||||
from datetime import datetime
|
||||
|
||||
def get_scores_dao(s,db):
|
||||
ab = db.query(Score).filter(Score.is_deleted ==0)
|
||||
if s.get('id'):
|
||||
ab = ab.filter(Score.id==s.get('id'))
|
||||
if s.get('sid'):
|
||||
ab = ab.filter(Score.sid==s.get('sid'))
|
||||
if s.get('cid'):
|
||||
ab = ab.filter(Score.cid==s.get('cid'))
|
||||
if s.get('num'):
|
||||
ab = ab.filter(Score.num==s.get('num'))
|
||||
if s.get('score'):
|
||||
ab = ab.filter(Score.score==s.get('score'))
|
||||
if s.get('tsub'):
|
||||
ab = ab.filter(Score.tsub==s.get('tsub'))
|
||||
aa = ab.all()
|
||||
if aa:
|
||||
return [{'id':i.id,'sid':i.sid
|
||||
,'cid':i.cid,'num':i.num
|
||||
,'score': i.score,'tsub':i.tsub
|
||||
,'create_date':i.create_date,'update_date':i.update_date
|
||||
,'is_deleted': i.is_deleted,'deleted_date': i.deleted_date
|
||||
}for i in aa]
|
||||
else:
|
||||
return []
|
||||
|
||||
def add_scores_dao(b,db):
|
||||
try:
|
||||
aa = db.query(Score).filter(Score.sid==b['sid']
|
||||
,Score.tsub==b['tsub']).all()
|
||||
if aa:
|
||||
raise ValueError
|
||||
else:
|
||||
z = Score(**b)
|
||||
db.add(z)
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
print(e)
|
||||
return False
|
||||
else:
|
||||
db.commit()
|
||||
return True
|
||||
|
||||
def update_scores_dao(sid:int,cid:int,num:int,tsub:str,update_date:dict,db):
|
||||
if not update_date:
|
||||
return 0
|
||||
update_date['update_date']=datetime.now
|
||||
try:
|
||||
aa = db.query(Score).filter(Score.sid == sid
|
||||
,Score.cid == cid
|
||||
,Score.num == num
|
||||
,Score.tsub == tsub ).update(update_date)
|
||||
db.commit()
|
||||
return aa
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
print(e)
|
||||
return -1
|
||||
|
||||
|
||||
def delete_scores_dao(sid:int,cid:int,tsub:str,db):
|
||||
try:
|
||||
aaa = db.query(Score).filter(Score.sid ==sid
|
||||
,Score.cid == cid
|
||||
,Score.tsub == tsub
|
||||
,Score.is_deleted==0
|
||||
).update({'is_deleted':1
|
||||
,'deleted_date':datetime.now})
|
||||
db.commit()
|
||||
return aaa
|
||||
except Exception :
|
||||
db.rollback()
|
||||
return -1
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
from sqlalchemy import *
|
||||
from sqlalchemy.orm import declarative_base,sessionmaker
|
||||
|
||||
# 导入.env参数
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv( )
|
||||
DB_USER = os.getenv("DB_USER", "root")
|
||||
DB_PASSWORD = os.getenv("DB_PASSWORD", "")
|
||||
DB_HOST = os.getenv("DB_HOST", "localhost")
|
||||
DB_PORT = os.getenv("DB_PORT", "3306")
|
||||
DB_NAME = os.getenv("DB_NAME", "student_manage_system")
|
||||
|
||||
db_url = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}?charset=utf8mb4"
|
||||
engine = create_engine(db_url)
|
||||
|
||||
Base = declarative_base()
|
||||
Session = sessionmaker(bind=engine, autoflush = False, autocommit = False)
|
||||
|
||||
def get_db():
|
||||
db = Session()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
@@ -0,0 +1,17 @@
|
||||
from fastapi import FastAPI,APIRouter
|
||||
from scores.api.score_api import score_api
|
||||
from scores.database import engine,Base
|
||||
|
||||
# 合并接口
|
||||
Scores_API = APIRouter()
|
||||
Scores_API.include_router(score_api)
|
||||
|
||||
# 自测接口
|
||||
app = FastAPI()
|
||||
app.include_router(score_api)
|
||||
|
||||
Base.metadata.create_all(engine)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import uvicorn
|
||||
uvicorn.run('main:app',host='127.0.0.1',port=12346)
|
||||
@@ -0,0 +1,38 @@
|
||||
from databases import engine_all,Base_all,Session
|
||||
from scores.database import Base
|
||||
from datetime import datetime
|
||||
from sqlalchemy import DateTime,Column,Integer,String,Float
|
||||
class Score(Base_all):
|
||||
__tablename__='scores'
|
||||
id = Column(Integer
|
||||
,primary_key = True
|
||||
,autoincrement = True
|
||||
,comment ='编号'
|
||||
)
|
||||
sid = Column(Integer
|
||||
,comment ='学生ID'
|
||||
)
|
||||
cid = Column(Integer
|
||||
,comment ='班级ID'
|
||||
)
|
||||
num = Column(Integer
|
||||
,comment ='考试序次'
|
||||
)
|
||||
score = Column(Integer,nullable = False)
|
||||
tsub = Column(String(100)
|
||||
,nullable = False
|
||||
)
|
||||
create_date = Column(DateTime
|
||||
,default = datetime.now
|
||||
)
|
||||
update_date = Column(DateTime
|
||||
,default = datetime.now
|
||||
)
|
||||
is_deleted = Column(Integer
|
||||
,default = 0
|
||||
,comment='是否删除: 0-正常,1-已删除'
|
||||
)
|
||||
deleted_date=Column(DateTime
|
||||
,default=None
|
||||
,comment='删除时间'
|
||||
)
|
||||
@@ -0,0 +1,23 @@
|
||||
from pydantic import BaseModel
|
||||
|
||||
class ScoreRequest(BaseModel):
|
||||
# id:int
|
||||
sid:int
|
||||
cid:int
|
||||
num:int
|
||||
score:int
|
||||
tsub:str
|
||||
|
||||
class ScoreQuery(BaseModel):
|
||||
id:int|None = None
|
||||
sid:int|None = None
|
||||
cid:int|None = None
|
||||
num:int|None = None
|
||||
score:int|None = None
|
||||
tsub:str|None = None
|
||||
|
||||
class ScoreResponse(BaseModel):
|
||||
code:int = 200
|
||||
detail:str = 'OK'
|
||||
totals:int = 0
|
||||
data:str|dict|tuple|list
|
||||
@@ -0,0 +1,128 @@
|
||||
import datetime
|
||||
|
||||
from fastapi import APIRouter,Depends,HTTPException
|
||||
from stu_jiuye.dao import *
|
||||
from stu_jiuye.request import PostRequest, EmpResponse, AddRequest,AddResponse,CompRequest,CompResponse
|
||||
from stu_jiuye.database import get_db
|
||||
CURD=APIRouter()
|
||||
|
||||
|
||||
@CURD.get('/emp',summary='查询就业信息')
|
||||
def get_stuinfo( #查询学生就业信息
|
||||
sname:str| None = None
|
||||
,class_num:int| None = None
|
||||
,employment_company:str|None=None
|
||||
,min_salary:int=0
|
||||
,max_salary:int|None=None
|
||||
,somewhere:str|None=None
|
||||
,employment_open_time:datetime.date|None=None
|
||||
,offer_recived_time:datetime.date|None=None
|
||||
,db=Depends(get_db)
|
||||
): #查询条件
|
||||
r=get_emp_dao( #调用函数
|
||||
sname,
|
||||
class_num,
|
||||
employment_company,
|
||||
min_salary,
|
||||
max_salary,
|
||||
somewhere,
|
||||
employment_open_time,
|
||||
offer_recived_time,
|
||||
db
|
||||
)
|
||||
if r:
|
||||
return r #成功返回
|
||||
raise HTTPException(status_code=404, detail='名单不存在!') #失败返回错误信息
|
||||
|
||||
@CURD.post('/emp',response_model=EmpResponse,summary='新增就业信息')
|
||||
def add_stuinfo( #新增学生就业信息
|
||||
p:PostRequest,db=Depends(get_db)
|
||||
): #调用请求体
|
||||
d=p.model_dump() #解析成字典
|
||||
r=add_employment_dao(o=d,db=db) #调用函数
|
||||
if not r:
|
||||
raise HTTPException(status_code=500, detail='服务器繁忙,请稍后添加!') #失败返回错误
|
||||
return EmpResponse(totals=1, data=d) #成功返回响应体
|
||||
|
||||
@CURD.post('/emp/company',response_model=CompResponse,summary='新增公司信息')
|
||||
def add_company( #新增公司表信息
|
||||
c:CompRequest,db=Depends(get_db)
|
||||
):
|
||||
d = c.model_dump() #解析
|
||||
r = add_company_dao(o=d, db=db) #调用添加公司函数
|
||||
if not r:
|
||||
raise HTTPException(status_code=500, detail='服务器繁忙,请稍后添加!') #失败返回错误信息
|
||||
return CompResponse(totals=1, data=d) #成功返回响应体
|
||||
|
||||
|
||||
@CURD.post('/emp/company/address',response_model=AddResponse,summary='新增公司所在区域')
|
||||
def add_address( #新增公司地址区域信息
|
||||
add:AddRequest,db=Depends(get_db)
|
||||
):
|
||||
d = add.model_dump() #解析
|
||||
r = add_address_dao(o=d, db=db) #调用添加地址函数
|
||||
if not r:
|
||||
raise HTTPException(status_code=500, detail='服务器繁忙,请稍后添加!') #失败返回错误信息
|
||||
return AddResponse(totals=1, data=d) #成功返回响应体
|
||||
|
||||
|
||||
@CURD.put('/emp',summary='更改就业信息')
|
||||
def updata_stuinfo( #更新数据模块
|
||||
up:PostRequest
|
||||
,id:int
|
||||
,db=Depends(get_db)
|
||||
): #根据id选择对象
|
||||
d = up.model_dump(exclude_unset=True) #解析
|
||||
d.pop('create_time', None)
|
||||
d.pop('is_deleted', None)
|
||||
r = update_emp_dao(id=id, update_data=d, db=db) #调用更新函数
|
||||
if not d:
|
||||
raise HTTPException(status_code=400, detail='没有需要更新的字段')
|
||||
if not r:
|
||||
raise HTTPException(status_code=404, detail='未找到对应记录') #失败返回错误信息
|
||||
return {'code': 200, 'totals': r, 'detail': '更新成功'} #成功返回信息
|
||||
|
||||
@CURD.delete('/emp/{id}',summary='删除就业信息')
|
||||
def delete_stuinfo( #删除模块
|
||||
id:int
|
||||
,db=Depends(get_db)
|
||||
): #根据id选择对象
|
||||
rows = delete_emp_dao(id, db) #调用删除函数
|
||||
if not rows:
|
||||
raise HTTPException(status_code=500, detail='没有删除!') #失败返回错误信息
|
||||
return {'code': 200, 'totals': rows, 'detail': '删除成功'} #成功返回信息
|
||||
|
||||
|
||||
@CURD.get('/Top5',summary='就业薪资Top5')
|
||||
def salarytop5(db=Depends(get_db)):
|
||||
q = db.query(Employments,Company.employment_company) \
|
||||
.join(Company, Employments.company_id == Company.id) \
|
||||
.filter(Employments.is_deleted == 0)\
|
||||
.order_by(Employments.employment_salary.desc())\
|
||||
.limit(5) .all()
|
||||
return [{'学生姓名':i.sname,'学生班级编号':i.class_num,'就业时间':i.offer_recived_time,'公司':employment_company} for i,employment_company in q]
|
||||
|
||||
@CURD.get('/worktime',summary='学生就业时常')
|
||||
def worktime(db=Depends(get_db)):
|
||||
q = db.query(Employments)\
|
||||
.filter(Employments.is_deleted==0)\
|
||||
.all()
|
||||
|
||||
return [{'学生姓名':i.sname,'就业时常':f'{i.offer_recived_time-i.employment_open_time}' if i.offer_recived_time and i.employment_open_time else '暂无数据'} for i in q]
|
||||
|
||||
|
||||
@CURD.get('/avgworktime',summary='学生平均就业时常')
|
||||
def avgworktime(db=Depends(get_db)):
|
||||
q = db.query(Employments)\
|
||||
.filter(Employments.employment_open_time.isnot(None)
|
||||
,Employments.offer_recived_time.isnot(None)
|
||||
,Employments.is_deleted==0)\
|
||||
.all()
|
||||
if not q:
|
||||
return {'平均就业时常': '暂无数据'}
|
||||
total = sum((i.offer_recived_time - i.employment_open_time).total_seconds() for i in q)
|
||||
avg_days=total /len(q)/86400 #一天86400秒
|
||||
return {
|
||||
'人数': len(q),
|
||||
'平均就业时常(天)': round(avg_days, 2)
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
|
||||
from stu_jiuye.model import Employments,Address,Company
|
||||
|
||||
|
||||
def add_employment_dao(o,db): #新增就业信息函数
|
||||
try:
|
||||
# r = db.query(Employments).filter(Employments.id == o['id'] ).all() #
|
||||
o.pop('id', None) #摘除id字段,自增字段无需手动添加
|
||||
o.pop('create_time', None)
|
||||
o.pop('update_time', None)
|
||||
o.pop('is_deleted', None)
|
||||
o1 = Employments( **o) #实例化参数
|
||||
db.add(o1) #添加数据进入表
|
||||
|
||||
except Exception as e: #失败返回
|
||||
print(f'添加失败,{e}')
|
||||
db.rollback() #回滚数据
|
||||
return False
|
||||
else:
|
||||
db.commit() #提交事务,永久保存
|
||||
return True
|
||||
|
||||
def add_company_dao(o,db): #添加公司信息函数
|
||||
try:
|
||||
o.pop('id', None) #摘除id字段,自增字段无需手动添加
|
||||
o.pop('create_time', None)
|
||||
o.pop('update_time', None)
|
||||
o.pop('is_deleted', None)
|
||||
o1 = Company( **o) #实例化参数
|
||||
db.add(o1) #添加数据进入表
|
||||
except Exception as e: #失败返回
|
||||
print(f'添加失败,{e}')
|
||||
db.rollback() #回滚数据
|
||||
return False
|
||||
else:
|
||||
db.commit() #提交事务,永久保存
|
||||
return True
|
||||
def add_address_dao(o,db): #添加地址区域信息函数
|
||||
try:
|
||||
o.pop('id', None) #摘除id字段,自增字段无需手动添加
|
||||
o.pop('create_time', None)
|
||||
o.pop('update_time', None)
|
||||
o.pop('is_deleted', None)
|
||||
o1 = Address( **o) #实例化参数
|
||||
db.add(o1) #添加数据进入表
|
||||
except Exception as e: #失败返回
|
||||
print(f'添加失败,{e}')
|
||||
db.rollback() #回滚数据
|
||||
return False
|
||||
else:
|
||||
db.commit() #提交事务,永久保存
|
||||
return True
|
||||
|
||||
|
||||
def update_emp_dao(id,update_data,db): #更新数据函数
|
||||
try:
|
||||
rows = db.query( Employments )\
|
||||
.filter( Employments.id == id )\
|
||||
.update( update_data ) #更新条件匹配id
|
||||
except Exception as e:
|
||||
print(f'更新失败,{e}') #返回失败信息
|
||||
db.rollback() #数据回滚
|
||||
return 0
|
||||
else:
|
||||
db.commit() #提交事务,永久保存
|
||||
return rows
|
||||
|
||||
def get_emp_dao( #获取学生就业信息函数
|
||||
# stuid,
|
||||
sname
|
||||
,class_num
|
||||
,employment_company
|
||||
,min_salary
|
||||
,max_salary
|
||||
,somewhere
|
||||
,employment_open_time
|
||||
,offer_recived_time
|
||||
,db
|
||||
|
||||
# ,page
|
||||
# ,page_size
|
||||
): #根据选择条件获取信息
|
||||
q = db.query(Employments,Company.employment_company,Address.somewhere)\
|
||||
.outerjoin(Company,Employments.company_id==Company.id)\
|
||||
.outerjoin(Address,Company.address_id==Address.id)\
|
||||
.filter(Employments.is_deleted == 0) #连接公司表和地址表,筛选未被删除信息
|
||||
# if stuid:
|
||||
# q = q.filter( Employments.id == stuid )
|
||||
|
||||
if sname: #根据学生姓名筛选
|
||||
q = q.filter( Employments.sname == sname )
|
||||
if class_num:
|
||||
q = q.filter(Employments.class_num == class_num)
|
||||
if somewhere:
|
||||
q = q.filter(Address.somewhere.like(f'%{somewhere}%'))
|
||||
if employment_company: #根据公司姓名筛选
|
||||
q=q.filter(Company.employment_company.like(f'%{employment_company}%'))
|
||||
# r = q.offset( (page-1)*page_size ).limit( page_size ).all()
|
||||
if min_salary is not None: #根据最小薪资筛选
|
||||
q = q.filter(Employments.employment_salary >= min_salary)
|
||||
if max_salary is not None: ##根据最大薪资筛选
|
||||
q = q.filter(Employments.employment_salary <= max_salary)
|
||||
if employment_open_time:
|
||||
q = q.filter(Employments.employment_open_time == employment_open_time)
|
||||
if offer_recived_time:
|
||||
q = q.filter(Employments.offer_recived_time == offer_recived_time)
|
||||
r= q.all()
|
||||
if r:
|
||||
return [ {"id": i.id,
|
||||
'学生姓名':i.sname,
|
||||
'学生班级编号':i.class_num,
|
||||
"公司": employment_company or '未就业',
|
||||
"公司区域":somewhere or '-',
|
||||
# "company_id": i.company_id,
|
||||
"薪资":i.employment_salary or '-',
|
||||
# "address_id": i.address_id,
|
||||
"就业开放时间":i.employment_open_time,
|
||||
"offer下发时间":i.offer_recived_time or '-',
|
||||
"创建时间": i.create_time,
|
||||
"更新时间": i.update_time,
|
||||
"是否删除":i.is_deleted } for i,employment_company,somewhere in r ] #返回信息列表
|
||||
|
||||
|
||||
def delete_emp_dao(id,db): #删除函数
|
||||
try:
|
||||
rows = db.query(Employments)\
|
||||
.filter(Employments.id == id,Employments.is_deleted == 0)\
|
||||
.update( {'is_deleted':1} ) #软删除,is_deleted返回1
|
||||
|
||||
except Exception as e: #失败返回信息
|
||||
print(f'删除失败,{e}')
|
||||
db.rollback() #数据回滚
|
||||
rows = 0 #返回0
|
||||
else:
|
||||
db.commit() #提交事务,永久保存
|
||||
return rows
|
||||
@@ -0,0 +1,34 @@
|
||||
from sqlalchemy import *
|
||||
from sqlalchemy.orm import declarative_base,sessionmaker
|
||||
|
||||
# 导入.env参数
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv( )
|
||||
DB_USER = os.getenv("DB_USER", "root")
|
||||
DB_PASSWORD = os.getenv("DB_PASSWORD", "")
|
||||
DB_HOST = os.getenv("DB_HOST", "localhost")
|
||||
DB_PORT = os.getenv("DB_PORT", "3306")
|
||||
DB_NAME = os.getenv("DB_NAME", "student_manage_system")
|
||||
|
||||
db_url = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}?charset=utf8mb4"
|
||||
engine = create_engine(db_url
|
||||
, pool_size=50
|
||||
, echo=True
|
||||
)
|
||||
|
||||
Base = declarative_base()
|
||||
Session = sessionmaker(bind=engine
|
||||
,autoflush=False
|
||||
,autocommit = False
|
||||
)
|
||||
|
||||
|
||||
def get_db():
|
||||
db = Session()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
from fastapi import FastAPI,APIRouter
|
||||
from stu_jiuye.CURD_api import CURD
|
||||
from stu_jiuye.database import Base,engine
|
||||
|
||||
# 合并接口
|
||||
Shtudent_Jiuye = APIRouter()
|
||||
Shtudent_Jiuye.include_router(CURD)
|
||||
|
||||
# 自测接口
|
||||
app=FastAPI()
|
||||
Base.metadata.create_all(engine)
|
||||
app.include_router(CURD,tags=['就业管理系统'])
|
||||
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
import uvicorn
|
||||
uvicorn.run('main:app', host='127.0.0.1', port=12395)
|
||||
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
from databases import engine_all,Base_all,Session
|
||||
from sqlalchemy import ForeignKey
|
||||
|
||||
from stu_jiuye.database import DATETIME,Base,Column,Integer,String,DATE
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
# class Student(Base):
|
||||
# __tablename__ = 'Student'
|
||||
# id = Column(Integer
|
||||
# , primary_key=True
|
||||
# , autoincrement=True
|
||||
# # 外键
|
||||
# ,comment='自增主键'
|
||||
# )
|
||||
# stuname = Column(String(50)
|
||||
# ,nullable=False
|
||||
# )
|
||||
# stuid=Column(Integer
|
||||
# ,nullable=False
|
||||
# )
|
||||
# stuclass=Column(String(50)
|
||||
# ,nullable=False
|
||||
# )
|
||||
# created_date = Column(DATETIME
|
||||
# ,default=datetime.now
|
||||
# )
|
||||
# updated_date = Column(DATETIME
|
||||
# ,default=datetime.now
|
||||
# ,onupdate=datetime.now
|
||||
# )
|
||||
|
||||
|
||||
class Employments(Base_all): #学生就业信息基类
|
||||
__tablename__ = 'Employments' #表名
|
||||
id = Column(Integer #主键,自增主键
|
||||
, primary_key=True
|
||||
, autoincrement=True
|
||||
# , ForeignKey='student.id'
|
||||
, comment='自增主键'
|
||||
)
|
||||
sname = Column(String(50)) #学生姓名,冗余字段
|
||||
class_num = Column(Integer) #学生班级,冗余字段
|
||||
|
||||
# compname=Column(String(50)
|
||||
# ,nullable=False
|
||||
# )
|
||||
company_id = Column(Integer #公司id,外键匹配公司表
|
||||
, ForeignKey('Company.id')
|
||||
, nullable=True
|
||||
|
||||
)
|
||||
# address_id = Column(Integer
|
||||
# , ForeignKey('Address.id')
|
||||
# , nullable=False
|
||||
# )
|
||||
employment_salary = Column(Integer, nullable=True) #薪资
|
||||
|
||||
employment_open_time = Column(DATE, nullable=True) #就业开放时间
|
||||
|
||||
offer_recived_time = Column(DATE, nullable=True) #offer下发时间
|
||||
|
||||
create_time = Column(DATETIME #创建数据时间
|
||||
, default=datetime.now
|
||||
)
|
||||
update_time = Column(DATETIME #更新数据时间
|
||||
, default=datetime.now
|
||||
, onupdate=datetime.now
|
||||
)
|
||||
is_deleted = Column(Integer, default=0) #是否为删除信息,默认为0
|
||||
|
||||
class Company(Base_all): #公司信息基类
|
||||
__tablename__ = 'Company' #表名
|
||||
id = Column(Integer
|
||||
, primary_key=True
|
||||
,autoincrement=True
|
||||
) #主键id,自增主键
|
||||
employment_company = Column(String(50) #公司姓名
|
||||
, nullable=False #非空约束
|
||||
)
|
||||
address_id = Column(Integer #地址id,匹配地址表
|
||||
, ForeignKey('Address.id') #外键地址表id
|
||||
, nullable=False #非空约束
|
||||
)
|
||||
created_date = Column(DATETIME
|
||||
, default=datetime.now
|
||||
)
|
||||
updated_date = Column(DATETIME
|
||||
, default=datetime.now
|
||||
, onupdate=datetime.now
|
||||
)
|
||||
is_deleted = Column(Integer, default=0)
|
||||
|
||||
class Address(Base_all):
|
||||
__tablename__ = 'Address'
|
||||
id = Column(Integer #地址id,自增主键
|
||||
, primary_key=True
|
||||
,autoincrement=True
|
||||
)
|
||||
somewhere=Column(String(50) #地址区域
|
||||
, nullable=False
|
||||
)
|
||||
created_date = Column(DATETIME
|
||||
, default=datetime.now
|
||||
)
|
||||
updated_date = Column(DATETIME
|
||||
, default=datetime.now
|
||||
, onupdate=datetime.now
|
||||
)
|
||||
is_deleted = Column(Integer, default=0)
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
from datetime import *
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class PostRequest(BaseModel): #学生就业信息请求体模型
|
||||
# compname: str|None=None
|
||||
sname: str|None=None
|
||||
class_num: int|None=None
|
||||
company_id:int|None=None
|
||||
# address_id: int|None=None
|
||||
employment_salary: int|None=None
|
||||
employment_open_time:date|None=None
|
||||
offer_recived_time:date|None=None
|
||||
|
||||
class EmpResponse(BaseModel): #学生就业信息响应体
|
||||
code: int = 200
|
||||
detail: str = 'OK'
|
||||
totals: int = 0
|
||||
data: str | dict | tuple | list
|
||||
|
||||
|
||||
class CompRequest(BaseModel): #公司信息请求体模型
|
||||
employment_company:str
|
||||
address_id: int
|
||||
|
||||
class CompResponse(BaseModel): #公司信息响应体
|
||||
code: int = 200
|
||||
detail: str = 'OK'
|
||||
totals: int = 0
|
||||
data: str | dict | tuple | list
|
||||
|
||||
class AddRequest(BaseModel): #地址请求体
|
||||
somewhere:str
|
||||
|
||||
class AddResponse(BaseModel): #地址响应体
|
||||
code: int = 200
|
||||
detail: str = 'OK'
|
||||
totals: int = 0
|
||||
data: str | dict | tuple | list
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
# students模块解释
|
||||
|
||||
## 项目层:
|
||||
web框架:FastAPI(自动生成接口)
|
||||
ORM:sqlalchemy
|
||||
校验:pydantic
|
||||
数据库:MYSQL(通过 PyMysql 驱动)
|
||||
运行:uvicorn,端口暂定52000
|
||||
|
||||
总结:用 FastAPI 暴露一组 HTTP 接口
|
||||
, 对 MySQL 里的一张 `students` 表做**增、删、改、查**
|
||||
, 其中删除是**逻辑删除(软删除)**
|
||||
, 数据不会被真正抹掉。
|
||||
|
||||
项目根目录是`main.py`是程序入口,而`students/`是一个
|
||||
|
||||
## main:学生管理系统的主程序文件、测试接口
|
||||
1. 下面所有配置文件的接口全部导入并建表
|
||||
2. 创建fastapi接口并本地运行
|
||||
|
||||
## databases:链接数据库配置文件
|
||||
1. 链接本地mysql,实例化mysql,创建引擎,创建基类,实例化数据库会话
|
||||
2. 创建函数,调用数据库会话
|
||||
|
||||
## api.students_api:接口文件
|
||||
### 学生表students的功能接口:
|
||||
1. 学生信息的查询
|
||||
2. 学生信息的新增
|
||||
3. 学生信息的更新
|
||||
4. 学生信息的逻辑删除
|
||||
|
||||
## dao.students_dao:数据筛选文件
|
||||
### 数据库的逻辑筛选文件:
|
||||
1. 查询:get_student_dao
|
||||
2. 新增:add_student_dao
|
||||
3. 更新:update_student_dao
|
||||
4. 逻辑删除:delete_student_dao
|
||||
|
||||
## model.students_model:数据库建表文件
|
||||
1. 数据表建表代码
|
||||
2. 字段信息: sid **PK**
|
||||
, student_name
|
||||
, student_phone
|
||||
, class_name
|
||||
, job_open_time
|
||||
, offer_issue_time
|
||||
, company_name
|
||||
, salary
|
||||
, create_date
|
||||
, update_date
|
||||
, is_deleted
|
||||
, deleted_date
|
||||
, **待建FK:** class_id/teacher_id/score_id/company_id/
|
||||
|
||||
|
||||
## schema.students_request:请求体、数据过滤
|
||||
请求体根据业务要求有不同调整,方便后续维护和更改需求
|
||||
1. 查询:StudentsQuery
|
||||
2. 新增:StudentsResponse
|
||||
3. 更新:StudentsUpdate
|
||||
4. 逻辑删除:StudentsDelete
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
show databases;
|
||||
|
||||
create database student_manage_system;
|
||||
|
||||
use student_manage_system;
|
||||
|
||||
show tables;
|
||||
|
||||
drop database student_manage_system;
|
||||
|
||||
drop table students;
|
||||
|
||||
select * from students limit 50;
|
||||
|
||||
desc students;
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
# students_api所有students的api路由、接口
|
||||
|
||||
from fastapi import APIRouter , Depends , HTTPException
|
||||
from students.dao.students_dao import *
|
||||
from students.model.students_model import *
|
||||
from students.schema.students_request import *
|
||||
from students.databases import *
|
||||
|
||||
s_api = APIRouter()
|
||||
|
||||
@s_api.get("/students"
|
||||
,summary='学生资料查询'
|
||||
,description=f'筛选条件全为空即查询所有学生资料,输入薪资会查询大于输入薪资以上的学生。\n '
|
||||
f'时间格式:YYYY-MM-DD'
|
||||
,response_model=list[StudentsQuery]
|
||||
)
|
||||
def get_students( stu:StudentsQuery = Depends()
|
||||
, db=Depends(get_db)
|
||||
):
|
||||
s_dict = stu.model_dump(exclude_unset=True) # 没值的不要
|
||||
result = get_student_dao( s=s_dict , db=db )
|
||||
if result:
|
||||
return result
|
||||
raise HTTPException(status_code=404, detail="没有找到符合条件的学生")
|
||||
|
||||
|
||||
@s_api.post("/students"
|
||||
,summary='新增学生资料'
|
||||
,description=f'学生ID自动分配,学生姓名、学生手机号和班级是必填项,不能为空。\n '
|
||||
f'时间格式:YYYY-MM-DD'
|
||||
,response_model=StudentsResponse )
|
||||
def add_student( stu:StudentsResponse = Depends()
|
||||
, db=Depends(get_db)
|
||||
):
|
||||
s_dict = stu.model_dump(exclude_unset=True)
|
||||
result = add_student_dao( s=s_dict , db=db )
|
||||
if not result:
|
||||
raise HTTPException(status_code=500, detail='手机号已有学生使用或必填项为空。')
|
||||
return result
|
||||
|
||||
@s_api.put("/students"
|
||||
,summary='学生资料更新'
|
||||
,description=f'请输入需要更新信息的学生ID,学生手机号不可重复,填写即更新,为空即不更新。\n '
|
||||
f'时间格式:YYYY-MM-DD'
|
||||
,response_model=StudentsUpdate )
|
||||
def get_students( stu:StudentsUpdate = Depends()
|
||||
, db=Depends(get_db)
|
||||
):
|
||||
s_dict = stu.model_dump(exclude_unset=True)
|
||||
result = update_student_dao( s=s_dict , db=db )
|
||||
if not result:
|
||||
raise HTTPException(status_code=500, detail='手机号已有学生使用。')
|
||||
return result
|
||||
|
||||
@s_api.delete("/students"
|
||||
,summary='删除学生资料'
|
||||
,description=f'输入需要删除的学生信息,删除条件任选,但不可全为空,工资筛选能删除大于等于输入工资的所有人。\n '
|
||||
f'时间格式:YYYY-MM-DD'
|
||||
,response_model=list[StudentsDelete] ) # 逻辑删除/软删除
|
||||
def delete_students( stu:StudentsDelete = Depends()
|
||||
, db=Depends(get_db)
|
||||
):
|
||||
s_dict = stu.model_dump(exclude_unset=True)
|
||||
result = delete_student_dao(s=s_dict, db=db)
|
||||
return result
|
||||
@@ -0,0 +1,218 @@
|
||||
# student_dao:所有数据库的逻辑
|
||||
|
||||
from students.model.students_model import Students
|
||||
from students.schema.students_request import *
|
||||
from fastapi import HTTPException
|
||||
|
||||
# 查询
|
||||
def get_student_dao( s , db):
|
||||
q = db.query(Students).filter(Students.is_deleted == False)
|
||||
if s.get('id'):
|
||||
q = q.filter( Students.id == s.get("id") )
|
||||
if s.get('num'):
|
||||
q = q.filter( Students.num == s.get("num") )
|
||||
if s.get('name'):
|
||||
q = q.filter( Students.name == s.get("name") )
|
||||
if s.get('age'):
|
||||
q = q.filter( Students.age == s.get("age") )
|
||||
if s.get('sex'):
|
||||
q = q.filter( Students.sex == s.get("sex") )
|
||||
if s.get('home_Place'):
|
||||
q = q.filter( Students.home_Place == s.get("home_Place") )
|
||||
if s.get('college'):
|
||||
q = q.filter( Students.college == s.get("college") )
|
||||
if s.get('specialty'):
|
||||
q = q.filter( Students.specialty == s.get("specialty") )
|
||||
if s.get('enrollment_time'):
|
||||
q = q.filter( Students.enrollment_time == s.get("enrollment_time") )
|
||||
if s.get('graduate_time'):
|
||||
q = q.filter( Students.graduate_time == s.get("graduate_time") )
|
||||
if s.get('education'):
|
||||
q = q.filter( Students.education == s.get("education") )
|
||||
if s.get('class_id'):
|
||||
q = q.filter(Students.class_id == s.get("class_id") )
|
||||
if s.get('advisor_id'):
|
||||
q = q.filter(Students.advisor_id == s.get("advisor_id") )
|
||||
if s.get('phone'):
|
||||
q = q.filter(Students.phone == s.get("phone"))
|
||||
s1 = q.all()
|
||||
if s1:
|
||||
return [{ 'id':i.id
|
||||
, 'num':i.num
|
||||
, 'name':i.name
|
||||
, 'age':i.age
|
||||
, 'sex':i.sex
|
||||
, 'home_Place':i.home_Place
|
||||
, 'college':i.college
|
||||
, 'specialty':i.specialty
|
||||
, 'enrollment_time':i.enrollment_time
|
||||
, 'graduate_time':i.graduate_time
|
||||
, 'education':i.education
|
||||
# 外键字段
|
||||
, 'class_id':i.class_id
|
||||
, 'advisor_id':i.advisor_id
|
||||
# 额外字段
|
||||
, 'phone':i.phone
|
||||
} for i in s1
|
||||
]
|
||||
|
||||
# 新增
|
||||
def add_student_dao( s , db):
|
||||
try:
|
||||
phone = s.get("phone")
|
||||
s1_deleted = db.query( Students ).filter( Students.phone == phone ).first()
|
||||
if s1_deleted and s1_deleted.is_deleted == False:
|
||||
return False
|
||||
if not phone:
|
||||
return False
|
||||
if s1_deleted and s1_deleted.is_deleted == True:
|
||||
s1_deleted.name = s.get("name")
|
||||
s1_deleted.age = s.get("age")
|
||||
s1_deleted.sex = s.get("sex")
|
||||
s1_deleted.home_Place = s.get("home_Place")
|
||||
s1_deleted.college = s.get("college")
|
||||
s1_deleted.specialty = s.get("specialty")
|
||||
s1_deleted.enrollment_time = s.get("enrollment_time")
|
||||
s1_deleted.graduate_time = s.get("graduate_time")
|
||||
s1_deleted.education = s.get("education")
|
||||
# 外键字段
|
||||
s1_deleted.class_id = s.get("class_id")
|
||||
s1_deleted.advisor_id = s.get("advisor_id")
|
||||
# 额外字段
|
||||
s1_deleted.phone = s.get("phone")
|
||||
|
||||
s1_deleted.create_date = datetime.now()
|
||||
s1_deleted.update_date = datetime.now()
|
||||
s1_deleted.is_deleted = False
|
||||
s1_deleted.deleted_date = None
|
||||
db.commit()
|
||||
db.refresh(s1_deleted) # 从数据库再拉一次数据
|
||||
return s1_deleted
|
||||
s1 = Students( **s )
|
||||
db.add(s1)
|
||||
except Exception:
|
||||
# 这里可以加日志
|
||||
db.rollback()
|
||||
return False
|
||||
else:
|
||||
db.commit()
|
||||
db.refresh(s1)
|
||||
return s1
|
||||
|
||||
# 更新
|
||||
def update_student_dao( s , db):
|
||||
try:
|
||||
phone = s.get("phone")
|
||||
if phone is not None and phone != '':
|
||||
s1_con = db.query( Students ).filter( Students.phone == phone
|
||||
,Students.id != s.get("id")
|
||||
,Students.is_deleted == False ).first()
|
||||
if s1_con:
|
||||
raise HTTPException(status_code=409, detail=f'手机号已被其他学生使用:{phone}')
|
||||
s1_id = db.query( Students ).filter( Students.id == s.get("id")
|
||||
,Students.is_deleted == False ).first()
|
||||
if not s1_id:
|
||||
raise HTTPException(status_code=404, detail=f'学生不存在:id={s.get("id")}')
|
||||
if s.get("num") is not None:
|
||||
s1_id.name = s.get("num")
|
||||
if s.get("name") is not None:
|
||||
s1_id.name = s.get("name")
|
||||
if s.get("age") is not None:
|
||||
s1_id.age = s.get("age")
|
||||
if s.get("sex") is not None:
|
||||
s1_id.sex = s.get("sex")
|
||||
if s.get("home_Place") is not None:
|
||||
s1_id.home_Place = s.get("home_Place")
|
||||
if s.get("college") is not None:
|
||||
s1_id.college = s.get("college")
|
||||
if s.get("specialty") is not None:
|
||||
s1_id.specialty = s.get("specialty")
|
||||
if s.get("enrollment_time") is not None:
|
||||
s1_id.enrollment_time = s.get("enrollment_time")
|
||||
if s.get("graduate_time") is not None:
|
||||
s1_id.graduate_time = s.get("graduate_time")
|
||||
if s.get("education") is not None:
|
||||
s1_id.education = s.get("education")
|
||||
# 外键字段
|
||||
if s.get("class_id") is not None:
|
||||
s1_id.class_id = s.get("class_id")
|
||||
if s.get("advisor_id") is not None:
|
||||
s1_id.advisor_id = s.get("advisor_id")
|
||||
# 额外字段
|
||||
if s.get("phone") is not None:
|
||||
s1_id.student_phone = s.get("phone")
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception:
|
||||
db.rollback()
|
||||
raise
|
||||
else:
|
||||
s1_id.update_date = datetime.now()
|
||||
db.commit()
|
||||
db.refresh(s1_id) # 从数据库再拉一次数据
|
||||
return s1_id
|
||||
|
||||
|
||||
# 逻辑删除/软删除
|
||||
def delete_student_dao( s , db):
|
||||
try:
|
||||
if not any([ s.get("id")
|
||||
,s.get("num")
|
||||
,s.get("name")
|
||||
,s.get("age")
|
||||
,s.get("sex")
|
||||
,s.get("home_Place")
|
||||
,s.get("college")
|
||||
,s.get("specialty")
|
||||
,s.get("enrollment_time")
|
||||
,s.get("graduate_time")
|
||||
,s.get("education")
|
||||
,s.get("class_id")
|
||||
,s.get("advisor_id")
|
||||
,s.get("phone")
|
||||
]):
|
||||
raise HTTPException(status_code=400, detail="输入需要删除的学生信息,删除条件任选,但不可全为空。")
|
||||
q = db.query(Students).filter(Students.is_deleted == False)
|
||||
if s.get('id'):
|
||||
q = q.filter(Students.id == s.get("id") )
|
||||
if s.get('num'):
|
||||
q = q.filter(Students.num == s.get("num"))
|
||||
if s.get('name'):
|
||||
q = q.filter(Students.name == s.get("name") )
|
||||
if s.get('age'):
|
||||
q = q.filter(Students.age == s.get("age") )
|
||||
if s.get('sex'):
|
||||
q = q.filter(Students.sex == s.get("sex"))
|
||||
if s.get('home_Place'):
|
||||
q = q.filter(Students.home_Place == s.get("home_Place") )
|
||||
if s.get('college'):
|
||||
q = q.filter(Students.college == s.get("college") )
|
||||
if s.get('specialty'):
|
||||
q = q.filter(Students.specialty == s.get("specialty"))
|
||||
if s.get('enrollment_time'):
|
||||
q = q.filter(Students.enrollment_time == s.get("enrollment_time") )
|
||||
if s.get('graduate_time'):
|
||||
q = q.filter(Students.graduate_time == s.get("graduate_time"))
|
||||
if s.get('education'):
|
||||
q = q.filter(Students.education == s.get("education") )
|
||||
if s.get('class_id'):
|
||||
q = q.filter(Students.class_id == s.get("class_id") )
|
||||
if s.get('advisor_id'):
|
||||
q = q.filter(Students.advisor_id == s.get("v") )
|
||||
if s.get("phone"):
|
||||
q = q.filter(Students.phone == s.get("phone") )
|
||||
s1_delete = q.all()
|
||||
if not s1_delete:
|
||||
raise HTTPException(status_code=404, detail="没有找到该学生。")
|
||||
for i in s1_delete:
|
||||
i.is_deleted = True
|
||||
i.deleted_date = datetime.now()
|
||||
# s2_deleted =[{"id":i.id,"name":i.name,"phone":i.phone}for i in s1_delete]
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception:
|
||||
db.rollback()
|
||||
raise HTTPException(status_code=500, detail="Internal server error")
|
||||
else:
|
||||
db.commit()
|
||||
return s1_delete
|
||||
@@ -0,0 +1,35 @@
|
||||
# databases链接数据库
|
||||
|
||||
from sqlalchemy import *
|
||||
from sqlalchemy.orm import sessionmaker,declarative_base
|
||||
|
||||
# 导入.env参数
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv( )
|
||||
DB_USER = os.getenv("DB_USER", "root")
|
||||
DB_PASSWORD = os.getenv("DB_PASSWORD", "")
|
||||
DB_HOST = os.getenv("DB_HOST", "localhost")
|
||||
DB_PORT = os.getenv("DB_PORT", "3306")
|
||||
DB_NAME = os.getenv("DB_NAME", "student_manage_system")
|
||||
|
||||
db_url = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}?charset=utf8mb4"
|
||||
|
||||
engine1 = create_engine(db_url)
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
Session = sessionmaker(bind=engine1
|
||||
,autoflush=False
|
||||
,autocommit=False)
|
||||
|
||||
def get_db():
|
||||
db = Session()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
from fastapi import FastAPI,APIRouter
|
||||
from students.api.students_api import s_api
|
||||
from students.databases import *
|
||||
from students.model import students_model
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
# 合并接口
|
||||
Students_API = APIRouter()
|
||||
Students_API.include_router(s_api)
|
||||
|
||||
# 自测接口
|
||||
@asynccontextmanager
|
||||
async def S_Fastapi(app: FastAPI):
|
||||
Base.metadata.create_all(engine1)
|
||||
yield
|
||||
engine1.dispose()
|
||||
|
||||
app = FastAPI(title="学生管理系统",lifespan=S_Fastapi) # 这里必须把上面写的加上
|
||||
app.include_router(s_api,tags=["students"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
def main():
|
||||
print("Hello from student-manage-system!")
|
||||
|
||||
import uvicorn
|
||||
uvicorn.run("main:app", host="0.0.0.0", port=51000)
|
||||
|
||||
"""students api
|
||||
IPv4 地址 . . . . . . . . . . . . : 192.168.6.40
|
||||
默认网关. . . . . . . . . . . . . : 192.168.6.1
|
||||
"""
|
||||
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
# students_model处理数据库模型
|
||||
import datetime
|
||||
|
||||
from databases import engine_all,Base_all,Session
|
||||
from sqlalchemy import *
|
||||
# from students.databases import *
|
||||
from datetime import datetime
|
||||
from sqlalchemy.dialects.mysql import DATETIME
|
||||
from students.schema.students_request import SexEnum , EducationEnum
|
||||
from sqlalchemy import Enum,ForeignKey,Integer,String
|
||||
|
||||
class Students(Base_all):
|
||||
__tablename__ = 'students'
|
||||
id = Column(Integer
|
||||
, primary_key=True
|
||||
, autoincrement=True
|
||||
, comment='学生ID,自增主键'
|
||||
)
|
||||
num = Column(String(50)
|
||||
, nullable=False
|
||||
, unique=True
|
||||
, comment='学生编号'
|
||||
)
|
||||
|
||||
name = Column(String(50)
|
||||
, comment='学生姓名'
|
||||
, nullable=False )
|
||||
|
||||
age = Column(Integer
|
||||
, comment='年龄'
|
||||
, nullable=False)
|
||||
|
||||
sex = Column(Enum(SexEnum, values_callable=lambda e: [i.value for i in e])
|
||||
, comment='性别'
|
||||
, nullable=False)
|
||||
|
||||
home_Place = Column(String(50)
|
||||
, comment='籍贯')
|
||||
|
||||
college = Column(String(50)
|
||||
, comment='毕业院校')
|
||||
|
||||
specialty = Column(String(50)
|
||||
, comment='专业')
|
||||
|
||||
enrollment_time = Column(DATETIME
|
||||
, comment='入学时间')
|
||||
|
||||
graduate_time = Column(DATETIME
|
||||
, comment='毕业时间')
|
||||
|
||||
education = Column(Enum(EducationEnum, values_callable=lambda e: [i.value for i in e])
|
||||
, comment='学历'
|
||||
, nullable=True)
|
||||
|
||||
# 外键字段
|
||||
class_id = Column(Integer
|
||||
, ForeignKey("classes.id")
|
||||
, comment='学生班级'
|
||||
, nullable=False )
|
||||
|
||||
advisor_id = Column(Integer
|
||||
, ForeignKey("teachers.id")
|
||||
, comment='顾问ID')
|
||||
|
||||
# 额外字段
|
||||
phone = Column(String(11)
|
||||
, comment='学生手机号'
|
||||
, nullable=False
|
||||
, unique=True )
|
||||
|
||||
# 通用字段
|
||||
create_date = Column(DATETIME(fsp=6)
|
||||
, default=datetime.now
|
||||
, comment='创建时间' )
|
||||
|
||||
update_date = Column(DATETIME(fsp=6)
|
||||
, default=datetime.now
|
||||
, comment='更新时间' )
|
||||
|
||||
is_deleted = Column(INTEGER
|
||||
, default=False
|
||||
, comment='是否删除:0-正常,1-已删除' )
|
||||
|
||||
deleted_date = Column(DATETIME(fsp=6)
|
||||
, default=None
|
||||
, comment='删除时间' )
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
# students_schema:请求、响应的模型框架
|
||||
|
||||
from pydantic import BaseModel , Field , field_validator
|
||||
from datetime import datetime
|
||||
import enum
|
||||
|
||||
class SexEnum(enum.Enum):
|
||||
m = '男'
|
||||
f = '女'
|
||||
|
||||
# 查询
|
||||
class StudentsQuery(BaseModel):
|
||||
id:int | None = None
|
||||
num:str | None = None
|
||||
name:str | None = None
|
||||
age:int | None = None
|
||||
sex:SexEnum | None = None
|
||||
home_Place:str | None = None
|
||||
college:str | None = None
|
||||
specialty:str | None = None
|
||||
enrollment_time:datetime | None = None
|
||||
graduate_time:datetime | None = None
|
||||
education:str | None = None
|
||||
# 外键字段
|
||||
class_id:int | None = None
|
||||
advisor_id:int | None = None
|
||||
# 额外字段
|
||||
phone:str | None = None
|
||||
|
||||
class EducationEnum(enum.Enum):
|
||||
z = '专科'
|
||||
ben = '本科'
|
||||
s = '硕士'
|
||||
bo = '博士'
|
||||
|
||||
# 新建
|
||||
class StudentsResponse(BaseModel):
|
||||
num:str = Field(..., min_length=1, max_length=50, description='学生编号' )
|
||||
name:str = Field(..., min_length=1, max_length=50, description='学生姓名' )
|
||||
age:int = Field(..., gt=0, lt=200, description='学生年龄')
|
||||
sex:SexEnum
|
||||
home_Place:str | None = None
|
||||
college:str | None = None
|
||||
specialty:str | None = None
|
||||
enrollment_time:datetime | None = None
|
||||
graduate_time:datetime | None = None
|
||||
education:EducationEnum
|
||||
# 外键字段
|
||||
class_id:int = Field(..., gt=0, description='班级ID')
|
||||
advisor_id:int | None = None
|
||||
# 额外字段
|
||||
phone:str = Field(...,min_length=11,max_length=11,description='学生手机号')
|
||||
|
||||
# 更新
|
||||
class StudentsUpdate(BaseModel):
|
||||
id:int = Field(..., gt=0 , description='学生ID' )
|
||||
num:str | None = None
|
||||
name:str | None = None
|
||||
age:int | None = None
|
||||
sex:SexEnum | None = None
|
||||
home_Place:str | None = None
|
||||
college:str | None = None
|
||||
specialty:str | None = None
|
||||
enrollment_time:datetime | None = None
|
||||
graduate_time:datetime | None = None
|
||||
education:str | None = None
|
||||
# 外键字段
|
||||
class_id:int | None = None
|
||||
advisor_id:int | None = None
|
||||
# 额外字段
|
||||
phone:str | None = None
|
||||
|
||||
# 删除
|
||||
class StudentsDelete(BaseModel):
|
||||
id:int | None = None
|
||||
num:str | None = None
|
||||
name:str | None = None
|
||||
age:int | None = None
|
||||
sex:SexEnum | None = None
|
||||
home_Place:str | None = None
|
||||
college:str | None = None
|
||||
specialty:str | None = None
|
||||
enrollment_time:datetime | None = None
|
||||
graduate_time:datetime | None = None
|
||||
education:str | None = None
|
||||
# 外键字段
|
||||
class_id:int | None = None
|
||||
advisor_id:int | None = None
|
||||
# 额外字段
|
||||
phone:str | None = None
|
||||
Reference in New Issue
Block a user