Compare commits

Author SHA1 Message Date
Anhoa db3983f174 增加多字段查询 2026-09-21 19:33:53 +08:00
Anhoa 5344109ff7 基础框架搭建完成,外键未连接 2026-09-21 14:17:12 +08:00
Orangeeee 5a0fa9e655 框架更新3 2026-09-19 17:13:22 +08:00
8 changed files with 526 additions and 12 deletions
+1 -5
View File
@@ -2,11 +2,7 @@
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="E:\anaconda3\envs\newai0824" jdkType="Python SDK" />
<orderEntry type="jdk" jdkName="Python 3.10" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PackageRequirementsSettings" />
<component name="PyDocumentationSettings" />
<component name="ReSTService" />
<component name="TestRunnerService" />
</module>
+246 -7
View File
@@ -5,9 +5,9 @@
# 每个人创建自己的分支
1. 每个表里都要有创建时间、更新时间、逻辑删除字段
---
好嘞,这份需求文档我帮你梳理清楚 + 出技术文档 + 拆6人分工,直接能拿去团队开会用。
# 版本一
一、需求整理(去冗余、补逻辑)
核心实体关系(ER 简版)
@@ -254,10 +254,249 @@ git checkout -b feature/migration-test # F
5. 数据库操作全部走 crud 层,api 层不直接写 SQL
6. 统计接口复杂查询放 services 层,crud 只做简单读写
四、给你的额外建议
# 方案二:
这份需求文档涉及 学生管理、成绩、就业、班级、老师、统计分析 6 大模块,加上数据库设计和后续合并测试,正好适合 6 人小组 按“模块 + 公共基础”的方式分工。下面给你一套可直接落地的分工方案,兼顾开发效率、代码解耦和答辩覆盖度。
1. 今天就能做的事:A(组长)按上面的目录结构建好项目,pip install -r requirements.txt 跑通,push 到 Git,其他人 clone 下来直接开干。
2. AI依赖(ollama/openai):需求里没说怎么用,如果老师要求用 AI 功能,可以加一个"智能查询"接口——用自然语言问"帮我找一下Java班成绩最好的学生",后端调 AI 转成 SQL 执行。
3. 冗余字段同步:学生改了姓名/班级,就业表的冗余字段要跟着改,这个逻辑放 crud/student.py 的 update 方法里,记得提醒 D 同学配合。
一、推荐分工方案(6 人)
需要我帮你把某个模块(比如学生管理)的 models + schemas + crud + api 完整代码骨架 写出来,你们直接往里填逻辑吗?
✅ 原则
• 每人负责 1 个独立模块
• 1 人负责 公共基础 + 数据库 + 项目骨架
• 每人独立分支,最后统一合并
• 答辩时每人都能讲“自己写的代码 + 涉及的设计”
👥 人员分工表
成员 负责模块 核心职责
A(组长/架构) 公共基础 + 数据库设计 项目初始化、数据库建模、BaseModel、异常、依赖、路由聚合、合并分支
B 学生基本信息管理 学生 CRUD、逻辑删除、查询筛选
C 成绩管理模块 成绩录入、修改、删除、学生成绩查询
D 就业管理模块 就业信息增删改查、冗余字段设计
E 班级 & 老师管理模块 班级、老师信息管理、带班关系
F 统计分析模块 所有统计接口(成绩/就业/基本信息统计)
二、每人详细职责说明
👤 A:公共基础 + 数据库设计(非常关键)
职责:
• MySQL 数据库设计(ER 图、表结构)
• FastAPI 项目骨架搭建
• 公共依赖(数据库 session、分页、权限预留)
• Pydantic 基础模型
• 统一异常处理
• 路由汇总(include_router)
• Git 分支管理与最终合并
产出:
• models/
• schemas/
• database.py
• dependencies.py
• exceptions.py
• main.py
📌 建议分支:
dev/base
👤 B:学生基本信息管理
职责:
• 学生表设计建议
• 学生增删改查
• 逻辑删除
• 条件查询(编号/姓名/班级)
API 示例:
• GET /students
• POST /students
• GET /students/{id}
• PUT /students/{id}
📌 分支:
dev/student
👤 C:成绩管理模块
职责:
• 成绩表设计
• 成绩录入、修改、删除
• 单学生成绩查询
API 示例:
• POST /score/
• GET /score/{stu_id}
• PUT /score/update
• POST /score/delete
📌 分支:
dev/score
👤 D:就业管理模块
职责:
• 就业表设计(含冗余字段)
• 就业信息新增 / 更新
• 多条件查询(公司、薪资范围)
API 示例:
• GET /employment/students/{id}
• POST /employment/students/{id}
📌 分支:
dev/employment
👤 E:班级 & 老师管理模块
职责:
• 班级表、老师表
• 班级 CRUD
• 老师 CRUD
• 带班关系设计
API 示例:
• GET /classes
• POST /classes
• GET /teachers
📌 分支:
dev/class-teacher
👤 F:统计分析模块
职责:
• 所有统计 SQL / ORM 查询
• 成绩统计
• 就业统计
• 基本信息统计
API 示例:
• GET /statistics/xxx
📌 分支:
dev/statistics
三、Git 分支与合并策略(强烈建议)
分支结构
├── dev/base (A)
├── dev/student (B)
├── dev/score (C)
├── dev/employment (D)
├── dev/class-teacher (E)
└── dev/statistics (F)
合并流程
1. A 先完成 dev/base
2. 其他人从 dev/base 拉自己的分支
3. 每人开发完成后 → PR 到 dev/base
4. A 负责 code review + 合并
5. 最后合并到 main
四、数据库设计建议(给 A 的提示)
核心表
• student
• score
• employment
• class
• teacher
• class_teacher(多对多)
冗余字段处理
• 就业表中:
• student_name
• class_name
• 插入/更新时同步,查询时直接读
五、答辩 & 抽查应对策略
每人必须能讲清楚
• 自己负责模块的:
• 表结构
• API 设计
• 关键代码逻辑
• 为什么这样设计(尤其是统计 SQL)
组长额外准备
• 项目整体架构
• 数据库 ER 图
• 分支管理
• 合并冲突解决
+75
View File
@@ -0,0 +1,75 @@
from datetime import date
from fastapi import APIRouter,Depends,HTTPException,Query
from dao.class_management_dao import add_class_dao, update_class_dao, get_class_dao, delete_class_dao
from database import get_db
from schema.class_management_request import ClassCreate, ClassResponse, ClassUpdate
class_api = APIRouter(tags=['班级管理模块'])
@class_api.post("/classes",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('/classes/{class_id}',summary='根据id查询班级')
def get_one_class(class_id:int,db=Depends(get_db)):
res = get_class_dao(c={'class_id':class_id},db=db)
if not res:
raise HTTPException(status_code=404, detail="该班级不存在或已删除")
return ClassResponse(data=res)
@class_api.get('/classes',summary='多字段查询班级')
def get_some_classes(class_name:str|None = Query(None,description='班级名称')
,class_teacher:str|None = Query(None,description='班主任')
,class_lecturer:str|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={'class_name':class_name
,'class_teacher':class_teacher
,'class_lecturer':class_lecturer
,'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]
page_response = ClassResponse(data=page_data,totals=total)
return page_response
@class_api.put("/classes/{class_id}",summary='修改班级信息')
def update_class(class_id,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": class_id})
@class_api.delete('/classes/{class_id}',summary='逻辑删除班级')
def remove_class(class_id:int,db=Depends(get_db)):
del_rows = delete_class_dao(class_id,db)
if del_rows <= 0:
raise HTTPException(status_code=404, detail="班级不存在,删除失败")
return ClassResponse(detail='删除成功',data={'class_id':class_id})
+81
View File
@@ -0,0 +1,81 @@
from 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('class_id'):
q = q.filter(ClassInfo.class_id == c.get('class_id'))
if c.get('class_name'):
q = q.filter(ClassInfo.class_name == c.get('class_name'))
if c.get('class_teacher'):
q = q.filter(ClassInfo.class_teacher == c.get('class_teacher'))
if c.get('class_lecturer'):
q = q.filter(ClassInfo.class_lecturer == c.get('class_lecturer'))
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'))
c1 = q.all()
if not c1:
return []
return[
{'班级编号':i.class_id
,'班级名称':i.class_name
,'班主任':i.class_teacher
,'授课老师':i.class_lecturer
,'开课时间':i.class_start_time
,'结课时间':i.class_end_time
} for i in c1
]
#新增
def add_class_dao(c:dict,db:Session):
try:
exist_class = db.query(ClassInfo)\
.filter(ClassInfo.class_name == c['class_name']
,ClassInfo.is_delete == 0)\
.first()
if exist_class:
msg = '班级名称已存在'
print(msg)
return [False,msg]
c1 = ClassInfo(**c)
db.add(c1)
except:
db.rollback()
return False
else:
db.commit()
return True
#修改
def update_class_dao(cid:int,update_data,db):
try:
rows = db.query(ClassInfo)\
.filter(ClassInfo.class_id == cid
,ClassInfo.is_delete==0)\
.update(update_data)
except:
db.rollback()
return False
else:
db.commit()
return rows
#删除
def delete_class_dao(cid,db):
try:
rows = db.query(ClassInfo)\
.filter(ClassInfo.class_id == cid,ClassInfo.is_delete==0)\
.update({'is_delete':1})
except:
db.rollback()
rows = 0
else:
db.commit()
finally:
return rows
+17
View File
@@ -0,0 +1,17 @@
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker,declarative_base
class_management_rul = "mysql+pymysql://root:123456@localhost:3306/ai0824_cyf?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()
+14
View File
@@ -0,0 +1,14 @@
from fastapi import FastAPI,APIRouter
from api.class_management_api import class_api
from model.class_management_model import ClassInfo
from database import Base,engine
import model
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)
+50
View File
@@ -0,0 +1,50 @@
from sqlalchemy import Column,Integer,String,Date,Boolean
from sqlalchemy.dialects.mysql import DATETIME
from datetime import datetime
from database import Base
class ClassInfo(Base):
__tablename__ = 'class_info'
class_id = Column(Integer
,primary_key=True
,autoincrement=True
,comment='班级id主键'
)
class_name = Column(String(10)
,nullable=False
,comment='班级名称'
)
class_teacher = Column(String(50)
,nullable=False
,comment='班主任'
)
class_lecturer = Column(String(50)
,nullable=False
,comment='授课老师'
)
class_start_time = Column(Date
,nullable=False
,comment='开班日期'
)
class_end_time = Column(Date
,nullable=False
,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='记录更新时间'
)
+42
View File
@@ -0,0 +1,42 @@
from pydantic import BaseModel,Field
from datetime import date,datetime
from typing import Optional,List
#基础班级请求体
class ClassBase(BaseModel):
class_teacher:str|None = Field(None,description='班主任')
class_lecturer:str|None = Field(None,description='授课老师')
class_start_time:date|None = Field(None,description='开班日期')
class_end_time:date|None = Field(None,description='结课日期')
#新增班级请求体模型
class ClassCreate(ClassBase):
class_name:str = Field(..., description='班级名称')
#修改班级请求体模型
class ClassUpdate(ClassBase):
class_name:str = Field(None,description='班级名称')
class ClassInfoResp(BaseModel):
class_id:int
class_name:str
class_teacher:str
class_lecturer:str
class_start_time:date
class_end_time:date
create_time:datetime
update_time:datetime
class Config:
from_attributes = True
class ClassResponse(BaseModel):
code:int = 200
detail:str = 'OK'
totals:int = 0
data:str|dict|tuple|list
msg:str = 'OK'