2024-12-31 22:27:04 +08:00
|
|
|
|
# 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_account_wechats、ims_account_wxapp、ims_account_xzapp等表格去除前缀和后缀,即为account.py**
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
# 这个模型类AccountWechats用于映射ims_account_wechats表:
|
|
|
|
|
# 1. 在ORM层中代表数据库表结构,方便CRUD操作。
|
|
|
|
|
# 2. 在FastAPI中引用该模型时,可以使用Pydantic自动转换ORM对象到响应模型。
|
|
|
|
|
# 3. 创建模型时需确保表名(__tablename__)与真实的数据库表名保持一致,否则查询和插入可能失败。
|
|
|
|
|
# 4. 字段类型要与数据库中定义的列类型严格匹配,避免出现不兼容或异常。
|
|
|
|
|
# 5. 通过Primary Key(acid)唯一标识记录,确保ORM能正确追踪和更新该对象。
|
|
|
|
|
# 6. config.orm_mode = True:允许直接把SQLAlchemy模型实例转换为Pydantic模型对象。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# filepath: ExamService/mooc/models/account.py
|
|
|
|
|
from sqlalchemy import Column, Integer, String, SmallInteger
|
|
|
|
|
from mooc.db.database import Base
|
|
|
|
|
# 类名命名规则: 去除ims前缀后采用 帕斯卡命名法,无连接符每个单词手写大写
|
|
|
|
|
# AccountWechats模型用于映射数据库表 ims_account_wechats
|
|
|
|
|
class AccountWechats(Base):
|
|
|
|
|
__tablename__ = "ims_account_wechats"
|
|
|
|
|
|
|
|
|
|
acid = Column(Integer, primary_key=True)
|
|
|
|
|
uniacid = Column(Integer, nullable=False)
|
|
|
|
|
token = Column(String(32), nullable=False)
|
|
|
|
|
encodingaeskey = Column(String(255), nullable=False)
|
|
|
|
|
level = Column(SmallInteger, nullable=False)
|
|
|
|
|
name = Column(String(30), nullable=False)
|
|
|
|
|
account = Column(String(30), nullable=False)
|
|
|
|
|
original = Column(String(50), nullable=False)
|
|
|
|
|
signature = Column(String(100), nullable=False)
|
|
|
|
|
country = Column(String(10), nullable=False)
|
|
|
|
|
province = Column(String(3), nullable=False)
|
|
|
|
|
city = Column(String(15), nullable=False)
|
|
|
|
|
username = Column(String(30), nullable=False)
|
|
|
|
|
password = Column(String(32), nullable=False)
|
|
|
|
|
lastupdate = Column(Integer, nullable=False)
|
|
|
|
|
key = Column(String(50), nullable=False)
|
|
|
|
|
secret = Column(String(50), nullable=False)
|
|
|
|
|
styleid = Column(Integer, nullable=False)
|
|
|
|
|
subscribeurl = Column(String(120), nullable=False)
|
|
|
|
|
auth_refresh_token = Column(String(255), nullable=False)
|
|
|
|
|
|
|
|
|
|
class Config:
|
2025-01-03 17:08:58 +08:00
|
|
|
|
from_attributes = True # 允许与Pydantic的ORM功能兼容,直接读取数据库模型对象
|
2024-12-31 22:27:04 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 2.创建schema:
|
|
|
|
|
```python
|
|
|
|
|
# filepath: ExamService/mooc/schemas/account.py
|
|
|
|
|
|
|
|
|
|
from pydantic import BaseModel
|
|
|
|
|
from typing import Optional
|
|
|
|
|
|
|
|
|
|
class AccountWechatsBase(BaseModel):
|
|
|
|
|
"""
|
|
|
|
|
数据模型基类: AccountWechatsBase
|
|
|
|
|
用于描述基础字段的类型、用途和注意点。
|
|
|
|
|
- 注意: 必填字段都在这里声明,每个字段都对应数据库中的一列。
|
|
|
|
|
"""
|
|
|
|
|
uniacid: int # 微信公众号/小程序的关联ID
|
|
|
|
|
token: str # 访问接口时使用的token
|
|
|
|
|
encodingaeskey: str # 用于消息加解密的AES密钥
|
|
|
|
|
level: int # 认证等级,如订阅号/服务号
|
|
|
|
|
name: str # 微信公众号名称
|
|
|
|
|
account: str # 公众号帐号,如微信号
|
|
|
|
|
original: str # 原始ID(通常以gh_开头)
|
|
|
|
|
signature: str # 公众号信息签名
|
|
|
|
|
country: str # 国家
|
|
|
|
|
province: str # 省份
|
|
|
|
|
city: str # 城市
|
|
|
|
|
username: str # 后台登录用户名
|
|
|
|
|
password: str # 后台登录密码
|
|
|
|
|
lastupdate: int # 最后一次更新的时间戳
|
|
|
|
|
key: str # 开发者Key
|
|
|
|
|
secret: str # 开发者Secret
|
|
|
|
|
styleid: int # 模板样式ID
|
|
|
|
|
subscribeurl: str # 订阅链接
|
|
|
|
|
auth_refresh_token: str # 用来刷新授权的token
|
|
|
|
|
|
|
|
|
|
class AccountWechatsCreate(AccountWechatsBase):
|
|
|
|
|
"""
|
|
|
|
|
用于创建新微信帐号记录:
|
|
|
|
|
- 继承自AccountWechatsBase, 不额外添加字段
|
|
|
|
|
- 仅表示此Schema专用于'创建'场景
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
# 用于创建新记录,包含所有必填字段
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
class AccountWechatsUpdate(BaseModel):
|
|
|
|
|
"""
|
|
|
|
|
用于更新已有微信帐号记录:
|
|
|
|
|
- 只包含可选字段,未在此处的内容将保持不变
|
|
|
|
|
- 注意: exclude_unset=True 可以避免更新空值
|
|
|
|
|
"""
|
|
|
|
|
token: Optional[str] # 可选更新token
|
|
|
|
|
encodingaeskey: Optional[str]# 可选更新AES key
|
|
|
|
|
|
|
|
|
|
class AccountWechats(AccountWechatsBase):
|
|
|
|
|
"""
|
|
|
|
|
表示完整的微信帐号记录:
|
|
|
|
|
- acid: 数据库主键ID
|
|
|
|
|
- 包含所有字段的最终模型,ORM转换时使用
|
|
|
|
|
"""
|
|
|
|
|
acid: int # 表中的主键ID
|
|
|
|
|
|
|
|
|
|
class Config:
|
2025-01-03 17:08:58 +08:00
|
|
|
|
from_attributes = True # 允许与ORM对象进行直接转换
|
2024-12-31 22:27:04 +08:00
|
|
|
|
```
|
|
|
|
|
|
2025-01-03 18:10:40 +08:00
|
|
|
|
### 3.创建 CRUD:
|
|
|
|
|
```python
|
2024-12-31 22:27:04 +08:00
|
|
|
|
# filepath: ExamService/mooc/crud/crud_account.py
|
|
|
|
|
from typing import Optional
|
2025-01-03 18:10:40 +08:00
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
|
from mooc.crud.crud_base import CRUDBase
|
2024-12-31 22:27:04 +08:00
|
|
|
|
from mooc.models.account import AccountWechats
|
|
|
|
|
from mooc.schemas.account import AccountWechatsCreate, AccountWechatsUpdate
|
|
|
|
|
|
2025-01-03 18:10:40 +08:00
|
|
|
|
class CRUDAccountWechats(CRUDBase[AccountWechats, AccountWechatsCreate, AccountWechatsUpdate]):
|
2024-12-31 22:27:04 +08:00
|
|
|
|
"""
|
2025-01-03 18:10:40 +08:00
|
|
|
|
AccountWechats的CRUD操作类
|
|
|
|
|
继承自CRUDBase基类,实现基础的增删改查功能
|
|
|
|
|
可以根据需要添加自定义的查询方法
|
2024-12-31 22:27:04 +08:00
|
|
|
|
"""
|
2025-01-03 18:10:40 +08:00
|
|
|
|
def get_by_uniacid(self, db: Session, *, uniacid: int) -> Optional[AccountWechats]:
|
2024-12-31 22:27:04 +08:00
|
|
|
|
"""
|
2025-01-03 18:10:40 +08:00
|
|
|
|
根据uniacid查询微信公众号账号
|
|
|
|
|
:param db: 数据库会话
|
|
|
|
|
:param uniacid: 统一公众号ID
|
|
|
|
|
:return: AccountWechats对象或None
|
2024-12-31 22:27:04 +08:00
|
|
|
|
"""
|
2025-01-03 18:10:40 +08:00
|
|
|
|
return db.query(self.model).filter(self.model.uniacid == uniacid).first()
|
2024-12-31 22:27:04 +08:00
|
|
|
|
|
2025-01-03 18:10:40 +08:00
|
|
|
|
# 实例化CRUD对象,方便在其他地方直接导入使用
|
|
|
|
|
account_wechats = CRUDAccountWechats(AccountWechats)
|
2024-12-31 22:27:04 +08:00
|
|
|
|
|
2025-01-03 18:10:40 +08:00
|
|
|
|
# 使用示例:
|
|
|
|
|
"""
|
|
|
|
|
# 创建新记录
|
|
|
|
|
new_account = account_wechats.create(db, obj_in=account_create_schema)
|
|
|
|
|
|
|
|
|
|
# 根据ID获取记录
|
|
|
|
|
account = account_wechats.get(db, id=1)
|
2024-12-31 22:27:04 +08:00
|
|
|
|
|
2025-01-03 18:10:40 +08:00
|
|
|
|
# 根据uniacid获取记录
|
|
|
|
|
account = account_wechats.get_by_uniacid(db, uniacid=100)
|
|
|
|
|
|
|
|
|
|
# 更新记录
|
|
|
|
|
updated = account_wechats.update(db, db_obj=existing_account, obj_in=update_data)
|
|
|
|
|
|
|
|
|
|
# 删除记录
|
|
|
|
|
account_wechats.remove(db, id=1)
|
|
|
|
|
"""
|
2024-12-31 22:27:04 +08:00
|
|
|
|
```
|
2025-01-03 18:10:40 +08:00
|
|
|
|
|
2024-12-31 22:27:04 +08:00
|
|
|
|
### 4.注册模型:
|
|
|
|
|
```python
|
|
|
|
|
# filepath: ExamService/mooc/db/base.py
|
|
|
|
|
from mooc.db.database import Base
|
|
|
|
|
from mooc.models.admin import Admin, Account, AccountWebapp
|
|
|
|
|
from mooc.models.account import AccountWechats # 新增,将创建好的模型类导入即可
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 5.运行代码更新数据库信息
|
|
|
|
|
```bash
|
|
|
|
|
python main.py
|
|
|
|
|
```
|
|
|
|
|
|
2025-01-03 18:03:30 +08:00
|
|
|
|
## 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"))`
|
|
|
|
|
|
2024-12-31 22:27:04 +08:00
|
|
|
|
## API文档
|
|
|
|
|
|
|
|
|
|
启动服务后访问: http://localhost:2333/docs
|