Files
student_manage_system/README.md
T
2026-09-21 18:03:34 +08:00

871 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 教培系统架构设计与开发
本项目旨在开发一个基于FastAPI的学生管理系统,提供**学生基本信息管理、考核成绩管理和就业管理**三大核心功能模块。
系统将采用RESTful API设计,支持前后端分离架构。
## 需求分期
团队对需求管理按照模块化解耦拆分开发,按照三个阶段开发
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 做架构优化
> - 添加业务模块:课程体系设计,
> -
> - 生成前端
> - 完善脚手架....