Skip to content

Repository files navigation

Course Management System

课程管理系统(Course Management System,简称 CMS)是一套面向学校内部使用的课程、课表、班级、学生和公告管理系统。项目由 Windows 桌面客户端、Android 客户端和 FastAPI 服务端组成。

功能概览

  • 用户登录、退出登录、令牌校验和修改密码
  • 管理员维护课程、教师、班级、作息时间和公告
  • 教师查看个人课表、班级课表、学生信息、请假信息和学生备注
  • Windows 客户端导出课表数据
  • Android 客户端离线缓存课表,并使用 Android Keystore 保护登录令牌
  • SQLite 数据库存储业务数据,头像和运行日志存放在独立数据目录

项目结构

.
├── src/
│   ├── client/                 # Windows 客户端:CustomTkinter UI、网络请求和本地缓存
│   ├── server/                 # FastAPI 服务端、路由、认证、权限和数据库初始化
│   ├── constants.py            # Python 客户端与服务端共用的学期和年级常量
│   └── main.py                 # Windows 客户端启动入口
├── android_client/             # Android 客户端:Kotlin、Retrofit、Room
├── assets/                     # Windows 客户端图标和其他静态资源
├── data/                       # 运行时数据目录,仅保留 data/.gitkeep
├── docs/architecture.md        # 系统边界和部署架构说明
├── requirements.txt            # Python 依赖
├── .env.example                # 服务端和 Windows 客户端配置模板
└── 课程管理系统.spec           # PyInstaller 打包配置

运行时数据不应提交到 GitHub。生产环境建议将 CMS_DATA_DIR 设置为源代码目录之外的专用目录,并单独进行访问控制和备份。

系统架构

Windows 客户端 ─┐
                ├─ HTTPS + Authorization: Bearer <token> ─> FastAPI ─> SQLite/头像/日志
Android 客户端 ─┘

服务端是唯一的权限判断边界。客户端中的角色判断只用于控制界面显示,不能替代服务端鉴权。生产环境应由 Nginx、Caddy 或云负载均衡器终止 TLS,再将请求转发给仅监听本机的 Uvicorn。

运行环境

服务端和 Windows 客户端

  • Windows、Linux 或 macOS
  • Python 3.10 或更高版本
  • 生产环境建议使用独立虚拟环境
  • 服务端需要可写的数据目录

Android 客户端

  • Android Studio 或 JDK 17 兼容的 Android 构建环境
  • Android SDK 34
  • Android 8.0(API 26)或更高版本的设备或模拟器
  • 项目自带 Gradle Wrapper,可使用 gradlewgradlew.bat

服务端部署

1. 获取源代码并创建虚拟环境

git clone <repository-url>
Set-Location CourseManagementSystem

py -3 -m venv .venv
.\.venv\Scripts\Activate.ps1
py -m pip install --upgrade pip
py -m pip install -r requirements.txt

Linux/macOS:

python3 -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -r requirements.txt

2. 创建服务端配置

复制 .env.example.env,或在服务管理器中直接设置环境变量:

Copy-Item .env.example .env

配置项说明:

变量 必填 说明
CMS_HOST Uvicorn 监听地址,默认 127.0.0.1
CMS_PORT Uvicorn 监听端口,默认 5000
CMS_DEBUG 是否启用 FastAPI 调试文档,生产环境必须为 false
CMS_LOG_LEVEL 日志级别,默认 INFO
CMS_DATA_DIR 数据库、日志和头像目录,生产环境建议放在项目目录外
CMS_SECRET_KEY 部署实例的随机长字符串,不能使用示例值
CMS_SEED_PASSWORD 首次初始化必填 空数据库首次初始化时的演示账号初始密码,至少 12 个字符
CMS_TOKEN_TTL_DAYS 登录令牌有效天数,默认 30
CMS_SERVER_URL 客户端使用 Windows 客户端连接的服务端基地址

生成随机值的示例:

py -c "import secrets; print(secrets.token_urlsafe(48))"

生产环境至少需要设置:

CMS_HOST=127.0.0.1
CMS_PORT=5000
CMS_DEBUG=false
CMS_DATA_DIR=/var/lib/course-management-system
CMS_SECRET_KEY=<随机生成的长字符串>
CMS_SEED_PASSWORD=<仅用于首次初始化的随机密码>
CMS_TOKEN_TTL_DAYS=30

不要把真实 .env 文件提交到仓库,也不要把密码写入启动脚本、截图、Issue 或日志。

3. 初始化数据库

服务端启动时会自动创建 SQLite 表。第一次启动空数据目录时,CMS_SEED_PASSWORD 会用于生成 models.py 中的示例账号:

$env:CMS_SECRET_KEY = "replace-with-a-private-random-secret"
$env:CMS_SEED_PASSWORD = "replace-with-a-private-seed-password"
$env:CMS_DATA_DIR = "$((Get-Location).Path)\runtime-data"
py -m src.server.server

Linux/macOS:

export CMS_SECRET_KEY="$(python -c 'import secrets; print(secrets.token_urlsafe(48))')"
export CMS_SEED_PASSWORD="$(python -c 'import secrets; print(secrets.token_urlsafe(18))')"
export CMS_DATA_DIR=/var/lib/course-management-system
python -m src.server.server

启动成功后,服务端默认监听 127.0.0.1:5000。健康检查:

GET http://127.0.0.1:5000/

返回 status: running 仅表示进程正常运行,不代表服务端已经配置好公网 TLS。

4. 首次登录后的操作

  1. 使用部署时设置的 CMS_SEED_PASSWORD 登录每个示例账号。
  2. 立即为每个账号设置不同的强密码。
  3. 删除不需要的示例账号,或在管理员接口中替换为实际账号。
  4. 检查管理员、班主任、课程教师和班级的对应关系。
  5. 确认数据库备份目录权限仅允许服务账号和备份账号访问。
  6. 初始化完成后可以保留 CMS_SEED_PASSWORD,但它不应再被视为生产账号密码;若重新初始化新数据库,仍需提供一个新的随机值。

5. Windows 服务方式运行

开发和小规模内部部署可以直接运行:

.\.venv\Scripts\Activate.ps1
py -m src.server.server

长期运行时应使用 Windows Service、任务计划程序或其他进程管理器,并设置:

  • 工作目录为项目根目录;
  • Python 可执行文件为虚拟环境中的 Python;
  • CMS_DATA_DIR 指向专用数据目录;
  • 自动重启和日志轮转;
  • 服务账号只拥有项目和数据目录所需的最小权限。

6. Linux systemd 示例

以下示例假设项目位于 /opt/course-management-system,运行用户为 cms

[Unit]
Description=Course Management System API
After=network.target

[Service]
User=cms
Group=cms
WorkingDirectory=/opt/course-management-system
EnvironmentFile=/etc/course-management-system/cms.env
ExecStart=/opt/course-management-system/.venv/bin/python -m src.server.server
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true

[Install]
WantedBy=multi-user.target

将配置文件保存为 /etc/systemd/system/course-management-system.service,再执行:

sudo systemctl daemon-reload
sudo systemctl enable --now course-management-system
sudo systemctl status course-management-system
sudo journalctl -u course-management-system -f

7. HTTPS/TLS 反向代理

客户端传输登录密码、Bearer Token、学生信息和头像数据。生产环境必须使用 HTTPS,不能让客户端直接连接公网 HTTP Uvicorn 端口。

Nginx 示例

以下配置将 https://cms.example.com 转发到本机 127.0.0.1:5000。证书路径应替换为实际证书路径,证书可以使用受信任的 ACME/Let's Encrypt 证书:

server {
    listen 80;
    listen [::]:80;
    server_name cms.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name cms.example.com;

    ssl_certificate     /etc/letsencrypt/live/cms.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/cms.example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_session_timeout 1d;

    client_max_body_size 5m;
    proxy_read_timeout 30s;

    location / {
        proxy_pass http://127.0.0.1:5000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
    }
}

校验证书和重载配置:

sudo nginx -t
sudo systemctl reload nginx
curl -fsS https://cms.example.com/

防火墙只应开放 80/tcp443/tcp。不要开放公网 5000/tcp,除非部署环境有明确的网络隔离和访问控制。

Caddy 示例

cms.example.com {
    reverse_proxy 127.0.0.1:5000
    request_body {
        max_size 5MB
    }
}

Caddy 会自动申请和续期受信任的 TLS 证书。服务端进程仍建议只监听 127.0.0.1

8. 备份与恢复

SQLite 使用 WAL 模式运行。备份时应先暂停写入,或使用 SQLite 的在线备份机制;不要只复制正在运行中的 cms.db 而忽略 -wal 文件。

停机复制示例:

sudo systemctl stop course-management-system
cp /var/lib/course-management-system/cms.db /backup/cms-$(date +%F).db
sudo systemctl start course-management-system

备份内容还包括头像文件和必要的运行配置,但不应把备份目录放进 Git 仓库。备份文件应加密保存,并定期验证可以恢复。

修改 models.py 中的示例数据

src/server/models.py_seed_data() 只会在 users 表为空时写入初始示例数据。因此,修改代码后已经存在的数据库不会自动更新。修改示例数据时必须明确区分“修改种子代码”和“修改现有数据库”两种操作。

1. 修改示例用户

users 列表中修改以下字段:

users = [
    (1, "示例管理员一", pw(seed_password), "admin", "示例管理员一"),
    (2, "示例管理员二", pw(seed_password), "admin", "示例管理员二"),
    (3, "示例管理员三", pw(seed_password), "admin", "示例管理员三"),
]

每一项的顺序是:

(用户 ID, 登录用户名, 密码哈希, 角色, 显示名称)

规则:

  • 登录用户名必须唯一;
  • admin 为管理员,teacher 为教师,member 为普通用户;
  • 初始密码由 CMS_SEED_PASSWORD 统一提供,代码中不能写入明文密码;
  • 修改用户名或显示名称后,必须同步修改下方课表中的教师名称引用;
  • 生产环境不要把真实人员姓名直接写入公开仓库的种子数据,可在部署后通过管理流程录入。

2. 修改班级和班主任

grade_advisor 定义班级与班主任的关系:

grade_advisor = {
    "一年级": "示例教师一",
    "二年级": "示例教师二",
}

字典的键必须与 grade_schedules 的年级键一致,值必须存在于 users 列表中,并且通常应对应 teacher 角色。班级教室名称由以下代码生成:

f"{grade}教室"

如果要使用自定义班级名称或地点,应同时修改班级插入逻辑、课表中的 class_namelocation 字段,避免学生、课程和班级无法关联。

3. 修改课程教师映射

grade_schedules 的每一项格式如下:

("数学", "示例教师一")

外层结构是:

年级 -> 星期几(0 表示星期一) -> 一天中的 7 个上课时段

periods 决定 7 个课程槽位使用的作息表 ID 和时间。修改课程时应注意:

  • 每天课程数量应与 periods 数量一致;
  • 教师名称必须能被 nid[teacher_name] 找到;
  • 课程所属年级必须同时存在于 grade_advisor
  • 课程颜色键应与 scol 中的科目名称一致,否则使用默认颜色;
  • 修改课表后应重新初始化测试数据库并登录验证每个角色。

4. 修改公告

公告位于 _seed_data() 中的 announcements 列表:

announcements = [
    ("公告标题", "公告正文"),
]

公告正文中的换行使用 \n。不要在公开仓库中写入真实电话号码、邮箱、地址或内部系统信息。

5. 修改示例学生

示例学生由以下逻辑生成:

for grade in grade_advisor.keys():
    for i in range(1, 6):
        ...

学生姓名、联系方式、性别和走读状态都是演示数据。修改时必须保证:

  • class_name 已存在于 classes.name
  • genderis_boarding 使用客户端和服务端约定的值;
  • 联系方式只能使用虚构号码;
  • 不要把真实未成年人信息放入公开仓库;
  • 生产数据应通过受控的管理流程导入,而不是提交 SQLite 数据库。

6. 让种子数据重新生效

仅修改 models.py 不会覆盖已有数据库。开发环境可以删除本地数据库后重新启动:

Remove-Item .\data\cms.db, .\data\cms.db-shm, .\data\cms.db-wal -Force -ErrorAction SilentlyContinue
$env:CMS_SEED_PASSWORD = "a-new-private-random-password"
py -m src.server.server

生产环境不要直接删除数据库。应先完成备份,在维护窗口执行迁移或通过管理接口逐项更新,并保留回滚方案。

Windows 客户端

在项目根目录创建并激活 Python 虚拟环境后运行:

.\.venv\Scripts\Activate.ps1
$env:CMS_SERVER_URL = "https://cms.example.com"
py -m src.main

开发环境连接本机服务端:

$env:CMS_SERVER_URL = "http://127.0.0.1:5000"
py -m src.main

生产环境必须使用 HTTPS 域名。客户端不会通过请求参数或自定义用户 ID 进行身份认证,登录后只使用服务端返回的 Bearer Token。

打包 Windows 可执行文件

.\.venv\Scripts\Activate.ps1
py -m pip install pyinstaller
pyinstaller --clean --noconfirm "课程管理系统.spec"

构建结果在 dist/。发布前应在干净的 Windows 环境中测试启动、登录、课表、公告、三种角色权限、修改密码、退出登录、头像上传和课表导出,并确认客户端配置的是正式 HTTPS 地址。

Android 客户端

Android 客户端位于 android_client/。服务端地址通过 Gradle 属性 CMS_BASE_URL 注入,不应硬编码到 Kotlin 源代码。

调试构建

Android 模拟器访问开发机本机服务端时,使用 10.0.2.2

Set-Location android_client
.\gradlew.bat assembleDebug -PCMS_BASE_URL=http://10.0.2.2:5000/

真机调试时,应使用开发机在局域网中的地址,并确保防火墙允许局域网访问。只允许调试构建使用明文 HTTP;发布构建默认禁止明文流量。

Android Studio 配置

  1. 用 Android Studio 打开 android_client/
  2. 等待 Gradle 同步完成。
  3. 在 Gradle properties 或命令行中设置 CMS_BASE_URL
  4. 选择模拟器或 USB 调试设备。
  5. 执行 assembleDebug 或直接运行 Debug 配置。

API 概览

认证请求使用:

Authorization: Bearer <token>

主要接口:

路径 方法 用途
/api/login POST 登录并获取 Bearer Token
/api/logout POST 撤销当前令牌
/api/verify GET 验证当前令牌
/api/schedule/full GET 获取课表和当前用户课程
/api/timetable GET 获取作息时间
/api/announcements GET 获取公告
/api/user/... 多种 获取资料、修改密码和头像
/api/classes/... GET 获取班级和教师信息
/api/students/... 多种 获取学生、备注和请假信息
/api/admin/... 多种 管理员课表和基础数据维护
/api/sync/status GET 获取数据同步版本

生产环境默认关闭 FastAPI /docs/redoc 和 OpenAPI 文档。调试时可临时设置 CMS_DEBUG=true,调试结束后必须恢复为 false

常见问题

启动时报 CMS_SEED_PASSWORD must be set

数据目录为空时必须设置至少 12 个字符的 CMS_SEED_PASSWORD。如果数据库已经初始化,可以继续保留该环境变量,也可以在不重建数据库的情况下修改它;它不会自动修改现有用户密码。

客户端提示连接失败

依次检查服务端进程、客户端地址、Android 模拟器的 10.0.2.2、反向代理到 127.0.0.1:5000 的连通性、TLS 证书信任链、防火墙和云安全组的 443/tcp 放行情况。

登录成功后请求返回 401

Token 可能已过期、被注销、服务端数据库已恢复到旧备份,或客户端缓存了旧 Token。退出登录后重新登录;如果仍失败,检查服务端时间、CMS_DATA_DIR 和数据库文件是否正确。

修改 models.py 后界面没有变化

种子函数只在空数据库上运行。开发环境删除数据库后重启,生产环境使用数据库迁移或受控管理接口,不要直接删除生产数据库。

Android Release 构建未签名

确认四个 CMS_RELEASE_* 属性均已提供,并且 CMS_RELEASE_STORE_FILE 指向存在且可读的 keystore。调试包可以不配置发布签名,但发布包必须使用受保护的签名凭据。

许可证与免责声明

开源许可

本项目遵循 MIT 许可证 开源,允许学习、交流与二次开发。许可证正文以根目录 LICENSE 文件为准。

免责与学术声明

  1. 仅供学习交流:本项目主要用于个人学习与课程设计参考,不对代码在生产环境中的可用性或安全性作任何保证。
  2. 学术诚信提示:严禁直接抄袭、打包本项目用于课程作业、毕业设计或答辩提交。使用者须自行承担因违背学术诚信要求而产生的任何后果。

上述学术诚信提示是项目使用提醒,不改变 MIT 许可证授予的开源权利。使用者仍应遵守所在学校、机构或平台的学术规范。

About

Course Management System with Windows, Android, and FastAPI clients

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages