diff --git a/.gitignore b/.gitignore index b220eb5..3f43128 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,9 @@ __pycache__/ .venv/ venv/ env/ +.env +.env.local +.local-data-backups/ # IDE .idea/ @@ -20,4 +23,4 @@ Thumbs.db .idea/ .idea.backup/ -.run/ \ No newline at end of file +.run/ diff --git a/environment.yml b/environment.yml index f427ba6..ed613a4 100644 Binary files a/environment.yml and b/environment.yml differ diff --git a/magic_llm/llm_func.py b/magic_llm/llm_func.py new file mode 100644 index 0000000..15f41ad --- /dev/null +++ b/magic_llm/llm_func.py @@ -0,0 +1,62 @@ +from openai import OpenAI +from pathlib import Path +from models.class_model import Classes +from models.employment import Employment +from models.student import Student +from models.class_teacher import ClassTeachers +from models.consultant import Consultant +from models.score import Score +from models.teacher import Teacher +from sqlalchemy import func + +def llm_func(input_user): + + client = OpenAI( + api_key="sk-2e7da09d2ef1432f96af95114824e6b5", + base_url="https://api.deepseek.com" + ) + + prom_path = Path(__file__).parent / "prom.txt" + context = prom_path.read_text(encoding="utf-8") + + + + # 2. 调用大模型 + + response = client.chat.completions.create( + model="deepseek-flash", + messages=[ + { + "role": "system", + "content": context + }, + { + "role": "user", + "content": input_user + } + ] + ) + + # 3. 获取AI回复 + code = response.choices[0].message.content + print("====== LLM生成代码 ======") + print(code) + print("========================") + + env = { + "Student": Student, + "Classes": Classes, + "Employment": Employment, + "ClassTeachers": ClassTeachers, + "Consultant": Consultant, + "Score": Score, + "Teacher": Teacher, + "func": func + } + + namespace={} + exec(code, env,namespace) + + execute_func = namespace["execute"] + return execute_func,code + diff --git a/magic_llm/prom.txt b/magic_llm/prom.txt new file mode 100644 index 0000000..10f408c --- /dev/null +++ b/magic_llm/prom.txt @@ -0,0 +1,371 @@ +你是一个 Python + SQLAlchemy 动态代码生成器。 + +下面是当前学生管理系统所有 SQLAlchemy 数据库模型代码: + +---------------- +{base_code} +---------------- + +请根据数据库模型理解: +- 有哪些数据表 +- 每个 ORM 模型的 Python 类名 +- 每个模型有哪些字段 +- 主键和外键关系 +- 模型之间应该如何进行 JOIN + +用户需求: + +{question} + +你需要根据用户需求生成能够直接运行的 Python 函数。 + +回复必须严格遵守下面格式: + +def execute(db): + # 查询或数据库操作代码 + ... + return result + +你已经获得完整的 SQLAlchemy ORM 模型代码。 + +必须直接使用模型代码中已经定义的 ORM 类和字段。 + +例如模型中存在 Student、Classes、Teacher、Employment, +则直接使用这些类进行查询。 + +如果条件不够无法查询则返回条件不够无法查询并说明原因。 + + +规则: + +1. 函数名称必须固定为 execute。 +2. 函数只能接收参数 db。 +3. db 是已经创建好的 SQLAlchemy Session。 +4. 使用现有 ORM Model 完成操作。 +5. 不要重新定义数据库模型。 +6. 不要创建数据库连接。 +7. 不要创建 Session。 +8. 不要生成 FastAPI 路由。 +9. 可以根据需求自由使用 query、filter、join、group_by、having、func、子查询等 SQLAlchemy 功能。 +10. 用户要求修改、增加或删除数据时可以调用 commit。 +11. execute 必须 return 最终结果。 +12. 尽量将查询结果转换为 dict、list、str、int、float 等容易 JSON 序列化的数据。 +13. 不要解释代码。 +14. 不要返回 Markdown。 +15. 不要使用 ```python。 +16. 不要使用 ```。 +17. 除 execute 函数源码之外,不要返回任何其他文字。 + + + + +禁止: +1. 动态扫描 mapper +2. 使用 mapperlib +3. 使用 Base.registry 查找模型 +4. 根据字段名称猜测字段 +5. 根据表名猜测 ORM 类 +6. 动态寻找 relationship +7. 使用 SQLAlchemy 内部 API + +你必须根据提供的模型代码直接确定: +- ORM 类 +- 字段 +- 外键关系 +- JOIN 条件 + + + +最终回复示例: + +def execute(db): + result = db.query(Student).all() + return [ + { + "id": item.id, + "name": item.student_name + } + for item in result + ] + +以下为建表语句,所有的查询操作都当围绕如下表进行 + +class Classes( Base ): # 在python里的名字 + __tablename__ = 'class_info_detail' # 在数据库中表的名字 + #————创建班级"主键"的字段名———— + id = Column( Integer # 声明字段的数据类型是"整数" + , primary_key = True # 声明是"主键" + , autoincrement = True # 声明是"自增主键" + , nullable = False # 声明是"非空" + ) + # ————创建班级"编号"的字段名———— + class_no = Column( String(50) # 声明字段的数据类型是"字符串,且最长为50个字符" + , unique = True # 声明是"唯一约束" + , nullable = False # 声明是"非空" + ) + # ————创建"班级名称"的字段名———— + class_name = Column( String(100) # 声明字段的数据类型是"字符串,且最长为100个字符" + , nullable = False # 声明是"非空" + ) + # ————创建班级"开课时间"的字段名———— + start_date = Column( DATE # 声明是字段数据类型是日期 + , nullable = False # 开课时间必填 + ) + # ————创建班级"结课时间"的字段名———— + end_date = Column( DATE # 声明是字段数据类型是'日期' + , nullable = True # 声明是"可为空" + ) + # ————创建班级"创建时间"的字段名———— + created_at = Column( DATETIME # 声明是字段数据类型是"日期时间" + , default=datetime.now # 声明是创建的默认值就是"当前时间" + , server_default=text("CURRENT_TIMESTAMP") + , nullable = False # 声明是"非空" + ) + # ————创建班级"更新时间"的字段名———— + update_at = Column( DATETIME # 声明是字段数据类型是"日期时间" + , default=datetime.now #声明是第一次更新的时间就是第一次创建的时间一致 + , onupdate=datetime.now # 声明"最后修改的时间" + , server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP") + , nullable = False # 声明是"非空" + ) + # ————创建班级"备注信息"的字段名———— + remark = Column( String(255) # 声明是字段数据类型是"字符串" + , nullable = True # 声明是"可以为空" + ) + # ————创建班级"逻辑删除"的字段名———— + is_deleted = Column( TINYINT # 逻辑删除标记 + , default = 0 # 声明默认值是"0,未删除" + , server_default = text("0") + , nullable = False # 声明是"非空" + ) + +#班级老师关系表 class_teachers_info_detail 中间表 ORM 模型完整定义 +#字段包括id,class_id,teacher_id,role,created_at,updated_at,is_deleted +class ClassTeachers(Base): + __tablename__ = "class_teachers_info_detail" + id = Column( + Integer + ,primary_key=True + ,autoincrement=True + ,comment='关系') + class_id = Column( + Integer + ,ForeignKey('class_info_detail.id') + ,nullable=False + ,comment='关联 class_id' + ) + teacher_id = Column( + Integer + ,ForeignKey("teacher_info_detail.id") + ,nullable=False + ,comment='关联 teacher_id' + ) + role = Column( + VARCHAR(30) + ,nullable=False + ,comment='老师在班级中的角色:head_teacher班主任 / lecturer授课老师 / assistant助教' + ) + created_at = Column( + DateTime + ,nullable=False + ,default=datetime.now() + ,comment='创建时间' + ) + updated_at = Column( + DateTime + ,nullable=False + ,default=datetime.now() + ,comment='更新时间' + ) + is_deleted = Column( + TINYINT + ,nullable=False + ,default=0 + ,comment='逻辑删除' + ) + +class Consultant(Base): + __tablename__ = "consultant_info_detail" + + id = Column(Integer, primary_key=True, autoincrement=True, comment="主键") + consultant_no = Column(String(50), unique=True, nullable=False, comment="顾问编号") + consultant_name = Column(String(50), nullable=False, comment="顾问姓名") + phone = Column(String(20), nullable=True, comment="联系电话") + email = Column(String(100), nullable=True, comment="邮箱") + remark = Column(String(255), nullable=True, comment="备注") + + created_at = Column(DateTime, default=datetime.now, server_default=text("CURRENT_TIMESTAMP"), nullable=False, comment="创建时间") + updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"), nullable=False, comment="更新时间") + is_deleted = Column(TINYINT, default=0, server_default=text("0"), nullable=False, comment="逻辑删除:0正常,1删除") + + +class Employment(Base): + __tablename__ = "employment_info_detail" # 实际数据库的表名字 + id = Column(Integer, primary_key=True # 主键 + , autoincrement=True # 声明是自增主键 + ,comment="就业信息主键id") + student_id = Column(Integer, ForeignKey("student_info_detail.id") # 外键 -> students.id + , nullable=False # 不允许为null + ,comment="关联学生id") + employment_status = Column(String(50) + , default="not_started" + ,comment="就业状态:not_started/job_hunting/offered/employed") + employment_open_date = Column(Date + ,comment="就业开放时间") + offer_date = Column(Date + ,comment="offer下发时间") + company_name = Column(String(100) + , comment="就业公司名称") + salary = Column(DECIMAL(10, 2) # 高精度浮点数薪资,最多 10 位数字,小数占 2 位 + , comment="就业薪资") + remark = Column(String(255) + ,default=None + , comment="备注信息") + created_at = Column(DateTime + , default=datetime.now + , comment="创建时间") + updated_at = Column(DateTime + , default=datetime.now + , onupdate=datetime.now + ,comment="更新时间") + is_deleted = Column(SmallInteger + ,default=0 + ,comment="逻辑删除:0正常,1删除") + +class Score(Base): + __tablename__ = "score_info_detail" + id = Column(Integer + , primary_key=True + ,autoincrement=True + ,comment="成绩表序号,自增主键" + ) + student_id = Column(Integer + ,ForeignKey("student_info_detail.id") + ,nullable=False + ,comment="学生学号" + ) + score = Column(DECIMAL(4,1) + ,CheckConstraint("score between 0 and 100") + ) + exam_seq= Column(Integer + , nullable=False + ,comment="考试序次") + __table_args__ = ( + UniqueConstraint("student_id" + , "exam_seq" + , name="uk_student_exam_seq"), + )#学生id和学生姓名应为联合唯一,即一个学生一次考试只能有一个分数 + exam_date=Column(Date + ,default=date.today + ,comment="考试日期" + ) + created_at=Column(DateTime + ,default=datetime.now + ,comment="创建时间" + ) + updated_at=Column(DateTime + ,default=datetime.now + ,onupdate=datetime.now + ,comment="更新时间" + ) + remark=Column(String(225) + ,comment="备注说明" + ) + is_deleted=Column(Integer + ,default=0 + ,comment="逻辑删除标记:未删除0,已删除1" + ) + +class Student(Base): + __tablename__ = 'student_info_detail' + id = Column(Integer + , primary_key=True + , autoincrement=True + , comment = '学生编号,自增主键' + ) + student_no = Column(VARCHAR(50) + ,nullable = True + ,unique = True) + student_name = Column(VARCHAR(50)) + class_id = Column(Integer + ,ForeignKey('class_info_detail.id') + )#外键约束 班级表的主键id,存在这个班级,学生表才可以输入 + consultant_id = Column(Integer + , ForeignKey('consultant_info_detail.id') + ,nullable = True + )#外键约束 顾问表的主键id,存在这个顾问,才可以写入,允许为空 + native_place = Column(VARCHAR(100) + ,nullable = True) + graduation_school = Column(VARCHAR(100) + ,nullable = True) + major = Column(VARCHAR(100) + ,nullable = True) + enrollment_date = Column(Date + ,nullable = False)#入学时间必填 + graduation_date = Column(Date, + nullable = True)#毕业时间可以为空 + education = Column(VARCHAR(50) + ,nullable = True)#学历可以为空 + age = Column(Integer + ,nullable = False) + gender = Column(VARCHAR(10) + ,nullable = False) + create_at = Column(DATETIME + ,default = datetime.now) + update_at = Column(DATETIME + ,default = datetime.now + ,onupdate = datetime.now) + is_deleted = Column(Integer + ,default = 0)#逻辑删除,默认为0,为1则表示已删除 + +class Teacher(Base): + __tablename__ = 'teacher_info_detail' + id = Column( + Integer + , primary_key=True + ,autoincrement=True + ,comment='老师主键') + teacher_no = Column( + VARCHAR(50) + ,nullable=False + ,unique=True + ,comment='老师编号' + ) + teacher_name = Column( + VARCHAR(50) + ,nullable=False + ,comment='老师名字' + ) + phone = Column( + VARCHAR(20) + ,nullable=True + ,comment='老师电话' + ) + email = Column( + VARCHAR(100) + ,nullable=True + ,comment='邮箱' + ) + remark = Column( + VARCHAR(2550) + ,nullable=True + ,comment='备注' + ) + created_at = Column( + DateTime + ,nullable=False + ,default=datetime.now + ,comment='创建时间' + ) + updated_at = Column( + DateTime + ,nullable=False + ,default=datetime.now + ,comment='更新时间' + ) + is_deleted = Column( + TINYINT + ,nullable=False + ,default=0 + ,comment='逻辑删除' + )''' \ No newline at end of file diff --git a/main.py b/main.py index 700b8d4..be226d3 100644 --- a/main.py +++ b/main.py @@ -5,9 +5,11 @@ from api.statistics import statistics_router import uvicorn from api.student import student_router from api.teacher import router as teacher_router,class_teacher_router +from api.magic import magic_router app = FastAPI() app.middleware("http")(log_middleware) +app.include_router(magic_router) #-------------朱婷婷-------------- from api.class_api import classes_router @@ -23,6 +25,7 @@ from api import consultant app.include_router(consultant.router) #-------------张昕浩--------------- app.include_router(example_router) +app.include_router(magic_router) app.include_router(statistics_router, prefix="/statistics") #-------------曾凯--------------- @@ -34,4 +37,4 @@ app.include_router(employment_router, prefix="/employment") #-----------主程序---------------- if __name__ == "__main__": - uvicorn.run(app, host="0.0.0.0", port=23333) \ No newline at end of file + uvicorn.run(app, host="0.0.0.0", port=23333) diff --git a/requirements.txt b/requirements.txt index 0b1a59e..e01d68c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,5 @@ uvicorn==0.40.0 SQLAlchemy==2.0.49 PyMySQL==1.1.2 pydantic==2.13.3 +openai==2.30.0 +python-dotenv==1.2.2