Files
chaoge bf42299ead 重构: 迁移至统一日志系统
- 新增 globalobjects/logger/ 模块化日志系统
- 支持异步写入、多目标输出、敏感信息脱敏
- 完全向后兼容原有logger API
- 备份旧版本为 logger_v1_backup.py 和 logger_v2_backup.py
- 更新 .env.example 和 AGENTS.md 文档
2026-05-22 00:23:30 +08:00

548 lines
14 KiB
Markdown
Raw Permalink 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.
# MyAPS API - 开发者指南
本文档为代码助手(如华为云CodeArts代码智能体)提供项目开发所需的关键信息,包括构建命令、代码风格指南和最佳实践。
## 项目概述
MyAPS API 是一个基于FastAPI的企业级数据操作平台,支持数据清洗、监控、WebSocket通信和定时任务调度。
**技术栈:**
- **后端框架**: FastAPI (>=0.110.0)
- **异步服务器**: Uvicorn (>=0.29.0)
- **数据库ORM**: Tortoise-ORM (>=1.1.7)
- **数据验证**: Pydantic (>=2.0.0)
- **前端**: 原生HTML/JS + Bootstrap CSS
## 统一日志系统
项目使用统一的日志系统(`globalobjects/logger/`),替代原有的logger.py和logger_v2.py。
### 特性
- 异步写入,不阻塞业务线程
- 多目标输出(控制台、文件、数据库、WebSocket)
- 敏感信息自动脱敏
- 日期前缀文件轮转
- API完全向后兼容
### 使用方法
```python
from globalobjects import logger
# 基本日志
logger.debug("调试信息")
logger.info("普通信息")
logger.warning("警告信息")
logger.error("错误信息")
logger.exception("异常信息") # 自动捕获异常堆栈
# 业务便捷方法
logger.success("推送数据", "单号001", "共5条")
logger.fail("查询失败", "表A", "连接超时")
logger.start("同步任务", "账套A01")
logger.stop("同步任务", "账套A01")
logger.query("用户表", count=100)
logger.insert("日志表", count=50)
# 配置
logger.set_level("DEBUG")
logger.set_db_initialized(True)
```
### 环境变量配置
```bash
LOG_LEVEL=INFO # 日志级别
LOG_DIR=logs # 日志目录
TO_CONSOLE=true # 输出到控制台
TO_FILE=true # 输出到文件
TO_DATABASE=true # 写入数据库
LOG_STACK_TRACE=false # 是否启用调用栈追踪
```
### 旧版本备份
- `logger_v1_backup.py` - 原logger.py备份
- `logger_v2_backup.py` - 原logger_v2.py备份
## 构建和运行命令
### 开发环境运行
```bash
# 启动开发服务器(默认端口8001
./scripts/dev_server.sh start
# 查看状态
./scripts/dev_server.sh status
# 停止服务
./scripts/dev_server.sh stop
# 重启服务
./scripts/dev_server.sh restart
# 查看日志
./scripts/dev_server.sh logs
./scripts/dev_server.sh logs -f # 实时查看
# 清除Python缓存
./scripts/dev_server.sh clear_cache
```
### 直接运行
```bash
# 使用uvicorn
uvicorn main:app --host 0.0.0.0 --port 8000
# 带热重载
uvicorn main:app --host 0.0.0.0 --port 8000 --reload
```
### Docker运行
```bash
# 使用Docker Compose(包含Redis
docker-compose up -d
# 单独构建
docker build -t myaps-api .
docker run -p 8000:8000 myaps-api
```
### 生产部署
```bash
# 使用Gunicorn
gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000
# 使用配置文件
gunicorn -c scripts/deploy/gunicorn.conf.py main:app
```
## 测试命令
### 运行所有测试
```bash
# 在项目根目录运行
python -m pytest tests/ -v
# 或直接使用pytest
pytest tests/ -v
```
### 运行单个测试文件
```bash
# 测试统一数据入口
python -m pytest tests/test_unified_router.py -v
# 测试业务规则
python -m pytest tests/test_business_rules.py -v
# 测试日志系统
python -m pytest tests/test_logger.py -v
```
### 运行单个测试类
```bash
# 测试特定的测试类
python -m pytest tests/test_unified_router.py::TestIsStagingMode -v
python -m pytest tests/test_unified_router.py::TestMapStagingResponseToDirect -v
```
### 运行单个测试方法
```bash
# 测试特定的测试方法
python -m pytest tests/test_unified_router.py::TestIsStagingMode::test_staging_mode_with_reserved_value -v
```
### 测试标记
项目使用pytest标记:
- `@pytest.mark.asyncio` - 异步测试标记
- 无其他特殊标记
### 测试覆盖率(如需要)
```bash
# 安装pytest-cov
pip install pytest-cov
# 运行测试并计算覆盖率
pytest --cov=. --cov-report=html tests/
```
## 代码风格指南
### Python代码风格
项目遵循PEP 8标准,但没有配置正式的lint工具。建议遵循以下约定:
#### 导入顺序
```python
# 1. 标准库导入
import os
import sys
from pathlib import Path
# 2. 第三方库导入
from fastapi import FastAPI, APIRouter
from pydantic import BaseModel
import pandas as pd
# 3. 本地应用导入
from core.app import create_app
from core.settings import PORT
from apps.io_api.routers import rt as io_rt
```
#### 命名约定
- **变量/函数**: `snake_case`
- **常量**: `UPPER_CASE_WITH_UNDERSCORES`
- **类名**: `PascalCase`
- **模块/包**: `snake_case`
- **私有成员**: `_private_variable``__really_private`
#### 类型提示
使用Python类型提示:
```python
from typing import Optional, List, Dict, Any
def process_data(
data: List[Dict[str, Any]],
config: Optional[Dict] = None
) -> Dict[str, Any]:
"""处理数据的函数文档字符串"""
pass
```
#### FastAPI特定约定
1. **路由函数**: 使用异步(async/await)
2. **Pydantic模型**: 使用BaseModel派生
3. **API响应**: 统一返回JSON格式
4. **错误处理**: 使用FastAPI异常处理器
#### 数据库相关
- **表名**: `snake_case` (如 `t_material_staging`)
- **列名**: `PascalCase` (如 `MaterialNo`)
- **注意**: 数据库字段名大小写敏感
### API设计规范
#### 路由定义
```python
from fastapi import APIRouter, Depends, HTTPException
router = APIRouter(prefix="/api", tags=["materials"])
@router.post("/upload", response_model=UploadResponse)
async def upload_materials(
data: UploadRequest,
db_name: str = Query(..., description="数据库名称")
):
"""上传物料数据"""
pass
```
#### 请求/响应模型
```python
from pydantic import BaseModel
from typing import Optional
class UploadRequest(BaseModel):
file_content: str
file_name: str
overwrite: bool = False
class UploadResponse(BaseModel):
success: bool
message: str
data: Optional[Dict] = None
meta: Optional[Dict] = None
```
#### API字段名
- **POST字段名**: 使用小写格式 (如 `file_content`, `db_name`)
- **Query参数**: 使用snake_case (如 `db_name`, `overwrite`)
- **路径参数**: 使用snake_case (如 `{material_id}`)
### 错误处理
#### HTTP状态码
- `200`: 成功
- `400`: 请求参数错误
- `401`: 未授权
- `403`: 禁止访问
- `404`: 资源不存在
- `500`: 服务器内部错误
#### 错误响应格式
```json
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "字段验证失败",
"details": {
"field_name": "具体错误信息"
}
}
}
```
### 日志记录
项目使用loguru或原生logging
```python
from globalobjects import logger
logger.info("操作成功")
logger.warning("警告信息")
logger.error("错误信息", exc_info=True)
```
## 项目结构
```
myaps_api/
├── apps/ # 应用模块
│ ├── common/ # 通用模块(监控、帮助)
│ ├── data_opt/ # 数据操作模块
│ └── io_api/ # I/O API模块
├── core/ # 核心组件
│ ├── app.py # FastAPI应用工厂
│ ├── database.py # 数据库配置
│ ├── settings.py # 应用设置
│ └── middleware.py # 中间件
├── globalobjects/ # 全局对象管理
├── scripts/ # 脚本目录
│ ├── dev_server.sh # 开发服务器管理
│ ├── deploy/ # 部署配置
│ └── migrate/ # 数据库迁移
├── static/ # 静态文件
├── storage/ # 数据存储
├── tests/ # 测试文件
├── .env # 环境变量配置
├── main.py # 应用入口
└── requirements.txt # 依赖包
```
## 开发工作流
### 1. 环境设置
```bash
# 创建虚拟环境(如不存在)
python -m venv venv
# 激活虚拟环境
source venv/bin/activate # Linux/Mac
venv\Scripts\activate # Windows
# 安装依赖
pip install -r requirements.txt
```
### 2. 代码规范检查
项目目前没有配置lint工具,建议添加以下工具:
**可选配置(如需要):**
```bash
# 安装格式化工具
pip install black isort ruff
# 代码格式化
black .
isort .
ruff check --fix
# 类型检查(如需)
pip install mypy
mypy .
```
### 3. 运行测试
```bash
# 运行所有测试
pytest tests/
# 运行特定模块测试
pytest tests/test_unified_router.py
# 运行带详细输出
pytest -v
# 运行并输出覆盖率
pytest --cov=. --cov-report=html
```
### 4. 代码提交前检查
1. 确保测试通过
2. 检查代码格式(如配置了lint工具)
3. 更新相关文档
4. 验证API接口
## 数据库操作
### 迁移脚本
```bash
# 数据库迁移
python scripts/migrate/auto_migrate.py
# Tortoise-ORM迁移
python scripts/migrate/migrate_with_tortoise.py
```
### 模型定义
```python
from tortoise.models import Model
from tortoise import fields
class MaterialStaging(Model):
"""物料暂存表"""
id = fields.IntField(pk=True)
MaterialNo = fields.CharField(max_length=50)
MaterialName = fields.CharField(max_length=255)
# 注意:列名使用PascalCase
class Meta:
table = "t_material_staging" # 表名使用snake_case
```
## 前端约定
### HTML/JS约定
1. **样式**: 使用Bootstrap 5.x
2. **图标**: 使用Bootstrap Icons (`bi-*`)
3. **字体**: 整个页面统一使用等宽字体
4. **布局**: 偏好卡片布局而非列表布局
5. **颜色**: 特定颜色使用十六进制值(如`#ff9300`
### 错误提示
1. **布局**: 横版布局,错误类型横向排列
2. **样式**: 统一错误高亮提示样式
3. **交互**: 手型光标用于可交互元素,插入光标用普通指针
### 验证规则显示
- 必填字段、枚举字段、业务规则块左右排列
- 内容在每个块内垂直分布
- 必填星号紧贴字段名右侧
## 部署配置
### 环境变量
复制`.env.example`(如存在)或创建`.env`文件:
```bash
# 应用配置
PORT=8000
HOST=0.0.0.0
LOG_LEVEL=INFO
# 数据库配置
DB_HOST=localhost
DB_PORT=3306
DB_USER=root
DB_PASSWORD=password
DB_NAME=myaps
# 功能开关
TURNON_BINLOG_LISTENER=False
TRUNON_SCHEDULER=False
# Staging模式配置
# 用于数据清洗模式的特殊数据库名称标识(默认--s)
STAGING_DB_NAME=--s
```
### Gunicorn配置
生产环境使用Gunicorn多进程配置:
```python
# scripts/deploy/gunicorn.conf.py
workers = 4
worker_class = "uvicorn.workers.UvicornWorker"
bind = "0.0.0.0:8000"
```
## 监控和调试
### 监控功能
- HTTP请求监控(`HTTPMonitorMiddleware`
- 资源使用监控
- 数据库监控(binlog监听)
- 定时任务监控
### 调试技巧
1. 使用`scripts/dev_server.sh logs -f`实时查看日志
2. 检查`logs/`目录下的日志文件
3. 使用FastAPI自动生成的API文档(`/docs``/redoc`
## 华为云CodeArts集成
项目已配置华为云CodeArts Doer
- 配置文件位于`.codeartsdoer/`
- 技能配置在`.codeartsdoer/skills/`
- 规范配置在`.codeartsdoer/specs/`
## 注意事项
1. **代码质量**: 确保新代码遵循项目现有模式
2. **API兼容性**: 修改API时保持向后兼容
3. **错误处理**: 所有API端点应有适当的错误处理
4. **日志记录**: 重要操作需记录日志
5. **测试覆盖**: 新功能应包含单元测试
6. **配置管理**: 避免硬编码,使用环境变量和配置对象
7. **数据库命名**: 表名用snake_case,列名用PascalCase,注意大小写敏感
## 故障排除
### 常见问题
1. **端口占用**: 修改`.env`中的`PORT`配置
2. **数据库连接失败**: 检查数据库配置和环境变量
3. **依赖安装失败**: 使用离线包或调整pip源
4. **权限问题**: 确保`logs/``storage/`目录可写
### Tortoise ORM 初始化竞态条件
**问题描述**
启动时偶尔出现"Tortoise ORM 初始化超时"错误,即使数据库可连接。
**根本原因**
FastAPI启动时,`register_tortoise`异步初始化与`lifespan`检查存在竞态条件:
- PostgreSQL首次连接可能需要3-5秒
- 启动事件和lifespan并行执行
- 早期请求到达时,ORM可能未完全初始化
**解决方案**(已实施):
使用**事件驱动的智能等待机制**(非硬编码等待):
1. **DatabaseInitManager** (`core/db_init_manager.py`)
- 事件驱动:初始化完成后主动通知等待者
- 精确计时:记录实际初始化耗时
- 状态追踪:监控初始化进度
2. **启动事件通知** (`core/database.py`)
- `@app.on_event("startup")` 中标记初始化完成
- 通知所有等待的协程
3. **智能等待** (`core/lifespan.py`, `apps/common/utils/db_helpers.py`)
- 使用`asyncio.Event`而非轮询
- 实际等待时间 = 数据库真实初始化时间
- 超时保护:最多等待30秒
**对比传统方案**
```python
# ❌ 旧方案:硬编码轮询
for i in range(20): # 固定等待10秒
await asyncio.sleep(0.5)
if Tortoise._inited:
break
# ✅ 新方案:事件驱动
result = await db_init_manager.wait_for_init(max_wait=30.0)
# 实际等待时间 = 数据库初始化实际耗时(通常1-3秒)
```
**相关配置**
- `STAGING_DB_NAME`: 清洗模式数据库标识(默认`--s`
- `THIS_DB_*`: PostgreSQL连接配置
### 调试指南
如需调试指导(如断点设置),请参考:
1. VS Code调试配置在`.vscode/`
2. 使用Python内置`pdb`模块
3. 或使用更高级的调试器如`debugpy`
---
**最后更新**: 2026-05-21
**维护者**: MyAPS开发团队
**版本**: 1.0.0