3862996045
CI/CD Pipeline / Check if frontend-only change (push) Waiting to run
CI/CD Pipeline / Validate - Code Quality (push) Waiting to run
CI/CD Pipeline / Validate - Type Check (mypy) (push) Waiting to run
CI/CD Pipeline / Validate - Migration (alembic) (push) Waiting to run
CI/CD Pipeline / Unit Tests (push) Blocked by required conditions
CI/CD Pipeline / Integration Tests (push) Blocked by required conditions
CI/CD Pipeline / Frontend Lint (push) Waiting to run
CI/CD Pipeline / Frontend Unit Tests (push) Blocked by required conditions
CI/CD Pipeline / PR Build API Image (push) Waiting to run
CI/CD Pipeline / PR Build Web Image (push) Waiting to run
CI/CD Pipeline / PR Build Worker Image (push) Waiting to run
CI/CD Pipeline / Build Staging API Image (push) Waiting to run
CI/CD Pipeline / Build Staging Web Image (push) Waiting to run
CI/CD Pipeline / Build Staging Worker Image (push) Waiting to run
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Blocked by required conditions
CI/CD Pipeline / Staging E2E Tests (push) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (push) Blocked by required conditions
CI/CD Pipeline / Build Production API Image (push) Waiting to run
CI/CD Pipeline / Build Production Web Image (push) Waiting to run
CI/CD Pipeline / Build Production Worker Image (push) Waiting to run
CI/CD Pipeline / Deploy Production (push) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (push) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (push) Blocked by required conditions
11 KiB
Executable File
11 KiB
Executable File
编码规范
本文档定义新 SaaS 项目的 Python 编码规范。
1. 基础规范
遵循 PEP 8,以下是重点和补充。
1.1 命名规范
模块/包:小写 + 下划线
# ✅ 正确
from packages.domain import entities
from packages.adapters.in_memory import project_repository
# ❌ 错误
from packages.Domain import Entities
from packages.adapters.InMemory import ProjectRepository
类:PascalCase
# ✅ 正确
class Project:
pass
class InMemoryProjectRepository:
pass
# ❌ 错误
class project:
pass
class in_memory_project_repository:
pass
函数/变量:小写 + 下划线
# ✅ 正确
def create_project(workspace_id: str, name: str) -> Project:
pass
user_count = 10
# ❌ 错误
def CreateProject(workspace_id: str, name: str) -> Project:
pass
UserCount = 10
常量:大写 + 下划线
# ✅ 正确
MAX_PROJECT_NAME_LENGTH = 100
DEFAULT_PAGE_SIZE = 20
# ❌ 错误
maxProjectNameLength = 100
default_page_size = 20
私有属性/方法:前缀 _
class Project:
def __init__(self):
self._internal_state = {}
def _validate(self):
pass
2. Type Hints
强制使用 type hints,提升代码可读性和 IDE 支持。
# ✅ 正确
def create_project(workspace_id: str, name: str, description: str = "") -> Project:
pass
def list_projects(workspace_id: str) -> list[Project]:
pass
def get_project(project_id: str) -> Project | None:
pass
# ❌ 错误
def create_project(workspace_id, name, description=""):
pass
复杂类型:
from typing import Protocol, Any
# Dict/List
def update_metadata(metadata: dict[str, Any]) -> None:
pass
# Optional (Python 3.10+ 用 | None)
def get_user(user_id: str) -> User | None:
pass
# Protocol
class Repository(Protocol):
def get(self, id: str) -> Entity | None:
pass
3. Dataclass
优先使用 dataclass 定义实体和值对象。
from dataclasses import dataclass, field
from datetime import datetime, timezone
# ✅ 正确
@dataclass(slots=True)
class Project:
id: str
workspace_id: str
name: str
description: str = ""
created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
# ❌ 错误(不用 dataclass)
class Project:
def __init__(self, id: str, workspace_id: str, name: str, description: str = ""):
self.id = id
self.workspace_id = workspace_id
self.name = name
self.description = description
为什么用 slots=True?
- 节省内存
- 防止意外添加属性
- 提升性能
4. 注释与文档
4.1 模块/类/函数注释
使用中文注释。
def create_project(workspace_id: str, name: str, description: str = "") -> Project:
"""
创建项目。
Args:
workspace_id: 工作空间 ID
name: 项目名称(不能为空)
description: 项目描述(可选)
Returns:
创建的项目实体
Raises:
ValueError: 项目名称为空时
"""
pass
4.2 复杂逻辑注释
# 正确做法:复杂逻辑加注释
def calculate_priority(job: IngestJob) -> int:
# 优先级规则:
# 1. FAILED 状态最高(需要重试)
# 2. PENDING 状态次高(等待处理)
# 3. PROCESSING 状态最低(正在处理)
if job.status == IngestJobStatus.FAILED:
return 100
elif job.status == IngestJobStatus.PENDING:
return 50
else:
return 10
5. 异常处理
5.1 使用具体异常
# ✅ 正确
def get_project(project_id: str) -> Project:
if not project_id:
raise ValueError("项目 ID 不能为空")
project = repository.get(project_id)
if project is None:
raise KeyError(f"项目 {project_id} 不存在")
return project
# ❌ 错误
def get_project(project_id: str) -> Project:
if not project_id:
raise Exception("错误") # 太宽泛
5.2 自定义异常
class ProjectNotFoundError(Exception):
"""项目不存在异常。"""
pass
class ProjectNameTooLongError(ValueError):
"""项目名称过长异常。"""
pass
6. Clean Architecture 约束
6.1 依赖方向
Apps (api/worker/web)
↓
Application (use cases)
↓
Ports (interfaces) ← Adapters (implementations)
↓
Domain (entities/rules)
Domain 层:
- ❌ 不能依赖任何外层
- ❌ 不能依赖 SQLAlchemy、FastAPI、Celery
- ✅ 只能依赖 Python 标准库
# ✅ 正确(Domain 层)
from dataclasses import dataclass
from datetime import datetime
from uuid import uuid4
@dataclass(slots=True)
class Project:
id: str
name: str
# ❌ 错误(Domain 层)
from sqlalchemy import Column, String # ❌ 不能依赖 SQLAlchemy
from fastapi import HTTPException # ❌ 不能依赖 FastAPI
@dataclass(slots=True)
class Project:
id: str
name: str
Application 层:
- ✅ 可以依赖 Domain + Ports
- ❌ 不能依赖 Adapters
Adapters 层:
- ✅ 可以依赖 Domain + Ports
- ✅ 可以使用外部库(SQLAlchemy、Redis 等)
7. 测试
7.1 测试文件命名
tests/
├── integration/
│ ├── test_projects.py
│ ├── test_ingest_pipeline.py
│ └── test_classification_pipeline.py
└── unit/
├── test_project_entity.py
└── test_asset_validation.py
7.2 测试函数命名
# ✅ 正确
def test_create_project_with_valid_name():
pass
def test_create_project_with_empty_name_should_fail():
pass
def test_list_projects_by_workspace():
pass
# ❌ 错误
def test1():
pass
def test_project():
pass
7.3 测试结构(AAA 模式)
def test_create_project():
# Arrange(准备)
workspace_id = "ws-1"
name = "测试项目"
repository = InMemoryProjectRepository()
use_case = CreateProjectUseCase(repository)
# Act(执行)
project = use_case.execute(
CreateProjectCommand(workspace_id=workspace_id, name=name)
)
# Assert(断言)
assert project.name == name
assert project.workspace_id == workspace_id
8. 代码格式化
8.1 行长度
- 最大 120 字符
- 优先 88 字符(Black 默认)
8.2 导入顺序
# 1. 标准库
import os
from datetime import datetime
from typing import Protocol
# 2. 第三方库
from fastapi import FastAPI
from sqlalchemy import Column
# 3. 本地模块
from packages.domain import Project
from packages.application import CreateProjectUseCase
8.3 空行
# 类之间:2 行
class User:
pass
class Workspace:
pass
# 函数之间:1 行
def create_user():
pass
def list_users():
pass
9. 安全规范
9.1 禁止硬编码敏感信息
# ❌ 错误
DATABASE_URL = "postgresql://admin:password123@localhost/db"
API_KEY = "sk-1234567890abcdef"
# ✅ 正确
import os
DATABASE_URL = os.getenv("DATABASE_URL")
API_KEY = os.getenv("API_KEY")
9.2 输入验证
# ✅ 正确
def create_project(name: str) -> Project:
clean_name = name.strip()
if not clean_name:
raise ValueError("项目名称不能为空")
if len(clean_name) > 100:
raise ValueError("项目名称不能超过 100 字符")
return Project(id=uuid4().hex, name=clean_name)
10. 性能规范
10.1 避免 N+1 查询
# ❌ 错误
projects = repository.list_by_workspace("ws-1")
for project in projects:
assets = asset_repository.list_by_project(project.id) # N+1
# ✅ 正确
projects = repository.list_by_workspace("ws-1")
project_ids = [p.id for p in projects]
assets = asset_repository.list_by_projects(project_ids) # 一次查询
10.2 使用生成器
# ✅ 正确(大数据集)
def list_all_assets() -> Generator[Asset, None, None]:
for asset in repository.stream():
yield asset
# ❌ 错误(加载全部到内存)
def list_all_assets() -> list[Asset]:
return repository.list_all() # 可能 OOM
8. 前端规范(React + TypeScript)
8.1 文件命名规范
| 类型 | 风格 | 示例 | 说明 |
|---|---|---|---|
| 目录名 | kebab-case | editing-planner/, asset-selector/ |
全小写,多单词用短横线连接 |
| 组件文件 | PascalCase | AssetSelector.tsx, MediaPanel.tsx |
与组件导出名一致 |
| 页面组件 | PascalCase | EditingPlanner.tsx, MyTemplates.tsx |
放在 kebab-case 目录中 |
| API 文件 | kebab-case | template-editor.ts, voice-clone.ts |
与 RESTful 资源路径风格一致 |
| Hooks | camelCase (use前缀) | useAuth.ts, useCloneProgress.ts |
React 官方惯例 |
| Store | kebab-case | auth-store.ts, ui-store.ts |
|
| 工具函数/helpers | kebab-case | format-duration.ts, date-utils.ts |
|
| 类型定义 | kebab-case | types.ts, subtitle-types.ts |
目录内类型定义可用 types.ts |
| 常量 | UPPER_SNAKE_CASE | MAX_UPLOAD_SIZE, API_BASE_URL |
|
| 测试文件 | 与被测文件同名 + .test |
auth.test.ts, AssetSelector.test.tsx |
放在 test/ 目录下,保持相同相对路径 |
8.2 组件命名
- 组件名使用 PascalCase,与文件名一致
- 默认导出组件名与文件名相同
- 高阶组件/包装器用
with前缀:withAuth(Component) - 渲染属性组件用
Render后缀:UserRender
// ✅ 正确
// 文件: AssetSelector.tsx
const AssetSelector: React.FC<AssetSelectorProps> = ({ assets }) => { ... };
export default AssetSelector;
// ❌ 错误
// 文件: asset-selector.tsx
const assetSelector = () => { ... };
8.3 变量与函数命名
- 变量/函数:camelCase
- 布尔变量:用
is/has/should/can前缀 - 事件处理函数:用
handle前缀 + 事件名 - 事件 handler prop:用
on前缀
// ✅ 正确
const isLoading = true;
const hasError = false;
const handleSubmit = () => { ... };
<Button onClick={onClick} />
8.4 导入路径
- 使用
@/别名引用src/下的文件 - 同一目录内用相对路径
./ - 导入顺序:React → 第三方库 → @/内部模块 → 相对路径 → 样式
import React, { useState } from 'react';
import { Button, Modal } from 'antd';
import { useAuth } from '@/hooks/useAuth';
import { Asset } from '@/api/asset-selector';
import { MediaPanel } from './MediaPanel';
import './AssetSelector.css';
8.5 CSS/样式命名
- CSS Modules / CSS 类名:kebab-case
- styled-components:PascalCase(与组件一致)
- Tailwind 工具类遵循官方惯例
/* ✅ 正确 */
.asset-selector { ... }
.asset-item { ... }
.asset-item--active { ... }
最后更新: 2026-07-23
版本: v1.1