Selaa lähdekoodia

init 系统配置、文件日志

YueYunyun 5 kuukautta sitten
vanhempi
sitoutus
b6255675dd

+ 51 - 0
.gitignore

@@ -0,0 +1,51 @@
+# Python
+__pycache__/
+*.py[cod]
+*$py.class
+*.so
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# IDE
+.idea/
+.vscode/
+*.swp
+*.swo
+*.sublime-workspace
+*.sublime-project
+
+# Environment
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Logs and databases
+*.log
+*.sqlite
+*.db
+
+# Testing
+.coverage
+htmlcov/
+.pytest_cache/
+.tox/
+
+

+ 127 - 212
README.md

@@ -1,237 +1,152 @@
-# 智能题库学习系统 (Smart Question Bank Learning System)
-
-## 项目简介
-
-这是一个基于 Python 开发的智能题库学习系统,旨在帮助学生进行高效的题目练习和知识掌握。系统具备题目管理、智能练习、错题本、学习进度追踪等功能,让学习更有针对性和效果。
-
-## 主要功能
-
-### 1. 题库管理
-
-- 支持多学科题目录入(数学、语文、英语等)
-- 题目分类管理(按照难度、知识点、题型等)
-- 支持多种题型(选择题、填空题、简答题等)
-- 每道题目包含详细解析和知识点说明
-- 题目版本控制,记录修改历史
-- 题目审核流程,确保题目质量
-- 题目标签系统,方便分类和搜索
-
-### 2. 智能练习
-
-- 根据学生水平自动推荐适合的题目
-- 支持自选题型和难度进行练习
-- 练习过程中即时查看答案和解析
-- 练习完成后显示得分和错题分析
-- 优化推荐算法,结合用户历史表现和学习目标
-- 个性化推荐,根据用户偏好推荐题目
-- 自适应练习模式,动态调整题目难度
-
-### 3. 错题本功能
-
-- 自动收集做错的题目
-- 错题分类整理
-- 支持错题重复练习
-- 错题数据分析,找出薄弱知识点
-- 错题原因分析,帮助用户理解错误原因
-- 错题标签,方便分类和复习
-- 错题相似题推荐,巩固薄弱知识点
-
-### 4. 学习进度追踪
-
-- 展示每日/每周/每月的练习情况
-- 知识点掌握程度分析
-- 学习时长统计
-- 成绩进步曲线
-- 知识点掌握预测,预估用户掌握程度
-- 学习路径规划,提供个性化学习建议
-- 学习目标设置,帮助用户明确学习方向
-- 手动考试成绩录入,记录各类考试成绩
-- 考试成绩分析,生成成绩报告
-
-## 技术架构
-
-- 后端:Python 3.13
-- 数据库:Mysql
-- 用户界面:Web 界面
-
-## 数据库设计
-
-### 题目表(questions)
-
-- id: 题目 ID
-- subject: 学科
-- type: 题型
-- difficulty: 难度等级
-- content: 题目内容
-- answer: 答案
-- explanation: 解析
-- knowledge_points: 相关知识点
-- version: 题目版本
-- status: 审核状态
-- tags: 题目标签
-- creator_id: 题目创建者
-- reviewer_id: 题目审核者
-
-### 用户表(users)
-
-- id: 用户 ID
-- username: 用户名
-- password: 密码
-- grade: 年级
-- create_time: 创建时间
-- preferences: 学习偏好
-- goals: 学习目标
-- learning_style: 学习风格
-- target_score: 目标分数
-
-### 练习记录表(practice_records)
-
-- id: 记录 ID
-- user_id: 用户 ID
-- question_id: 题目 ID
-- is_correct: 是否正确
-- practice_time: 练习时间
-- time_spent: 用时
-- steps: 答题步骤
-- thinking_time: 思考时间
-- confidence_level: 答题信心
-- feedback: 用户反馈
-
-### 知识点表(knowledge_points)
-
-- id: 知识点 ID
-- name: 知识点名称
-- description: 知识点描述
-- subject: 所属学科
-- difficulty: 难度等级
-- prerequisite: 先修知识点
-
-### 学习路径表(learning_paths)
-
-- id: 路径 ID
-- user_id: 用户 ID
-- target_knowledge_point: 目标知识点
-- path: 学习路径
-- progress: 当前进度
-
-### 考试成绩表(exam_scores)
-
-- id: 成绩 ID
-- user_id: 用户 ID
-- exam_name: 考试名称
-- subject: 考试科目
-- score: 考试成绩
-- total_score: 总分
-- exam_date: 考试日期
-- remark: 备注
-
-## 使用说明
-
-1. 系统启动后,首先需要注册/登录账号
-2. 选择想要练习的学科和题型
-3. 开始答题,系统会记录答题情况
-4. 可以随时查看错题本和学习进度
-5. 在成绩管理页面手动录入考试成绩
-
-## 项目结构
+## 配置模块使用说明
 
+配置模块提供了统一的配置管理接口,主要功能包括:
+
+- 获取配置实例
+- 获取配置项值
+- 注册配置变更回调
+
+### 获取配置实例
+
+```python
+from app.config import get_config
+
+config = get_config()
 ```
-smart-question-bank/
-├── backend/              # 后端服务
-│   ├── src/              # 源代码目录
-│   │   ├── common/       # 公共模块
-│   │   ├── exception/    # 异常处理
-│   │   ├── models/       # 数据模型
-│   │   ├── services/     # 业务逻辑
-│   │   ├── controllers/  # API控制器
-│   │   ├── middleware/   # 中间件
-│   │   ├── utils/        # 工具类
-│   │   ├── config/       # 配置文件
-│   │   ├── migrations/   # 数据库迁移
-│   │   └── main.py       # 程序入口
-│   ├── tests/            # 测试目录
-│   ├── requirements.txt  # 依赖包列表
-│   └── Dockerfile        # Docker配置
-├── frontend/             # 前端应用
-│   ├── public/           # 静态资源
-│   ├── src/              # 源代码目录
-│   │   ├── assets/       # 静态资源
-│   │   ├── components/   # 通用组件
-│   │   ├── views/        # 页面视图
-│   │   ├── router/       # 路由配置
-│   │   ├── store/        # 状态管理
-│   │   ├── api/          # API接口
-│   │   ├── utils/        # 工具函数
-│   │   ├── styles/       # 全局样式
-│   │   └── main.js       # 程序入口
-│   ├── tests/            # 测试目录
-│   ├── package.json      # 项目配置
-│   ├── vue.config.js     # Vue配置
-│   └── Dockerfile        # Docker配置
-├── docs/                 # 文档目录
-│   ├── api/              # API文档
-│   └── design/           # 设计文档
-├── scripts/              # 脚本目录
-│   ├── deploy/           # 部署脚本
-│   └── migration/        # 数据迁移脚本
-├── docker-compose.yml    # Docker Compose配置
-├── README.md             # 项目说明
-└── .gitignore            # Git忽略配置
+
+### 获取配置项值
+
+```python
+from app.config import get_config_value
+
+# 获取配置项值,如果不存在则返回默认值
+value = get_config_value('key', default='default_value')
 ```
 
-## 安装与运行
+### 注册配置变更回调
 
-1. 安装依赖
+```python
+from app.config import on_config_change
 
-```bash
-pip install -r requirements.txt
+def callback():
+    print('配置已变更')
+
+on_config_change(callback)
 ```
 
-2. 配置数据库
+### 异常处理
 
-- 创建 MySQL 数据库
-- 修改 src/common/config.py 中的数据库连接配置
+所有接口都可能会抛出`RuntimeError`异常,建议在使用时进行捕获处理:
 
-3. 运行系统
+```python
+try:
+    config = get_config()
+except RuntimeError as e:
+    print(f'获取配置失败:{e}')
+```
 
-```bash
-python src/main.py
+## 环境配置说明
+
+项目支持多环境配置,通过不同的配置文件管理:
+
+- application.yml:基础配置
+- application-dev.yml:开发环境配置
+- application-prod.yml:生产环境配置
+
+### 配置优先级
+
+1. 环境特定配置(application-{env}.yml)
+2. 基础配置(application.yml)
+
+### 环境切换
+
+通过设置环境变量`APP_ENV`来切换环境:
+
+#### 项目级设置(推荐)
+
+在项目根目录下创建`.env`文件,内容如下:
+
+```env
+# 开发环境
+APP_ENV=dev
+
+# 生产环境
+# APP_ENV=prod
+```
+
+注意:
+
+1. 该设置仅对当前项目有效
+2. .env 文件应添加到.gitignore 中,避免将环境配置提交到代码库
+3. 如果同时存在系统环境变量和.env 文件,.env 文件的优先级更高
+
+#### Windows 系统
+
+1. 临时设置(仅当前命令行窗口有效):
+
+```cmd
+:: 开发环境
+set APP_ENV=dev
+
+:: 生产环境
+set APP_ENV=prod
 ```
 
-## 已实现功能
+2. 永久设置:
+
+- 右键点击"此电脑" -> "属性" -> "高级系统设置"
+- 点击"环境变量"按钮
+- 在"系统变量"或"用户变量"中新建变量:
+  - 变量名:APP_ENV
+  - 变量值:dev(开发环境)或 prod(生产环境)
 
-### 1. 图形用户界面
+#### macOS/Linux 系统
 
-- 使用 PyQt5 开发桌面应用界面
-- 提供友好的用户交互体验
-- 支持题目展示、答题、结果查看等功能
+```bash
+# 开发环境
+export APP_ENV=dev
+
+# 生产环境
+export APP_ENV=prod
+```
 
-### 2. 在线题库更新
+### 各环境主要差异
 
-- 实现题库云端同步功能
-- 支持自动更新最新题目
-- 提供题目审核机制
+| 配置项               | 开发环境          | 生产环境            |
+| -------------------- | ----------------- | ------------------- |
+| app.debug            | true              | false               |
+| database.host        | localhost         | db.prod.example.com |
+| database.name        | question_bank_dev | question_bank_prod  |
+| logging.path         | logs              | /var/log/app        |
+| logging.level        | DEBUG             | INFO                |
+| logging.retention    | 7 天              | 30 天               |
+| logging.max_size     | 10MB              | 100MB               |
+| logging.backup_count | 5                 | 30                  |
 
-### 3. 做题时间限制
+## 日志配置说明
 
-- 支持按题型设置时间限制
-- 提供倒计时功能
-- 超时自动提交答案
+项目使用 loguru 库进行日志管理,主要特性包括:
 
-### 4. 班级管理
+- 开发环境:
 
-- 教师创建和管理班级
-- 学生加入班级
-- 班级练习统计和分析
+  - 日志级别:DEBUG
+  - 日志路径:logs/
+  - 日志保留:7 天
+  - 日志格式:包含时间、级别、文件名和行号
+  - 控制台输出:彩色格式
 
-### 5. 题目难度评估
+- 生产环境:
+  - 日志级别:INFO
+  - 日志路径:/var/log/app
+  - 日志保留:30 天
+  - 日志格式:仅包含时间、级别和消息
+  - 控制台输出:简洁格式
 
-- 基于学生答题数据自动评估题目难度
-- 动态调整题目难度系数
-- 提供难度分布可视化
+使用示例:
 
-### 6. 智能出题算法
+```python
+from app.logger import logger
 
-- 基于知识图谱的题目推荐
-- 个性化出题策略
-- 自适应难度调整
+logger.info("这是一条信息日志")
+logger.error("这是一条错误日志", exc_info=True)
+```

+ 331 - 0
README_DEV.md

@@ -0,0 +1,331 @@
+# 智能题库学习系统 (Smart Question Bank Learning System)
+
+## 项目简介
+
+这是一个基于 Python 开发的智能题库学习系统,旨在帮助学生进行高效的题目练习和知识掌握。系统具备题目管理、智能练习、错题本、学习进度追踪等功能,让学习更有针对性和效果。
+
+## 主要功能
+
+### 1. 题库管理
+
+- 支持多学科题目录入(数学、语文、英语等)
+- 题目分类管理(按照难度、知识点、题型等)
+- 支持多种题型(选择题、填空题、简答题等)
+- 每道题目包含详细解析和知识点说明
+- 题目标签系统,方便分类和搜索
+
+### 2. 智能练习
+
+- 根据学生水平自动推荐适合的题目
+- 支持自选题型和难度进行练习
+- 练习过程中即时查看答案和解析
+- 练习完成后显示得分和错题分析
+- 优化推荐算法,结合用户历史表现和学习目标
+- 个性化推荐,根据用户偏好推荐题目
+- 自适应练习模式,动态调整题目难度
+
+### 3. 错题本功能
+
+- 自动收集做错的题目
+- 错题分类整理
+- 支持错题重复练习
+- 错题数据分析,找出薄弱知识点
+- 错题原因分析,帮助用户理解错误原因
+- 错题标签,方便分类和复习
+- 错题相似题推荐,巩固薄弱知识点
+
+### 4. 学习进度追踪
+
+- 展示每日/每周/每月的练习情况
+- 知识点掌握程度分析
+- 学习时长统计
+- 成绩进步曲线
+- 知识点掌握预测,预估用户掌握程度
+- 学习路径规划,提供个性化学习建议
+- 学习目标设置,帮助用户明确学习方向
+- 手动考试成绩录入,记录各类考试成绩
+- 考试成绩分析,生成成绩报告
+
+## 技术架构
+
+- 后端:Python 3.13
+- 数据库:Mysql
+- 用户界面:Web 界面
+
+## 数据库设计
+
+### 用户表(sys_users)
+
+- id: 用户 ID
+- username: 用户名
+- password: 密码
+- grade: 年级
+- create_time: 创建时间
+- update_time: 更新时间
+- create_id: 创建者 ID
+- update_id: 更新者 ID
+- is_deleted: 是否删除 (0:未删除, 1:已删除)
+- delete_time: 删除时间
+- delete_id: 删除者 ID
+- preferences: 学习偏好
+- goals: 学习目标
+- learning_style: 学习风格
+- target_score: 目标分数
+
+### 系统角色表(sys_roles)
+
+- id: 角色 ID
+- name: 角色名称
+- code: 角色代码
+- description: 角色描述
+- status: 状态(0:禁用,1:启用)
+- create_id: 创建者 ID
+- create_time: 创建时间
+- update_id: 更新者 ID
+- update_time: 更新时间
+- is_deleted: 是否删除 (0:未删除, 1:已删除)
+- delete_time: 删除时间
+- delete_id: 删除者 ID
+
+### 系统用户角色表(sys_user_roles)
+
+- id: 主键 ID
+- user_id: 用户 ID
+- role_id: 角色 ID
+- create_time: 创建时间
+- create_id: 创建者 ID
+
+### 系统权限表(sys_permissions)
+
+- id: 权限 ID
+- code: 权限代码
+- score: 权限作用域 U: 用户,R: 角色
+- value: 权限值
+- description: 权限描述
+- create_time: 创建时间
+- create_id: 创建者 ID
+
+### 字典类型表(sys_dict_type)
+
+- id: 字典类型 ID
+- name: 字典名称
+- type: 字典类型
+- status: 状态(0:禁用,1:启用)
+- remark: 备注
+- create_id: 创建者 ID
+- create_time: 创建时间
+- update_id: 更新者 ID
+- update_time: 更新时间
+- is_deleted: 是否删除 (0:未删除, 1:已删除)
+- delete_time: 删除时间
+- delete_id: 删除者 ID
+
+### 字典数据表(sys_dict_data)
+
+- id: 字典数据 ID
+- dict_type: 字典类型
+- dict_label: 字典标签
+- dict_value: 字典值
+- sort: 排序
+- status: 状态(0:禁用,1:启用)
+- remark: 备注
+- create_id: 创建者 ID
+- create_time: 创建时间
+- update_id: 更新者 ID
+- update_time: 更新时间
+- is_deleted: 是否删除 (0:未删除, 1:已删除)
+- delete_time: 删除时间
+- delete_id: 删除者 ID
+
+### 题目表(questions)
+
+- id: 题目 ID
+- subject: 学科
+- type: 题型
+- difficulty: 难度等级
+- content: 题目内容
+- answer: 答案
+- explanation: 解析
+- knowledge_points: 相关知识点
+- version: 题目版本
+- status: 审核状态
+- tags: 题目标签
+- creator_id: 题目创建者
+- reviewer_id: 题目审核者
+- create_time: 创建时间
+- update_time: 更新时间
+- create_id: 创建者 ID
+- update_id: 更新者 ID
+- is_deleted: 是否删除 (0:未删除, 1:已删除)
+- delete_time: 删除时间
+- delete_id: 删除者 ID
+
+### 练习记录表(practice_records)
+
+- id: 记录 ID
+- user_id: 用户 ID
+- question_id: 题目 ID
+- is_correct: 是否正确
+- practice_time: 练习时间
+- time_spent: 用时
+- steps: 答题步骤
+- thinking_time: 思考时间
+- confidence_level: 答题信心
+- feedback: 用户反馈
+- create_id: 创建者 ID
+- create_time: 创建时间
+- update_id: 更新者 ID
+- update_time: 更新时间
+- is_deleted: 是否删除 (0:未删除, 1:已删除)
+- delete_time: 删除时间
+- delete_id: 删除者 ID
+
+### 知识点表(knowledge_points)
+
+- id: 知识点 ID
+- name: 知识点名称
+- description: 知识点描述
+- subject: 所属学科
+- difficulty: 难度等级
+- prerequisite: 先修知识点
+- create_id: 创建者 ID
+- create_time: 创建时间
+- update_id: 更新者 ID
+- update_time: 更新时间
+- is_deleted: 是否删除 (0:未删除, 1:已删除)
+- delete_time: 删除时间
+- delete_id: 删除者 ID
+
+### 学习路径表(learning_paths)
+
+- id: 路径 ID
+- user_id: 用户 ID
+- target_knowledge_point: 目标知识点
+- path: 学习路径
+- progress: 当前进度
+- create_id: 创建者 ID
+- create_time: 创建时间
+- update_id: 更新者 ID
+- update_time: 更新时间
+- is_deleted: 是否删除 (0:未删除, 1:已删除)
+- delete_time: 删除时间
+- delete_id: 删除者 ID
+
+### 考试成绩表(exam_scores)
+
+- id: 成绩 ID
+- user_id: 用户 ID
+- exam_name: 考试名称
+- subject: 考试科目
+- score: 考试成绩
+- total_score: 总分
+- exam_date: 考试日期
+- remark: 备注
+- create_time: 创建时间
+- update_time: 更新时间
+- create_id: 创建者 ID
+- update_id: 更新者 ID
+- is_deleted: 是否删除 (0:未删除, 1:已删除)
+- delete_time: 删除时间
+- delete_id: 删除者 ID
+
+## 使用说明
+
+1. 系统启动后,首先需要登录账号,账号由管理员派发
+2. 选择想要练习的学科和题型
+3. 开始答题,系统会记录答题情况
+4. 可以随时查看错题本和学习进度
+5. 在成绩管理页面手动录入考试成绩
+
+## 项目结构
+
+```
+smart-question-bank/
+├── SERVER/               # 后端服务
+│   ├── app/              # APP目录
+│   │   ├── common/       # 公共模块
+│   │   ├── exception/    # 异常处理
+│   │   ├── models/       # 数据模型
+│   │   ├── stores/       # 数据持久层
+│   │   ├── services/     # 业务逻辑
+│   │   ├── controllers/  # API控制器
+│   │   ├── middleware/   # 中间件
+│   │   ├── utils/        # 工具类
+│   │   ├── config/       # 配置管理
+│   │   ├── logger/       # 日志管理
+│   │   ├── data_seeder/  # 数据初始化
+│   │   └── main.py       # 程序入口
+│   ├── tests/            # 测试目录
+│   ├── requirements.txt  # 依赖包列表
+│   └── Dockerfile        # Docker配置
+├── UI/                   # 前端服务
+│   ├── vue/              # Web项目
+│   │   ├── public/       #
+│   │   ├── src/          #
+│   │   └── Dockerfile    # Docker配置
+│   └── app/              # 移动端项目
+│       └── src/          #
+├── docker-compose.yml    # Docker Compose配置
+├── README.md             # 项目说明
+└── .gitignore            # Git忽略配置
+```
+
+调用顺序:
+controllers => services => stores => models
+
+## 安装与运行
+
+1. 安装依赖
+
+```bash
+pip install -r requirements.txt
+```
+
+2. 配置数据库
+
+- 创建 MySQL 数据库
+- 修改 src/common/config.py 中的数据库连接配置
+
+3. 运行系统
+
+```bash
+python src/main.py
+```
+
+## 已实现功能
+
+### 1. 图形用户界面
+
+- 使用 PyQt5 开发桌面应用界面
+- 提供友好的用户交互体验
+- 支持题目展示、答题、结果查看等功能
+
+### 2. 在线题库更新
+
+- 实现题库云端同步功能
+- 支持自动更新最新题目
+- 提供题目审核机制
+
+### 3. 做题时间限制
+
+- 支持按题型设置时间限制
+- 提供倒计时功能
+- 超时自动提交答案
+
+### 4. 班级管理
+
+- 教师创建和管理班级
+- 学生加入班级
+- 班级练习统计和分析
+
+### 5. 题目难度评估
+
+- 基于学生答题数据自动评估题目难度
+- 动态调整题目难度系数
+- 提供难度分布可视化
+
+### 6. 智能出题算法
+
+- 基于知识图谱的题目推荐
+- 个性化出题策略
+- 自适应难度调整

+ 3 - 0
SERVER/app/__init__.py

@@ -0,0 +1,3 @@
+"""
+app包初始化文件
+"""

+ 20 - 0
SERVER/app/application-dev.yml

@@ -0,0 +1,20 @@
+app:
+  name: Smart Question Bank
+  version: 1.0.1
+  debug: true
+
+database:
+  host: 192.168.0.82
+  port: 3306
+  user: root
+  password: ''
+  name: question_bank_dev
+
+logging:
+  path: logs
+  retention: 7 # 开发环境保留7天日志
+  level: DEBUG # 开发环境记录所有日志
+  format: '{time:YYYY-MM-DD HH:mm:ss.SSS} | {level} | {file}:{line} | {message}'
+  console_format: '<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | <level>{level}</level> | <cyan>{file}:{line}</cyan> | {message}'
+  max_size: 10485760
+  backup_count: 5

+ 20 - 0
SERVER/app/application-prod.yml

@@ -0,0 +1,20 @@
+app:
+  name: Smart Question Bank
+  version: 1.0.0
+  debug: false
+
+database:
+  host: db.prod.example.com
+  port: 3306
+  user: prod_user
+  password: '${DB_PASSWORD}' # 从环境变量读取
+  name: question_bank_prod
+
+logging:
+  path: /var/log/app
+  retention: 30 # 生产环境保留30天日志
+  level: INFO # 生产环境只记录重要日志
+  format: '{time:YYYY-MM-DD HH:mm:ss.SSS} | {level} | {message}'
+  console_format: '{time:YYYY-MM-DD HH:mm:ss.SSS} | {level} | {message}'
+  max_size: 104857600 # 生产环境日志文件更大
+  backup_count: 30 # 生产环境保留更多备份

+ 20 - 0
SERVER/app/application.yml

@@ -0,0 +1,20 @@
+app:
+  name: Smart Question Bank
+  version: 1.0.0
+  debug: false
+
+database:
+  host: 192.168.0.81
+  port: 3306
+  user: root
+  password: ''
+  name: question_bank
+
+logging:
+  level: INFO
+  file: app.log
+  max_size: 10485760
+  backup_count: 5
+  path: logs
+  format: '{time:YYYY-MM-DD HH:mm:ss.SSS} | {level} | {module}:{line} - {message}'
+  console_format: '<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | <level>{level}</level> | <cyan>{module}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>'

+ 69 - 0
SERVER/app/config/__init__.py

@@ -0,0 +1,69 @@
+from typing import Any, Callable
+from .config import Config
+from .loader_config import ConfigLoader
+"""配置模块初始化文件
+
+该模块负责初始化配置加载器,并提供获取配置的接口。
+"""
+
+# 创建全局配置加载器实例
+config_loader = ConfigLoader()
+
+
+def get_config() -> Config:
+    """获取配置实例
+    
+    Returns:
+        Config: 配置实例
+        
+    Raises:
+        RuntimeError: 配置加载失败时抛出
+    """
+    try:
+        return config_loader._config
+    except Exception as e:
+        raise RuntimeError("获取配置失败") from e
+
+
+# 暴露配置实例
+config: Config = get_config()
+
+
+def get_config_value(key: str, default=None) -> Any:
+    """获取配置值
+    
+    Args:
+        key (str): 配置项key
+        default (Any): 默认值
+        
+    Returns:
+        Any: 配置项值
+        
+    Raises:
+        RuntimeError: 配置加载失败时抛出
+    """
+    try:
+        return config_loader.get(key, default)
+    except Exception as e:
+        raise RuntimeError(f"获取配置项{key}失败") from e
+
+
+def on_config_change(callback: Callable):
+    """注册配置变更回调
+    
+    Args:
+        callback (Callable): 配置变更回调函数
+        
+    Raises:
+        RuntimeError: 回调注册失败时抛出
+    """
+    try:
+        config_loader.on_config_change(callback)
+    except Exception as e:
+        raise RuntimeError("注册配置变更回调失败") from e
+
+
+__all__ = [
+    'Config', 'ConfigLoader', 'config', 'get_config', 'get_config_value',
+    'on_config_change'
+]

+ 72 - 0
SERVER/app/config/config.py

@@ -0,0 +1,72 @@
+from pydantic_settings import BaseSettings
+from .config_app import AppConfig
+from .config_database import DatabaseConfig
+from .config_logging import LoggingConfig
+
+
+class Config(BaseSettings):
+    app: AppConfig = AppConfig()
+    database: DatabaseConfig = DatabaseConfig()
+    logging: LoggingConfig = LoggingConfig()
+
+    class Config:
+        env_prefix = ''
+        env_nested_delimiter = '__'
+        case_sensitive = False
+
+    def __init__(self, **data):
+        super().__init__(**data)
+        # 确保logging配置被正确初始化
+        if not hasattr(self, 'logging'):
+            self.logging = LoggingConfig()
+
+    def to_string(self, indent: int = 0) -> str:
+        """将配置转换为字符串"""
+        indent_str = ' ' * indent
+        result = []
+        for field_name, field_value in self.__dict__.items():
+            if hasattr(field_value, 'to_string'):
+                result.append(
+                    f"{indent_str}{field_name}:\n{field_value.to_string(indent + 4)}"
+                )
+            else:
+                result.append(f"{indent_str}{field_name}: {field_value}")
+        return '\n'.join(result)
+
+    def _update_config_section(self, config_dict: dict, section: str,
+                               config_class: type) -> None:
+        """
+        更新配置的某个部分
+        
+        Args:
+            config_dict: 包含配置项的字典
+            section: 配置部分名称
+            config_class: 配置类
+        """
+        if section in config_dict:
+            setattr(
+                self, section,
+                config_class(
+                    **{
+                        k: v
+                        for k, v in config_dict[section].items()
+                        if k in config_class.model_fields
+                    }))
+
+    def update_from_dict(self,
+                         config_dict: dict,
+                         partial: bool = False) -> None:
+        """
+        从字典更新配置
+        
+        Args:
+            config_dict: 包含配置项的字典
+            partial: 是否部分更新,默认为False
+        """
+        try:
+            self._update_config_section(config_dict, 'app', AppConfig)
+            self._update_config_section(config_dict, 'database',
+                                        DatabaseConfig)
+            self._update_config_section(config_dict, 'logging', LoggingConfig)
+        except Exception as e:
+            raise ValueError(f"配置更新失败: {e}")

+ 30 - 0
SERVER/app/config/config_app.py

@@ -0,0 +1,30 @@
+from pydantic_settings import BaseSettings
+from pydantic import field_validator
+import re
+
+
+class AppConfig(BaseSettings):
+    """应用基础配置"""
+    name: str = "Smart Question Bank"
+    version: str = "1.0.0"
+    debug: bool = False
+
+    @field_validator('name')
+    def validate_name(cls, value):
+        if not value or len(value) < 2:
+            raise ValueError('应用名称不能为空且至少2个字符')
+        return value
+
+    @field_validator('version')
+    def validate_version(cls, value):
+        if not re.match(r'^\d+\.\d+\.\d+$', value):
+            raise ValueError('版本号格式必须为x.x.x')
+        return value
+
+    def to_string(self, indent: int = 0) -> str:
+        """将配置转换为字符串"""
+        indent_str = ' ' * indent
+        result = []
+        for field_name, field_value in self.__dict__.items():
+            result.append(f"{indent_str}{field_name}: {field_value}")
+        return '\n'.join(result)

+ 31 - 0
SERVER/app/config/config_database.py

@@ -0,0 +1,31 @@
+from pydantic_settings import BaseSettings
+from pydantic import field_validator
+
+
+class DatabaseConfig(BaseSettings):
+    """数据库配置"""
+    host: str = "localhost"
+    port: int = 3306
+    user: str = "root"
+    password: str = ""
+    name: str = "question_bank"
+
+    @field_validator('port')
+    def validate_db_port(cls, value):
+        if value < 1024 or value > 65535:
+            raise ValueError('数据库端口必须在1024-65535之间')
+        return value
+
+    @field_validator('name')
+    def validate_db_name(cls, value):
+        if not value or len(value) < 2:
+            raise ValueError('数据库名称不能为空且至少2个字符')
+        return value
+
+    def to_string(self, indent: int = 0) -> str:
+        """将配置转换为字符串"""
+        indent_str = ' ' * indent
+        result = []
+        for field_name, field_value in self.__dict__.items():
+            result.append(f"{indent_str}{field_name}: {field_value}")
+        return '\n'.join(result)

+ 34 - 0
SERVER/app/config/config_logging.py

@@ -0,0 +1,34 @@
+from pydantic_settings import BaseSettings
+from pydantic import field_validator
+
+
+class LoggingConfig(BaseSettings):
+    """日志配置"""
+    level: str = "INFO"
+    file: str = "app.log"
+    path: str = "logs"
+    max_size: int = 10485760  # 10MB
+    backup_count: int = 5
+    format: str = "{time:YYYY-MM-DD HH:mm:ss.SSS} | {level} | {module}:{line} - {message}"
+    console_format: str = "<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | <level>{level}</level> | <cyan>{module}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>"
+
+    @field_validator('level')
+    def validate_log_level(cls, value):
+        valid_levels = ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']
+        if value not in valid_levels:
+            raise ValueError(f'日志级别必须是: {", ".join(valid_levels)}')
+        return value
+
+    @field_validator('max_size')
+    def validate_max_log_size(cls, value):
+        if value < 1048576:  # 1MB
+            raise ValueError('日志文件最大大小至少为1MB')
+        return value
+
+    def to_string(self, indent: int = 0) -> str:
+        """将配置转换为字符串"""
+        indent_str = ' ' * indent
+        result = []
+        for field_name, field_value in self.__dict__.items():
+            result.append(f"{indent_str}{field_name}: {field_value}")
+        return '\n'.join(result)

+ 205 - 0
SERVER/app/config/loader_config.py

@@ -0,0 +1,205 @@
+import os, yaml, datetime
+from typing import Any, List, Callable
+from watchdog.observers import Observer
+from watchdog.events import FileSystemEventHandler
+from .config import Config
+
+
+class ConfigReloadHandler(FileSystemEventHandler):
+    """配置文件变更处理器"""
+
+    def __init__(self, callback: Callable):
+        self.callback = callback
+
+    def on_modified(self, event):
+        if not event.is_directory:
+            self.callback()
+
+
+class ConfigLoader:
+    """配置加载器"""
+    _instance = None
+    _observers = []
+
+    # 环境变量映射配置
+    ENV_MAPPING = {
+        'APP_NAME': 'app.name',
+        'APP_VERSION': 'app.version',
+        'APP_DEBUG': 'app.debug',
+        'DB_HOST': 'database.host',
+        'DB_PORT': 'database.port',
+        'DB_USER': 'database.user',
+        'DB_PASSWORD': 'database.password',
+        'DB_NAME': 'database.name',
+    }
+
+    def __new__(cls):
+        if cls._instance is None:
+            cls._instance = super().__new__(cls)
+            cls._instance._load_config()
+        return cls._instance
+
+    def _load_env_vars(self, config: Config) -> Config:
+        """加载环境变量配置"""
+        for env_key, config_path in self.ENV_MAPPING.items():
+            value = os.getenv(env_key)
+            if value is None:
+                continue
+
+            try:
+                # 按路径分割
+                keys = config_path.split('.')
+                current = config
+
+                # 遍历配置路径
+                for key in keys[:-1]:
+                    if not hasattr(current, key):
+                        setattr(current, key, Config())
+                    current = getattr(current, key)
+
+                # 获取目标字段类型
+                last_key = keys[-1]
+                target_type = type(getattr(current, last_key, str))
+
+                # 根据目标类型转换值
+                if target_type == bool:
+                    # 支持多种布尔值表示
+                    value_lower = value.lower()
+                    if value_lower in ('true', 'yes', 'y', '1'):
+                        setattr(current, last_key, True)
+                    elif value_lower in ('false', 'no', 'n', '0'):
+                        setattr(current, last_key, False)
+                    else:
+                        raise ValueError(f"环境变量 {env_key} 的值 {value} 不是有效的布尔值")
+                elif target_type == int:
+                    # 支持数字字符串
+                    try:
+                        setattr(current, last_key, int(float(value)))
+                    except (ValueError, TypeError):
+                        raise ValueError(f"环境变量 {env_key} 的值 {value} 不是有效的整数")
+                elif target_type == float:
+                    # 支持浮点数字符串
+                    try:
+                        setattr(current, last_key, float(value))
+                    except (ValueError, TypeError):
+                        raise ValueError(f"环境变量 {env_key} 的值 {value} 不是有效的浮点数")
+                elif target_type == list:
+                    # 支持数组类型,格式:value1,value2,value3
+                    try:
+                        setattr(current, last_key,
+                                [item.strip() for item in value.split(',')])
+                    except Exception:
+                        raise ValueError(
+                            f"环境变量 {env_key} 的值 {value} 不是有效的数组格式")
+                elif target_type == set:
+                    # 支持集合类型,格式:value1,value2,value3
+                    try:
+                        setattr(current, last_key,
+                                set(item.strip() for item in value.split(',')))
+                    except Exception:
+                        raise ValueError(
+                            f"环境变量 {env_key} 的值 {value} 不是有效的集合格式")
+                elif target_type == dict:
+                    # 支持字典类型,格式:key1:value1,key2:value2
+                    try:
+                        setattr(
+                            current, last_key,
+                            dict(item.split(':') for item in value.split(',')))
+                    except Exception:
+                        raise ValueError(
+                            f"环境变量 {env_key} 的值 {value} 不是有效的字典格式")
+                elif target_type == datetime.datetime:
+                    # 支持时间类型,格式:YYYY-MM-DD HH:MM:SS
+                    try:
+                        setattr(
+                            current, last_key,
+                            datetime.datetime.strptime(value,
+                                                       '%Y-%m-%d %H:%M:%S'))
+                    except Exception:
+                        raise ValueError(
+                            f"环境变量 {env_key} 的值 {value} 不是有效的时间格式")
+                else:
+                    setattr(current, last_key, value)
+
+            except Exception as e:
+                print(f"[ERROR] 加载环境变量 {env_key} 时出错: {str(e)}")
+                continue
+
+        return config
+
+    def _load_yaml_config(self, file_path: str) -> dict:
+        """加载YAML配置文件"""
+        try:
+            if os.path.exists(file_path):
+                with open(file_path, 'r', encoding='utf-8') as f:
+                    config = yaml.safe_load(f) or {}
+                    if not isinstance(config, dict):
+                        raise ValueError(f"配置文件 {file_path} 格式错误:应为字典格式")
+                    return config
+            else:
+                print(f"配置文件 {file_path} 不存在")
+            return {}
+        except yaml.YAMLError as e:
+            print(f"YAML解析错误:{str(e)}")
+            return {}
+        except Exception as e:
+            print(f"加载配置文件 {file_path} 时发生错误:{str(e)}")
+            return {}
+
+    def _load_config(self):
+        """加载配置"""
+        # 初始化配置实例
+        self._config = Config()
+        self._callbacks: List[Callable] = []
+
+        # 1. 加载默认值(通过Config类初始化)
+
+        # 2. 加载通用配置
+        self._config.update_from_dict(
+            self._load_yaml_config('SERVER/app/application.yml'))
+
+        # 3. 加载指定环境配置
+        env = os.getenv('ENV', 'dev')
+        self._config.update_from_dict(
+            self._load_yaml_config(f'SERVER/app/application-{env}.yml'))
+
+        # 4. 最后加载环境变量(最高优先级)
+        self._config = self._load_env_vars(self._config)
+
+        # 启动配置监控
+        self._start_config_watcher()
+
+    def _start_config_watcher(self):
+        """启动配置文件监控"""
+        event_handler = ConfigReloadHandler(self.reload_config)
+        observer = Observer()
+        observer.schedule(event_handler, path='SERVER/app/', recursive=False)
+        observer.start()
+        self._observers.append(observer)
+
+    def reload_config(self):
+        """重新加载配置"""
+        self._load_config()
+        for callback in self._callbacks:
+            callback()
+
+    def get(self, key: str, default=None) -> Any:
+        """获取配置值"""
+        keys = key.split('.')
+        current = self._config
+        try:
+            for k in keys:
+                current = getattr(current, k)
+            return current
+        except (KeyError, AttributeError):
+            return default
+
+    def on_config_change(self, callback: Callable):
+        """注册配置变更回调"""
+        self._callbacks.append(callback)
+
+    def __del__(self):
+        """清理资源"""
+        for observer in self._observers:
+            observer.stop()
+            observer.join()

+ 5 - 0
SERVER/app/logger/__init__.py

@@ -0,0 +1,5 @@
+"""日志模块初始化文件"""
+
+from .logging import logger
+
+__all__ = ["logger"]

+ 62 - 0
SERVER/app/logger/logging.py

@@ -0,0 +1,62 @@
+import sys
+from pathlib import Path
+from loguru import logger as _logger
+from ..config import config
+
+
+class Logger:
+    """单例日志类"""
+    _instance = None
+    _logger_id = None  # 用于存储logger id以便重新配置
+
+    def __new__(cls):
+        if cls._instance is None:
+            cls._instance = super().__new__(cls)
+            cls._instance._initialized = False
+            cls._instance._logger = _logger
+            cls._instance._setup()
+        return cls._instance
+
+    def _remove_existing_handlers(self):
+        """移除现有的日志处理器"""
+        if self._logger_id is not None:
+            self._logger.remove(self._logger_id)
+            self._logger_id = None
+
+    def _setup(self):
+        """私有方法:初始化日志配置"""
+        try:
+            # 移除所有默认处理器
+            self._logger.remove()
+            self._remove_existing_handlers()
+
+            # 创建日志目录
+            log_dir = Path(config.logging.path)
+            log_dir.mkdir(parents=True, exist_ok=True)
+            log_file = log_dir / config.logging.file
+
+            # 配置日志
+            self._logger_id = self._logger.add(
+                log_file,
+                rotation="1 day",  # 每天滚动日志
+                retention=f"{config.logging.backup_count} days",
+                level=config.logging.level,
+                format=config.logging.format,
+                encoding="utf-8",
+                enqueue=True,  # 线程安全
+                compression="zip"  # 压缩旧日志
+            )
+
+            # 使用用户配置的控制台输出
+            if config.logging.console_format:
+                self._logger.add(sys.stdout,
+                                 level=config.logging.level,
+                                 format=config.logging.console_format)
+
+        except Exception as e:
+            self._logger.error(f"日志配置初始化失败: {str(e)}")
+            raise
+
+
+# 创建全局单例实例
+logger = Logger()._logger

+ 22 - 0
SERVER/app/main.py

@@ -0,0 +1,22 @@
+import sys
+import os
+
+# 添加项目根目录到Python路径
+sys.path.append(
+    os.path.dirname(os.path.dirname(os.path.dirname(
+        os.path.abspath(__file__)))))
+
+from SERVER.app.config import config
+from SERVER.app.logger import logger
+
+
+def main():
+    logger.info(f"正在启动应用 [{config.app.name}]")
+    logger.debug(f"调试模式: {config.app.debug}")
+
+    # logger.info(f"加载配置文件:\n{config.to_string()}")
+    pass
+
+
+if __name__ == "__main__":
+    main()

+ 7 - 0
application-dev.yml

@@ -0,0 +1,7 @@
+debug: true
+database:
+  uri: mysql://localhost:3306/dev_db
+  pool_size: 10
+api:
+  url: http://localhost:8000
+  timeout: 60

+ 6 - 0
requirements.txt

@@ -0,0 +1,6 @@
+PyYAML>=6.0.1
+pydantic>=2.0.0
+pydantic-settings>=2.0.0
+watchdog>=3.0.0
+loguru>=0.7.0
+