# 教培系统架构设计与开发 本项目旨在开发一个基于FastAPI的学生管理系统,提供**学生基本信息管理、考核成绩管理和就业管理**三大核心功能模块。 系统将采用RESTful API设计,支持前后端分离架构。 ## 痛点分析 - 沃林专项 - 部门系统与产品 <- 没有拳头产品 - 给外面演示的系统,产品 - 内部OA系统都没有接AI,用的是飞书大厂的平台 - 学员就业信息等还是纸质存储,没有信息化 - 拳头产品,**自身没有服务外部公司的团队与技术能力** - 目前**只局限培训** - 教学课程开发 <- 课程体系太单一 - 只有入门-项目,不够灵活,满足专业3-5年的诉求 - 入门课程没有采用最新工具,**框架现代化API,很容易出去被pass - 外包等** - 只有30-60人班级,没有**小班制进阶,专家培训** - 客户群体拓展 <- 更多是跨行想入门的小白, - 风险把控 <- 后付款的包就业模式,资金链如何把控? ## 需求分期 团队对需求管理按照模块化解耦拆分开发,按照三个阶段开发 1. 基础需求:按照开发任务,完成5 + 1 统计模块的开发 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; create database sms; use sms; -- students create table if not exists students( id int auto_increment primary key, num varchar(30) not null comment '学生编号', name varchar(30) not null default '', age int not null default 18, sex tinyint not null default 0 comment '0未知 1男 2女', 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 default 0, advisor_id int null comment '顾问id', -- advisor_num int null comment '顾问编号', -- join teacher -- 通用字段 create_time datetime not null default current_timestamp, update_time datetime not null default current_timestamp on update current_timestamp, is_deleted tinyint not null default 0, delete_time datetime null comment '删除时间,记录软删除时间' ); -- teachers create table if not exists teachers( id int auto_increment primary key, num varchar(30) not null comment '老师编号', name varchar(30) not null, age int not null, sex tinyint not null default 0 comment '0未知 1男 2女', 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 varchar(50) null comment '教学方向:应用,项目,算法', -- 通用字段 create_time datetime not null default current_timestamp, update_time datetime not null default current_timestamp on update current_timestamp, is_deleted tinyint not null default 0, delete_time datetime null comment '删除时间,记录软删除时间' ); -- classes create table if not exists classes( id int auto_increment primary key, num varchar(30) not null comment '班级编号', name varchar(30) 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 default current_timestamp, update_time datetime not null default current_timestamp on update current_timestamp, is_deleted tinyint not null default 0, delete_time datetime null comment '删除时间,记录软删除时间' ); -- scores create table if not exists scores( id int auto_increment primary key, sid int not null, -- snum varchar(30) not null, num varchar(30) not null comment '考核序次', score decimal(10,2) not null, -- 通用字段 create_time datetime not null default current_timestamp, update_time datetime not null default current_timestamp on update current_timestamp, is_deleted tinyint not null default 0, delete_time datetime null comment '删除时间,记录软删除时间' ); -- employment create table if not exists employment( id int auto_increment primary key, sid int not null, -- snum varchar(30) not null, -- sname varchar(30) not null, class_id int not null, -- class_num varchar(30) 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(20,5) null comment '就业薪资', -- 通用字段 create_time datetime not null default current_timestamp, update_time datetime not null default current_timestamp on update current_timestamp, is_deleted tinyint not null default 0, delete_time datetime null comment '删除时间,记录软删除时间' ); ``` ### 造数据压测 #### qa - cout / max 对聚合的支持数据库实现区别? - #### 小范围数据测试 ```sql -- use student_management_system; use sms; -- ==================== 清空旧数据(按依赖倒序) ==================== set foreign_key_checks = 0; truncate table employment; truncate table scores; truncate table students; truncate table classes; truncate table teachers; set foreign_key_checks = 1; -- ==================== teachers ==================== insert into teachers (num, name, age, sex, home_place, college, specialty, enrollment_time, graduate_time, education, work_experience, coach_area) values ('T001', '张伟', 35, 1, '北京', '清华大学', '计算机科学与技术', '2008-09-01', '2012-06-30', '本科', '10年Java教学经验', '应用'), ('T002', '李娜', 32, 2, '上海', '复旦大学', '软件工程', '2010-09-01', '2014-06-30', '硕士', '8年前端教学经验', '项目'), ('T003', '王强', 40, 1, '广州', '中山大学', '人工智能', '2004-09-01', '2008-06-30', '博士', '12年算法教学经验', '算法'), ('T004', '赵敏', 28, 2, '深圳', '浙江大学', '数据科学', '2014-09-01', '2018-06-30', '硕士', '5年Python教学经验', '应用'), ('T005', '陈刚', 38, 1, '成都', '电子科技大学', '通信工程', '2006-09-01', '2010-06-30', '本科', '11年嵌入式教学经验', '项目'); -- ==================== classes ==================== insert into classes (num,name, class_start_time, head_teacher_id, coach_teacher_id, tutor_teacher_id) values ('C2024001','C2024001', '2024-03-01 09:00:00', 1, 2, 4), ('C2024002','C2024001', '2024-04-01 09:00:00', 2, 3, 5), ('C2024003','C2024003', '2024-05-01 09:00:00', 3, 1, 4); -- ==================== students ==================== insert into students (num, name, age, sex, home_place, college, specialty, enrollment_time, graduate_time, education, class_id, advisor_id) values ('S2024001', '刘洋', 22, 1, '北京', '北京大学', '计算机科学与技术', '2020-09-01', '2024-06-30', '本科', 1, 1), ('S2024002', '孙丽', 23, 2, '上海', '上海交通大学', '软件工程', '2019-09-01', '2023-06-30', '本科', 1, 1), ('S2024003', '周杰', 24, 1, '广州', '华南理工大学', '人工智能', '2018-09-01', '2022-06-30', '硕士', 1, 2), ('S2024004', '吴敏', 21, 2, '深圳', '深圳大学', '数据科学', '2021-09-01', '2025-06-30', '本科', 2, 3), ('S2024005', '郑浩', 25, 1, '成都', '四川大学', '通信工程', '2017-09-01', '2021-06-30', '本科', 2, 4), ('S2024006', '冯雪', 22, 2, '杭州', '浙江大学', '计算机科学与技术', '2020-09-01', '2024-06-30', '本科', 2, 5), ('S2024007', '黄磊', 23, 1, '南京', '南京大学', '软件工程', '2019-09-01', '2023-06-30', '本科', 3, 1), ('S2024008', '徐婷', 24, 2, '武汉', '武汉大学', '人工智能', '2018-09-01', '2022-06-30', '硕士', 3, 2), ('S2024009', '马超', 21, 1, '西安', '西安交通大学', '数据科学', '2021-09-01', '2025-06-30', '本科', 3, 3), ('S2024010', '朱琳', 22, 2, '天津', '南开大学', '通信工程', '2020-09-01', '2024-06-30', '本科', 3, 4); -- ==================== scores ==================== insert into scores (sid, num, score) values -- 刘洋 (1, '第一次考核', 85.50), (1, '第二次考核', 90.00), (1, '第三次考核', 88.00), -- 孙丽 (2, '第一次考核', 92.00), (2, '第二次考核', 87.50), (2, '第三次考核', 91.00), -- 周杰 (3, '第一次考核', 78.00), (3, '第二次考核', 82.50), (3, '第三次考核', 80.00), -- 吴敏 (4, '第一次考核', 95.00), (4, '第二次考核', 93.50), (4, '第三次考核', 96.00), -- 郑浩 (5, '第一次考核', 70.00), (5, '第二次考核', 75.50), (5, '第三次考核', 72.00), -- 冯雪 (6, '第一次考核', 88.00), (6, '第二次考核', 90.50), (6, '第三次考核', 89.00), -- 黄磊 (7, '第一次考核', 82.00), (7, '第二次考核', 85.00), (7, '第三次考核', 83.50), -- 徐婷 (8, '第一次考核', 91.00), (8, '第二次考核', 94.00), (8, '第三次考核', 92.50), -- 马超 (9, '第一次考核', 76.00), (9, '第二次考核', 79.50), (9, '第三次考核', 77.00), -- 朱琳 (10, '第一次考核', 89.00), (10, '第二次考核', 86.50), (10, '第三次考核', 90.00); -- 提供不及格数据 insert into students (num, name, age, sex, home_place, college, specialty, enrollment_time, graduate_time, education, class_id, advisor_id) values ('S2026001', '牧遥', 22, 1, '北京', '北京大学', '计算机科学与技术', '2020-09-01', '2024-06-30', '本科', 1, 1); insert into scores (sid, num, score) values -- 牧遥 (11, '第一次考核', 56.50), (11, '第二次考核', 30.00), (11, '第三次考核', 88.00); -- ==================== employment ==================== insert into employment (sid, class_id, employment_open_time, offer_recived_time, employment_company, employment_salary) values (1, 1, '2024-06-01 00:00:00', '2024-07-15 10:00:00', '字节跳动', 25000.00000), (2, 1, '2024-06-01 00:00:00', '2024-07-20 14:30:00', '腾讯', 28000.00000), (3, 1, '2024-06-01 00:00:00', '2024-08-01 09:00:00', '阿里巴巴', 30000.00000), (4, 2, '2024-07-01 00:00:00', '2024-08-10 11:00:00', '美团', 24000.00000), (5, 2, '2024-07-01 00:00:00', null, null, null), (6, 2, '2024-07-01 00:00:00', '2024-08-15 16:00:00', '京东', 26000.00000), (7, 3, '2024-08-01 00:00:00', '2024-09-05 10:30:00', '百度', 27000.00000), (8, 3, '2024-08-01 00:00:00', '2024-09-10 15:00:00', '华为', 32000.00000), (9, 3, '2024-08-01 00:00:00', null, null, null), (10, 3, '2024-08-01 00:00:00', '2024-09-20 09:30:00', '小米', 23000.00000); ``` ##### 统计大盘 - 示例sql ```sql -- 基本信息统计 select * from students where age > 30; select class_id, sex, count(*) from students group by class_id,sex; -- 成绩统计 select s.num,s.name, sc.score from students s left join scores sc on s.id = sc.sid where sc.score > 80; -- 两次不及格 -> CTE 更好点: <- todo | 代码实现角度:? select s.name 学生名称,c.name 班级名称 ,tmp.score 学生成绩 from students as s, classes as c, (select sid,score from scores where score < 60 group by sid, score) as tmp , (select sid from (select sid,score from scores where score < 60 group by sid, score) as tmp group by sid having count(*) >= 2 ) as sid_table where s.id = sid_table.sid and s.class_id = c.id and s.id = tmp.sid; -- 或者:全量join,然后2次group by -- select s.class_id, sc.num, avg(sc.score) from students s left join scores sc on s.id = sc.sid group by sc.num, s.class_id order by avg(sc.score) desc; -- 就业统计 select s.name, c.name, e.employment_open_time, e.offer_recived_time, e.employment_company,e.employment_salary from students s left join classes c on s.class_id = c.id left join employment e on s.id = e.sid order by e.employment_salary desc limit 5; select s.name, c.name, e.employment_open_time, e.offer_recived_time, e.employment_company,e.employment_salary, (e.offer_recived_time - e.employment_open_time) as duration from students s left join classes c on s.class_id = c.id left join employment e on s.id = e.sid; select s.class_id, avg(e.offer_recived_time -e.employment_open_time) from students s left join classes c on s.class_id = c.id left join employment e on s.id = e.sid where e.employment_open_time is not null group by s.class_id; ``` ```md employment_open_time datetime null comment '就业开放时间', offer_recived_time datetime null comment 'offer下发时间', employment_company varchar(50) null comment '就业公司', employment_salary decimal(20,5) null comment '就业薪资', ``` #### 学生表 - 数据量瓶颈是多少? - 千万?亿级 #### 分库分表 - 考虑历史数据与最新数据 ### 表结构优化 #### 通用字段方案 ##### 框架层 - ORM 基类 ```python from sqlalchemy import Column, Integer, String, DATETIME, create_engine, Boolean from datetime import datetime # 定义基类模板,解决通用字段 class Template(): id = Column( Integer # 声明字段的数据类型 , primary_key=True # 声明是主键 , autoincrement=True # 声明是自增主键 , comment='订单编号,自增主键' # 注释 ) create_datetime = Column(DATETIME , default=datetime.now # 默认值是当前时间 ) update_datetime = Column(DATETIME , default=datetime.now # 首次创建的时间跟更新时间一致 , onupdate=datetime.now ) is_deleted = Column(Boolean, default=False ) delete_datetime = Column(DATETIME, nullable=True) ``` ##### triger deletetime ```sql delimiter $$ create trigger trg_students_update_time before update on students for each row begin -- 只在业务字段变化时更新 update_time,纯软删除操作不更新 if not (old.is_deleted <=> new.is_deleted) and old.sname <=> new.sname and old.age <=> new.age then set new.update_time = old.update_time; else set new.update_time = current_timestamp; end if; end$$ delimiter ; ``` #### 索引与约束 ##### 索引同步 | 表 | 索引名 | 类型 | 字段 | 对应查询场景 | | -------------- | ----------------- | ---- | ------------------------ | -------------------------------- | | **students** | PRIMARY | 主键 | `id` | 按主键查 | | | uk_snum | 唯一 | `snum` | 按学号精确查 | | | idx_class_id | 普通 | `class_id` | 按班级查学生列表 | | | idx_advisor_id | 普通 | `advisor_id` | 按顾问查学生 | | | idx_class_deleted | 联合 | `(class_id, is_deleted)` | 查某班未删除学生 | | **teachers** | PRIMARY | 主键 | `id` | 按主键查 | | | uk_tnum | 唯一 | `tnum` | 按老师编号精确查 | | **classes** | PRIMARY | 主键 | `id` | 按主键查 | | | uk_class_num | 唯一 | `class_num` | 按班级编号精确查 | | | idx_head_teacher | 普通 | `head_teacher_id` | 按班主任查班级 | | | idx_coach_teacher | 普通 | `coach_teacher_id` | 按授课老师查班级 | | | idx_tutor_teacher | 普通 | `tutor_teacher_id` | 按助教查班级 | | **scores** | PRIMARY | 主键 | `id` | 按主键查 | | | uk_sid_score | 唯一 | `(sid, score_num)` | 防止同一学生同一考核序次重复录入 | | | idx_snum | 普通 | `snum` | 按学号查成绩 | | | idx_sid | 普通 | `sid` | 按学生 ID 查成绩 | | **employment** | PRIMARY | 主键 | `id` | 按主键查 | | | uk_sid | 唯一 | `sid` | 一个学生只能有一条就业记录 | | | idx_class_id | 普通 | `class_id` | 按班级统计就业 | | | idx_snum | 普通 | `snum` | 按学号查就业 | | | idx_class_deleted | 联合 | `(class_id, is_deleted)` | 查某班未删除就业记录 | ```sql -- alter table students drop primary key -- auto_increment 列 必须是 key -- alter table students drop index idx_email -- ==================== students ==================== alter table students add unique key uk_snum (num), add key idx_class_id (class_id), add key idx_advisor_id (advisor_id), add key idx_class_deleted (class_id, is_deleted); -- ==================== teachers ==================== alter table teachers add unique key uk_tnum (num); -- ==================== classes ==================== alter table classes add unique key uk_class_num (num), add key idx_head_teacher (head_teacher_id), add key idx_coach_teacher (coach_teacher_id), add key idx_tutor_teacher (tutor_teacher_id); -- ==================== scores ==================== alter table scores add unique key uk_sid_score (sid, num), -- add key idx_snum (snum), add key idx_sid (sid); -- ==================== employment ==================== alter table employment add unique key uk_sid (sid), add key idx_class_id (class_id), -- add key idx_snum (snum), add key idx_class_deleted (class_id, is_deleted); ``` ##### 外键约束 | 表 | 字段 | 引用 | 含义 | | -------------- | ------------------ | ------------- | ---------------- | | **students** | `class_id` | `classes.id` | 学生所属班级 | | **students** | `advisor_id` | `teachers.id` | 学生的顾问老师 | | **classes** | `head_teacher_id` | `teachers.id` | 班主任 | | **classes** | `coach_teacher_id` | `teachers.id` | 授课老师 | | **classes** | `tutor_teacher_id` | `teachers.id` | 助教老师 | | **scores** | `sid` | `students.id` | 成绩所属学生 | | **employment** | `sid` | `students.id` | 就业记录所属学生 | | **employment** | `class_id` | `classes.id` | 就业记录所属班级 | ```sql -- SET FOREIGN_KEY_CHECKS = 0; -- 临时关闭 -- 批量导入 -- SET FOREIGN_KEY_CHECKS = 1; -- ==================== students ==================== alter table students add constraint fk_students_class foreign key (class_id) references classes(id) on delete restrict on update cascade, -- on delete set null on update cascade, add constraint fk_students_advisor foreign key (advisor_id) references teachers(id) on delete set null on update cascade; -- ==================== classes ==================== alter table classes add constraint fk_classes_head_teacher foreign key (head_teacher_id) references teachers(id) on delete set null on update cascade, add constraint fk_classes_coach_teacher foreign key (coach_teacher_id) references teachers(id) on delete set null on update cascade, add constraint fk_classes_tutor_teacher foreign key (tutor_teacher_id) references teachers(id) on delete set null on update cascade; -- ==================== scores ==================== alter table scores add constraint fk_scores_student foreign key (sid) references students(id) on delete cascade on update cascade; -- on delete set null on update cascade; -- ==================== employment ==================== alter table employment add constraint fk_employment_student foreign key (sid) references students(id) on delete cascade on update cascade, -- on delete set null on update cascade, add constraint fk_employment_class foreign key (class_id) references classes(id) on delete restrict on update cascade; -- on delete set null on update cascade; ``` #### **字典表**: 解决字段规范问题 > **`education`、`college`、`specialty`** | 字段 | 所在表 | 问题 | 字典化收益 | | ------------------------ | ------------------ | --------------------------- | ---------------------------------- | | **`sex`** | students, teachers | 用 `int` 存,取值含义靠注释 | 统一“0未知/1男/2女” | | **`education`** | students, teachers | 字符串自由填写 | 统一“本科/硕士/博士”等 | | **`college`** | students, teachers | 字符串自由填写 | 统一院校名称 | | **`specialty`** | students, teachers | 字符串自由填写 | 统一专业名称 | | **`coach_area`** | teachers | `text` 存“应用/项目/算法” | 统一教学方向 | | **`employment_company`** | employment | 字符串自由填写 | 可选,公司名变化快,不一定适合字典 | **判断标准**:取值**有限、稳定、需要跨表统一**的字段,适合字典化;取值**开放、频繁变化**的字段(如公司名、地址),不适合。 ##### 通用字典表 ```sql create table if not exists dict_items ( id int auto_increment primary key, dict_type varchar(50) not null comment '字典类型:sex, education, college, specialty', dict_code varchar(50) not null comment '字典编码,存到业务表的值', dict_label varchar(100) not null comment '显示名称', sort_order int not null default 0 comment '排序', is_enabled tinyint not null default 1 comment '是否启用', create_time datetime not null default current_timestamp, update_time datetime not null default current_timestamp on update current_timestamp, is_deleted tinyint not null default 0, delete_time datetime not null default null, unique key uk_type_code (dict_type, dict_code), key idx_type (dict_type) ) engine=InnoDB default charset=utf8mb4; insert into dict_items (dict_type, dict_code, dict_label, sort_order) values ('sex', '0', '未知', 1), ('sex', '1', '男', 2), ('sex', '2', '女', 3), ('education', 'bachelor', '本科', 1), ('education', 'master', '硕士', 2), ('education', 'phd', '博士', 3), ('coach_area', 'app', '应用', 1), ('coach_area', 'project', '项目', 2), ('coach_area', 'algorithm', '算法', 3); ``` ##### 每个维度独立建字典 - 适合高校+专业维度多 - 后期维护更专业! ```sql create table if not exists dict_colleges ( id int auto_increment primary key, college_code varchar(50) not null, college_name varchar(100) not null, is_enabled tinyint not null default 1, unique key uk_code (college_code) ) engine=InnoDB default charset=utf8mb4; create table if not exists dict_specialties ( id int auto_increment primary key, specialty_code varchar(50) not null, specialty_name varchar(100) not null, college_id int not null comment '所属学院', is_enabled tinyint not null default 1, unique key uk_code (specialty_code), key idx_college (college_id) ) engine=InnoDB default charset=utf8mb4; -- 全国高校,专业数据 ``` ##### 业务表引用 ###### dict_code - 采用 ```sql -- students.education 存 'bachelor',而不是 '本科' select s.sname, d.dict_label as education_name from students s left join dict_items d on d.dict_type = 'education' and d.dict_code = s.education and d.is_deleted = 0; ``` ###### id - 简单,但是迁移 - 跨环境id自增导致不一致问题 ```sql select s.* , from students s left join dict_items d on s.college = d.id ``` ###### 注意事项 > **软删除的字典项,业务表还在引用** <- 字典表更新 - 轻易不再动 > > - 如果某个字典项被软删除,但业务表里还有引用它的记录,查询时会 JOIN 不到,显示为空。 > - **解决方案**:字典项不轻易删除,只设 `is_enabled = 0`(禁用),禁用后不允许新记录引用,但历史记录仍能 JOIN 到 > > 业务表引用字典表的 `dict_code`,**不建议加数据库外键** > > - 因为 `dict_code` 是字符串,且字典表可能被缓存到应用层,加外键会限制字典的灵活性。 > > - **用应用层校验**:插入业务记录前,先检查 `dict_code` 是否存在于字典表。 > > **字典数据量大了怎么办?** > > - 如果字典项很多(比如全国高校列表),`dict_items` 一张表会变得很大。 > - **解决方案**:按 `dict_type` 分表,或者把大字典(如高校)单独建表。 | 方案 | 优点 | 缺点 | 适用场景 | | ---------------- | -------------------- | -------------------- | ------------------ | | **字典表** | 可动态维护、跨表统一 | 需 JOIN、需维护 | 取值有限且需要统一 | | **枚举(ENUM)** | 数据库层面约束 | 改枚举要 DDL、不灵活 | 取值极少且永不变 | | **直接存字符串** | 简单、无 JOIN | 容易不一致、难统计 | 取值开放、无需统一 | ## 技术架构设计 ### 构建环境适配 ```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 - ### 脚手架设计 - 单体架构 ### 分布式架构 - 微服务 > 方向: > > - 把学生模块拆分出来 > - ## AI-agent接入 > 核心 > > - 让agent 做架构优化 > - 添加业务模块:课程体系设计, > - > - 生成前端 > - 完善脚手架....