# ExamService 题库小程序服务端 - FastAPI实现 ## 项目结构 ``` ExamService/ ├── alembic/ # 数据库迁移相关 ├── mooc/ # 主应用目录 │ ├── api/ # API路由 │ │ └── v1/ # API v1版本 │ ├── core/ # 核心配置 │ ├── crud/ # 数据库操作 │ ├── db/ # 数据库 │ ├── models/ # 数据库模型 │ ├── schemas/ # Pydantic模型 │ └── utils/ # 工具函数 ├── tests/ # 测试目录 └── [配置文件] ``` ## 安装 ### 1. 安装 Miniconda 在 Ubuntu 上安装 Miniconda,执行以下步骤: ```bash # 下载 Miniconda 安装脚本 wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh # 给安装脚本添加执行权限 chmod +x Miniconda3-latest-Linux-x86_64.sh # 运行安装脚本 ./Miniconda3-latest-Linux-x86_64.sh # 按照提示完成安装,选择安装目录并接受许可协议 # 激活 Miniconda source ~/.bashrc # 创建名为 'mooc' 的虚拟环境,Python 版本大于等于 3.13 conda create --name mooc python>=3.13 # 激活 'mooc' 环境 conda activate mooc # 安装依赖 pip install -r requirements.txt ``` ### 安装mysql开发库 ```bash # 更新包列表 apt-get update # 安装 MySQL 开发包和其他必要的包 apt-get install -y python3-dev default-libmysqlclient-dev build-essential pkg-config pip install mysqlclient pymysql cryptography ``` ## 运行 ```bash uvicorn main:app --reload ``` ## 初始化数据库 ```bash alembic init alembic alembic revision --autogenerate -m "Initial migration" alembic upgrade head ``` ## 数据库管理 ### 添加新的数据库表 1. 创建模型文件: 将同类型的表模型放在同一个文件中。文件命名规则:去除表名的 `ims_` 前缀,例如: - `ims_account_wechats`、`ims_account_wxapp` 等表 → `account.py` - `ims_uni_account`、`ims_uni_settings` 等表 → `uni_account.py` ```python # filepath: mooc/models/account.py from sqlalchemy import Column, Integer, String, SmallInteger from mooc.db.database import Base class AccountWechats(Base): """微信公众号账号表""" __tablename__ = "ims_account_wechats" acid = Column(Integer, primary_key=True) uniacid = Column(Integer, nullable=False) # ... 其他字段 ``` 2. 在 `mooc/models/__init__.py` 中注册表: ```python # 添加到 expected_tables 集合中 expected_tables = { # ... 现有表 ... 'ims_account_wechats', # 新添加的表 } ``` 3. 导出模型类: ```python # 在 mooc/models/__init__.py 中添加导出 from mooc.models.account import AccountWechats ``` 4. 创建 Schema: ```python # filepath: mooc/schemas/account.py from pydantic import BaseModel class AccountWechatsBase(BaseModel): uniacid: int # ... 其他字段 class AccountWechatsCreate(AccountWechatsBase): pass class AccountWechatsUpdate(BaseModel): # 可选字段用于更新 uniacid: Optional[int] = None class AccountWechatsRead(AccountWechatsBase): acid: int class Config: from_attributes = True ``` 5. 创建 CRUD 操作: ```python # filepath: mooc/crud/crud_account.py from mooc.crud.crud_base import CRUDBase class CRUDAccountWechats(CRUDBase[AccountWechats, AccountWechatsCreate, AccountWechatsUpdate]): def get_by_uniacid(self, db: Session, *, uniacid: int) -> Optional[AccountWechats]: return self.get_by_field(db, "uniacid", uniacid) # 创建实例 account_wechats = CRUDAccountWechats(AccountWechats) ``` 6. 添加 API 路由: ```python # filepath: mooc/api/v1/endpoints/account.py @account_router.post("/wechats", response_model=AccountWechatsRead) def create_wechat_account( *, db: Session = Depends(deps.get_db), account_in: AccountWechatsCreate, ): """创建微信公众号账号""" return account_wechats.create(db=db, obj_in=account_in) ``` ### 数据库表自动创建 应用启动时会自动执行以下操作: 1. 导入并验证所有模型定义 2. 检查数据库中缺失的表 3. 自动创建缺失的表 你可以通过查看应用日志了解表创建的详细情况: ```bash python main.py ``` ### 注意事项 1. 模型类名采用帕斯卡命名法(PascalCase),去除 `ims_` 前缀 2. 表名(`__tablename__`)必须与数据库中的实际表名完全一致 3. 字段类型要与数据库定义严格匹配 4. 确保主键和索引的正确定义 5. 添加适当的文档字符串说明表的用途 ### 调试帮助 如果遇到问题,可以: 1. 检查日志输出了解具体错误 2. 使用 `get_all_table_names()` 查看已注册的表 3. 验证模型定义是否完整 4. 确认数据库连接配置是否正确 ## CRUDBase 使用指南 ### 简介 CRUDBase是一个通用的CRUD基类,提供了基础的增删改查操作。通过继承这个基类,可以快速实现特定模型的CRUD操作。 ### 使用方法 1. 创建CRUD类: ```python from mooc.crud.base import CRUDBase from mooc.models.your_model import YourModel from mooc.schemas.your_schema import YourCreateSchema, YourUpdateSchema class CRUDYourModel(CRUDBase[YourModel, YourCreateSchema, YourUpdateSchema]): pass # 继承基础功能即可,如需自定义方法可以在这里添加 ``` 2. 实例化CRUD对象: ```python crud_your_model = CRUDYourModel(YourModel) ``` ### 基础操作示例 ```python # 创建记录 new_item = crud_your_model.create(db, obj_in=item_create_schema) # 获取单条记录 item = crud_your_model.get(db, id=123) # 获取多条记录 items = crud_your_model.get_multi(db, skip=0, limit=100) # 更新记录 updated_item = crud_your_model.update(db, db_obj=existing_item, obj_in=item_update_schema) # 删除记录 crud_your_model.remove(db, id=123) ``` ### 自定义方法示例 ```python class CRUDYourModel(CRUDBase[YourModel, YourCreateSchema, YourUpdateSchema]): def get_by_custom_field(self, db: Session, field_value: str) -> Optional[YourModel]: return db.query(self.model).filter(self.model.custom_field == field_value).first() ``` ### 注意事项 1. CRUDBase需要三个类型参数: - ModelType: SQLAlchemy模型类 - CreateSchemaType: Pydantic创建模型 - UpdateSchemaType: Pydantic更新模型 2. 所有方法都需要传入数据库会话(db: Session)参数 3. 更新操作支持两种方式: - 字典形式:`update(db, db_obj=item, obj_in={"field": "new_value"})` - Pydantic模型:`update(db, db_obj=item, obj_in=ItemUpdate(field="new_value"))` ## API文档 启动服务后访问: http://localhost:2333/docs ### 使用 AI 生成模型代码 当你有一个新的数据库表需要添加时,可以使用以下方式让 AI 帮助生成代码: 1. 准备 SQL 建表语句,例如: ```sql CREATE TABLE `ims_uni_settings` ( `uniacid` int(10) UNSIGNED NOT NULL, `passport` varchar(200) NOT NULL, `oauth` varchar(100) NOT NULL, -- ... 其他字段 PRIMARY KEY (`uniacid`) ) ENGINE = MyISAM CHARACTER SET = utf8 COLLATE = utf8_general_ci; ``` 2. 使用以下提示词模板: ``` 请帮我为这个表生成 FastAPI + SQLAlchemy 的实现,包括: 1. SQLAlchemy 模型(models/xxx.py) 2. Pydantic Schema(schemas/xxx.py) 3. CRUD 操作(crud/crud_xxx.py) 4. API 路由(api/v1/endpoints/xxx.py) 要求: - 遵循项目的命名规范(去除ims_前缀,使用帕斯卡命名法) - 确保字段类型映射正确 - 包含必要的文档注释 - 实现基本的 CRUD 操作和常用查询方法 [SQL建表语句] ``` 3. 对生成的代码进行以下检查: - 表名是否与数据库一致 - 字段类型是否正确映射 - 主键和索引是否正确定义 - 是否符合项目的命名规范 - CRUD 方法是否满足业务需求 4. 常用的 SQL 类型映射参考: ```python # MySQL 到 SQLAlchemy 的类型映射 int/bigint -> Integer varchar/char -> String(length) text -> Text datetime -> DateTime timestamp -> TIMESTAMP tinyint -> SmallInteger decimal -> DECIMAL(precision, scale) ``` 5. 示例提示词: ``` 我需要为以下数据表生成 FastAPI 实现: - 表名:ims_uni_settings - 主键:uniacid - 特殊需求: * 需要通过 uniacid 查询的方法 * 需要处理 Text 类型的 payment 字段 * 所有字符串字段都是必填的 [SQL语句] ... 请生成完整的代码实现。 ``` 6. 代码生成后的集成步骤: - 将模型类添加到对应的 models 文件 - 在 models/__init__.py 中注册表名 - 在 api.py 中注册新的路由 - 检查并测试生成的 API 端点 这种方式可以: - 快速生成符合项目规范的代码 - 确保类型映射的准确性 - 保持代码结构的一致性 - 减少手动编码的错误