Program/README.md
烟雨如花 5d8480dd17
update README.md.
Signed-off-by: 烟雨如花 <2324281453@qq.com>
2025-01-15 13:18:50 +00:00

493 lines
12 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.

# 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 Schemaschemas/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 端点
这种方式可以:
- 快速生成符合项目规范的代码
- 确保类型映射的准确性
- 保持代码结构的一致性
- 减少手动编码的错误
## PHP 到 FastAPI 迁移指南
### 全局变量对应关系
1. `$_W` (WeEngine全局配置):
```php
$_W = array(
'config' => array(), // 系统配置信息
'timestamp' => time(), // 当前时间戳
'charset' => 'utf8', // 字符集
'clientip' => '', // 客户端IP
'uniacid' => 0, // 当前统一公众号ID
'acid' => 0, // 当前账号ID
'uid' => 0, // 当前用户ID
'isajax' => false, // 是否AJAX请求
'ispost' => false, // 是否POST请求
'siteroot' => '', // 站点根目录URL
'siteurl' => '', // 当前URL
'attachurl' => '', // 附件URL
'setting' => array(), // 站点设置
'module' => array() // 当前模块信息
);
```
2. `$_GPC` (全局请求参数):
```php
$_GPC = array_merge(
$_GET, // GET参数
$_POST, // POST参数
$_COOKIE // COOKIE数据
);
```
### 迁移方法步骤
以迁移 `dopageLogin` 为例:
1.`wxapp.py` 中添加路由:
```python
@wxapp_router.post("/index")
async def handle_wxapp_request(
request: Request,
i: str, # uniacid
do: Optional[str] = None,
db: Session = Depends(get_db)
):
# 处理不同的 do 参数
if do == "Login":
return await handle_login(request, i, db)
```
2. 创建请求数据模型:
```python
class LoginRequest(BaseModel):
code: str
encryptedData: Optional[str] = None
iv: Optional[str] = None
```
3. 创建处理函数:
```python
async def handle_login(request: Request, uniacid: str, db: Session):
# 获取所有参数
params = await get_all_params(request)
# 验证必要参数
if not params.get("code"):
return {"code": 1, "msg": "code值获取失败"}
# 获取小程序配置
account = await get_account_info(db, uniacid)
# 业务逻辑处理
try:
result = await process_login(
db,
code=params["code"],
appid=account.key,
secret=account.secret,
encrypted_data=params.get("encryptedData"),
iv=params.get("iv")
)
return {"code": 0, "msg": "登录成功", "data": result}
except Exception as e:
return {"code": 1, "msg": str(e)}
```
4. 实现数据库操作:
```python
# 在 crud/crud_goouc_fullexam_user.py 中
class CRUDUser(CRUDBase[User, UserCreate, UserUpdate]):
async def get_by_openid(self, db: Session, *, openid: str) -> Optional[User]:
return db.query(self.model).filter(
self.model.openid == openid,
self.model.istatus == 1
).first()
```
5. 实现业务逻辑:
```python
# 在 services/user.py 中
async def process_login(
db: Session,
code: str,
appid: str,
secret: str,
encrypted_data: Optional[str] = None,
iv: Optional[str] = None
) -> Dict:
# 1. 调用微信API获取session_key和openid
# 2. 解密用户信息
# 3. 查找或创建用户
# 4. 更新登录时间
# 5. 返回结果
```
### 注意事项
1. 参数获取:
- PHP: `$_GPC["param"]`
- FastAPI: `params = await get_all_params(request)`
2. 数据库操作:
- PHP: `pdo_fetch("SELECT * FROM " . tablename($this->t_user))`
- FastAPI: `db.query(User).filter(User.id == uid).first()`
3. 返回格式:
- PHP: `$this->result(0, "success", $data)`
- FastAPI: `{"code": 0, "msg": "success", "data": data}`
4. 错误处理:
```python
try:
# 业务逻辑
return {"code": 0, "msg": "success", "data": result}
except Exception as e:
return {"code": 1, "msg": str(e)}
```
5. 表名处理:
- PHP: `tablename($this->t_user)`
- FastAPI: 在模型定义中指定 `__tablename__ = "ims_goouc_fullexam_user"`