create-dwy 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -2
- package/src/prompts/grouped-checkbox.js +223 -0
- package/src/sync.js +61 -39
- package/templates/claude-global/hooks/pre-git-commit-sensitive-check.sh +0 -0
- package/templates/claude-global/rules/{dwy-python-backend.md → Python/dwy-python-backend.md} +43 -3
- package/templates/claude-global/rules/{dwy-python-core.md → Python/dwy-python-core.md} +35 -2
- package/templates/claude-global/skills//345/237/272/347/241/200/345/272/223/dwy-eapi/SKILL.md +226 -0
- package/templates/claude-global/skills//346/225/260/346/215/256/345/272/223/dwy-dolphindb/SKILL.md +144 -0
- package/templates/claude-global/skills//346/225/260/346/215/256/345/272/223/dwy-dolphindb/references/create-database-table.md +219 -0
- package/templates/claude-global/skills//346/225/260/346/215/256/345/272/223/dwy-dolphindb/references/crud-operations.md +281 -0
- package/templates/claude-global/skills//346/225/260/346/215/256/345/272/223/dwy-dolphindb/references/data-import.md +271 -0
- package/templates/claude-global/skills//346/225/260/346/215/256/345/272/223/dwy-dolphindb/references/docker-deployment.md +63 -0
- package/templates/claude-global/skills//346/225/260/346/215/256/345/272/223/dwy-dolphindb/references/performance-tuning.md +223 -0
- package/templates/claude-global/skills//346/225/260/346/215/256/345/272/223/dwy-dolphindb/references/pitfalls-and-best-practices.md +352 -0
- package/templates/claude-global/skills//346/225/260/346/215/256/345/272/223/dwy-dolphindb/references/python-api.md +306 -0
- package/templates/claude-global/skills//346/225/260/346/215/256/345/272/223/dwy-dolphindb/references/setup-and-connection.md +143 -0
- package/templates/claude-global/skills//346/225/260/346/215/256/345/272/223/dwy-dolphindb/references/sources.md +46 -0
- package/templates/claude-global/skills//346/225/260/346/215/256/345/272/223/dwy-dolphindb/references/sql-batch-compute.md +206 -0
- package/templates/claude-global/skills//350/277/220/347/273/264/345/217/221/345/270/203/dwy-cibuildwheel/SKILL.md +354 -0
- package/templates/claude-global/skills//350/277/220/347/273/264/345/217/221/345/270/203/dwy-cibuildwheel/references/release-workflow-template.yml +99 -0
- package/templates/claude-global/skills/{dwy-deploy-audit → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit}/scripts/check_base.sh +0 -0
- package/templates/claude-global/skills/{dwy-deploy-audit → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit}/scripts/check_capacity.sh +0 -0
- package/templates/claude-global/skills/{dwy-deploy-audit → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit}/scripts/check_db.sh +0 -0
- package/templates/claude-global/skills/{dwy-deploy-audit → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit}/scripts/check_docker.sh +0 -0
- package/templates/claude-global/skills/{dwy-deploy-audit → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit}/scripts/check_env.sh +0 -0
- package/templates/claude-global/skills/{dwy-deploy-audit → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit}/scripts/check_https.sh +0 -0
- package/templates/claude-global/skills/{dwy-deploy-audit → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit}/scripts/check_logs.sh +0 -0
- package/templates/claude-global/skills/{dwy-deploy-audit → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit}/scripts/check_nginx.sh +0 -0
- package/templates/claude-global/skills/{dwy-deploy-audit → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit}/scripts/check_resilience.sh +0 -0
- package/templates/claude-global/skills/{dwy-deploy-audit → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit}/scripts/check_secrets.sh +0 -0
- package/templates/claude-global/skills/{dwy-deploy-audit → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit}/scripts/check_services.sh +0 -0
- package/templates/claude-global/skills/{dwy-deploy-audit → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit}/scripts/check_ssh.sh +0 -0
- package/templates/claude-global/skills/{dwy-deploy-audit → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit}/scripts/run_all.sh +0 -0
- package/templates/claude-global/skills/{dwy-docker → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-docker}/scripts/query_dockerhub.py +0 -0
- package/templates/claude-global/skills/{dwy-docker → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-docker}/scripts/scan_images.sh +0 -0
- package/templates/claude-global/skills/{dwy-mirror-source → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-mirror-source}/scripts/apply_mirrors.py +0 -0
- package/templates/claude-global/skills/{dwy-mirror-source → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-mirror-source}/scripts/check_mirrors.py +0 -0
- package/templates/claude-global/skills/dwy-dolphindb/SKILL.md +0 -98
- package/templates/claude-global/skills/dwy-dolphindb/evals/evals.json +0 -67
- package/templates/claude-global/skills/dwy-dolphindb/references/dolphindb-rules.md +0 -441
- package/templates/claude-global/skills/dwy-eapi/SKILL.md +0 -495
- package/templates/claude-global/skills/dwy-eapi/references/health.md +0 -47
- package/templates/claude-global/skills/dwy-eapi/references/logger.md +0 -60
- /package/templates/claude-global/rules/{dwy-android-core.md → Android/dwy-android-core.md} +0 -0
- /package/templates/claude-global/rules/{dwy-flutter-core.md → Flutter/dwy-flutter-core.md} +0 -0
- /package/templates/claude-global/rules/{dwy-flutter-network.md → Flutter/dwy-flutter-network.md} +0 -0
- /package/templates/claude-global/rules/{dwy-flutter-router.md → Flutter/dwy-flutter-router.md} +0 -0
- /package/templates/claude-global/rules/{dwy-flutter-testing.md → Flutter/dwy-flutter-testing.md} +0 -0
- /package/templates/claude-global/rules/{dwy-python-testing.md → Python/dwy-python-testing.md} +0 -0
- /package/templates/claude-global/rules/{dwy-vue-core.md → Vue/dwy-vue-core.md} +0 -0
- /package/templates/claude-global/rules/{dwy-vue-pinia.md → Vue/dwy-vue-pinia.md} +0 -0
- /package/templates/claude-global/rules/{dwy-vue-testing.md → Vue/dwy-vue-testing.md} +0 -0
- /package/templates/claude-global/rules/{dwy-tdd-development.md → /345/274/200/345/217/221/346/265/201/347/250/213/dwy-tdd-development.md"} +0 -0
- /package/templates/claude-global/rules/{dwy-db-migration.md → /346/225/260/346/215/256/345/272/223/dwy-db-migration.md"} +0 -0
- /package/templates/claude-global/rules/{dwy-postgres.md → /346/225/260/346/215/256/345/272/223/dwy-postgres.md"} +0 -0
- /package/templates/claude-global/rules/{dwy-redis.md → /346/225/260/346/215/256/345/272/223/dwy-redis.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-eapi/references/tasks-integration-guide.md → /345/237/272/347/241/200/345/272/223/dwy-eapi/references/tasks-integration-guide.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-ekit/SKILL.md → /345/237/272/347/241/200/345/272/223/dwy-ekit/SKILL.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-eui/SKILL.md → /345/237/272/347/241/200/345/272/223/dwy-eui/SKILL.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-eui/references/eui-design-guide.md → /345/237/272/347/241/200/345/272/223/dwy-eui/references/eui-design-guide.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-eui/references/eui-integration-guide.md → /345/237/272/347/241/200/345/272/223/dwy-eui/references/eui-integration-guide.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-eui/references/eui-landing-design-guide.md → /345/237/272/347/241/200/345/272/223/dwy-eui/references/eui-landing-design-guide.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/SKILL.md → /345/256/211/345/205/250/dwy-pentest/SKILL.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/.python-version → /345/256/211/345/205/250/dwy-pentest/scripts/.python-version"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/lib/__init__.py → /345/256/211/345/205/250/dwy-pentest/scripts/lib/__init__.py"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/lib/__pycache__/__init__.cpython-311.pyc → /345/256/211/345/205/250/dwy-pentest/scripts/lib/__pycache__/__init__.cpython-311.pyc"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/lib/__pycache__/config.cpython-311.pyc → /345/256/211/345/205/250/dwy-pentest/scripts/lib/__pycache__/config.cpython-311.pyc"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/lib/__pycache__/executor.cpython-311.pyc → /345/256/211/345/205/250/dwy-pentest/scripts/lib/__pycache__/executor.cpython-311.pyc"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/lib/__pycache__/severity.cpython-311.pyc → /345/256/211/345/205/250/dwy-pentest/scripts/lib/__pycache__/severity.cpython-311.pyc"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/lib/__pycache__/validator.cpython-311.pyc → /345/256/211/345/205/250/dwy-pentest/scripts/lib/__pycache__/validator.cpython-311.pyc"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/lib/config.py → /345/256/211/345/205/250/dwy-pentest/scripts/lib/config.py"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/lib/executor.py → /345/256/211/345/205/250/dwy-pentest/scripts/lib/executor.py"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/lib/severity.py → /345/256/211/345/205/250/dwy-pentest/scripts/lib/severity.py"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/lib/validator.py → /345/256/211/345/205/250/dwy-pentest/scripts/lib/validator.py"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/pentest_auth.py → /345/256/211/345/205/250/dwy-pentest/scripts/pentest_auth.py"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/pentest_recon.py → /345/256/211/345/205/250/dwy-pentest/scripts/pentest_recon.py"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/pentest_report.py → /345/256/211/345/205/250/dwy-pentest/scripts/pentest_report.py"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/pentest_scan.py → /345/256/211/345/205/250/dwy-pentest/scripts/pentest_scan.py"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/pentest_ssl.py → /345/256/211/345/205/250/dwy-pentest/scripts/pentest_ssl.py"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/pentest_vuln.py → /345/256/211/345/205/250/dwy-pentest/scripts/pentest_vuln.py"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/pentest_web.py → /345/256/211/345/205/250/dwy-pentest/scripts/pentest_web.py"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/pyproject.toml → /345/256/211/345/205/250/dwy-pentest/scripts/pyproject.toml"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/setup_tools.sh → /345/256/211/345/205/250/dwy-pentest/scripts/setup_tools.sh"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/uv.lock → /345/256/211/345/205/250/dwy-pentest/scripts/uv.lock"} +0 -0
- /package/templates/claude-global/skills/{dwy-dev-qa/SKILL.md → /345/274/200/345/217/221/346/265/201/347/250/213/dwy-dev-qa/SKILL.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-git-commit/SKILL.md → /345/274/200/345/217/221/346/265/201/347/250/213/dwy-git-commit/SKILL.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-fullstack-scaffold/SKILL.md → /350/204/232/346/211/213/346/236/266/dwy-fullstack-scaffold/SKILL.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-tech-stack/SKILL.md → /350/204/232/346/211/213/346/236/266/dwy-tech-stack/SKILL.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-deploy-audit/SKILL.md → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit/SKILL.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-deploy-audit/references/checks-data.md → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit/references/checks-data.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-deploy-audit/references/checks-network.md → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit/references/checks-network.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-deploy-audit/references/checks-runtime.md → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit/references/checks-runtime.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-docker/SKILL.md → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-docker/SKILL.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-docker/references/ask-templates.md → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-docker/references/ask-templates.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-docker/references/templates.md → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-docker/references/templates.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-docker/references/version-rules.md → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-docker/references/version-rules.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-mirror-source/SKILL.md → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-mirror-source/SKILL.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-mirror-source/references/dockerfile-snippets.md → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-mirror-source/references/dockerfile-snippets.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-mirror-source/references/mirror-providers.md → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-mirror-source/references/mirror-providers.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-mirror-source/scripts/__pycache__/check_mirrors.cpython-313.pyc → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-mirror-source/scripts/__pycache__/check_mirrors.cpython-313.pyc"} +0 -0
- /package/templates/claude-global/skills/{dwy-publish/SKILL.md → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-publish/SKILL.md"} +0 -0
|
@@ -1,495 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: dwy-eapi
|
|
3
|
-
description: "dwyeapi FastAPI 后端基础设施速查。当用户使用 FastAPI 构建后端、配置数据库/Redis/JWT/异常处理、写 API 响应/分页/日志/健康检查/数据脱敏、处理异步耗时任务、接入邮件验证码发送时,**必须**使用此 skill。即使用户没有明确说'dwyeapi'或'eapi',只要涉及 FastAPI 项目的基础设施搭建、统一响应封装、异常处理、数据库会话管理、Redis 缓存、JWT 认证、后台任务队列、PII 脱敏、日志配置,也应触发。"
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# dwyeapi 后端基础设施速查
|
|
7
|
-
|
|
8
|
-
FastAPI 项目基础设施包,Python 3.11+,全异步。11 个模块。
|
|
9
|
-
|
|
10
|
-
> **Tasks 集成指南:** 详见 [references/tasks-integration-guide.md](references/tasks-integration-guide.md)
|
|
11
|
-
|
|
12
|
-
## 安装
|
|
13
|
-
|
|
14
|
-
```bash
|
|
15
|
-
pip install dwyeapi
|
|
16
|
-
# 需要任务处理时
|
|
17
|
-
pip install dwyeapi[tasks]
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
```python
|
|
21
|
-
from dwyeapi.config import BaseSettings
|
|
22
|
-
from dwyeapi.database import Base, TimestampMixin, create_async_engine_factory, create_session_factory
|
|
23
|
-
from dwyeapi.security import hash_password, verify_password, create_token, decode_token
|
|
24
|
-
from dwyeapi.exceptions import NotFoundError, BusinessError, register_exception_handlers
|
|
25
|
-
from dwyeapi.response import success, fail, paginated
|
|
26
|
-
from dwyeapi.pagination import PaginationParams, paginate
|
|
27
|
-
from dwyeapi.cache import configure as configure_redis, get_redis, close_redis
|
|
28
|
-
from dwyeapi.dependencies import create_get_db
|
|
29
|
-
from dwyeapi import dt
|
|
30
|
-
from dwyeapi.masking import mask_phone, mask_email, mask_id_card, mask_name
|
|
31
|
-
|
|
32
|
-
# 任务模块 (需安装 [tasks] extra)
|
|
33
|
-
from dwyeapi.tasks import setup_tasks, task_router, register, TaskContext, TaskStatus, create_worker_settings
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## 查阅源码
|
|
37
|
-
|
|
38
|
-
每个模块在 `backend/src/dwyeapi/{module}.py`,单文件设计可直接阅读。
|
|
39
|
-
|
|
40
|
-
---
|
|
41
|
-
|
|
42
|
-
## config — Pydantic Settings
|
|
43
|
-
|
|
44
|
-
```python
|
|
45
|
-
from dwyeapi.config import BaseSettings
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
子类化后使用,从 `.env` 或环境变量读取配置。
|
|
49
|
-
|
|
50
|
-
```python
|
|
51
|
-
class Settings(BaseSettings):
|
|
52
|
-
app_name: str = "My API"
|
|
53
|
-
# 继承的必填字段:database_url, redis_url, secret_key
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
| 字段 | 类型 | 默认值 | 说明 |
|
|
57
|
-
|------|------|--------|------|
|
|
58
|
-
| database_url | str | **必填** | 异步数据库 URL |
|
|
59
|
-
| redis_url | str | **必填** | Redis URL |
|
|
60
|
-
| secret_key | str | **必填** | JWT 签名密钥 |
|
|
61
|
-
| jwt_algorithm | str | `"HS256"` | JWT 算法 |
|
|
62
|
-
| access_token_expire_minutes | int | `30` | Token 过期分钟 |
|
|
63
|
-
| environment | `"dev" \| "prod"` | `"prod"` | 运行环境(见下) |
|
|
64
|
-
| allowed_origins | list[str] | `[]` | CORS 允许域名 |
|
|
65
|
-
| task_max_jobs | int | `5` | Worker 最大并发任务数 |
|
|
66
|
-
| task_job_timeout | int | `3600` | 单个任务超时秒数 |
|
|
67
|
-
| task_failure_ttl | int | `86400` | 失败任务 Redis 保留秒数 |
|
|
68
|
-
|
|
69
|
-
### 运行环境识别 (dev / prod)
|
|
70
|
-
|
|
71
|
-
`environment` 默认 `"prod"`(误配置时保守)。**没有 `debug` 字段** —— 所有调试开关统一判断 `is_dev()`,避免两个维度冲突。业务代码通过三个顶层 API 读当前环境:
|
|
72
|
-
|
|
73
|
-
```python
|
|
74
|
-
from dwyeapi import is_dev, is_prod, get_environment
|
|
75
|
-
|
|
76
|
-
# FastAPI docs/redoc/openapi 三个端点仅 dev 开启,prod 必须关闭(防路由元信息泄露)
|
|
77
|
-
app = FastAPI(
|
|
78
|
-
docs_url="/docs" if is_dev() else None,
|
|
79
|
-
redoc_url="/redoc" if is_dev() else None,
|
|
80
|
-
openapi_url="/openapi.json" if is_dev() else None,
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
# SQL echo、慢路由 profiling 等性能相关调试也走 is_dev()
|
|
84
|
-
engine = create_async_engine_factory(settings.database_url, echo=is_dev())
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
**Email Provider 注入**: `providers.email` 内置仅 `resend`;业务自定义发送通道(腾讯云 SES、AWS SES、自建 SMTP 等)需继承 `EmailProviderBase` 实现 `_send`,再调 `register_email_provider("name", factory)` 注册,`.env` 设 `EMAIL__PROVIDER=name` 启用。验证码 + Redis + 品牌化模板由基类全部复用。
|
|
88
|
-
|
|
89
|
-
---
|
|
90
|
-
|
|
91
|
-
## database — 异步 SQLAlchemy
|
|
92
|
-
|
|
93
|
-
```python
|
|
94
|
-
from dwyeapi.database import Base, TimestampMixin, create_async_engine_factory, create_session_factory
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
### 模型定义
|
|
98
|
-
|
|
99
|
-
```python
|
|
100
|
-
from sqlalchemy.orm import Mapped, mapped_column
|
|
101
|
-
from sqlalchemy import String
|
|
102
|
-
|
|
103
|
-
class User(Base, TimestampMixin):
|
|
104
|
-
__tablename__ = "users"
|
|
105
|
-
id: Mapped[int] = mapped_column(primary_key=True)
|
|
106
|
-
name: Mapped[str] = mapped_column(String(50))
|
|
107
|
-
# TimestampMixin 自动添加 created_at, updated_at
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
### 引擎和会话
|
|
111
|
-
|
|
112
|
-
```python
|
|
113
|
-
engine = create_async_engine_factory(settings.database_url, echo=is_dev())
|
|
114
|
-
# 参数: database_url, pool_size=5, max_overflow=10, echo=False
|
|
115
|
-
# SQLite 自动跳过连接池参数
|
|
116
|
-
|
|
117
|
-
session_factory = create_session_factory(engine)
|
|
118
|
-
# 返回 async_sessionmaker[AsyncSession],expire_on_commit=False
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
## dependencies — FastAPI 依赖注入
|
|
124
|
-
|
|
125
|
-
```python
|
|
126
|
-
from dwyeapi.dependencies import create_get_db
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
### create_get_db(session_factory)
|
|
130
|
-
|
|
131
|
-
生成 `get_db` FastAPI Depends 函数,自动管理会话生命周期。
|
|
132
|
-
|
|
133
|
-
```python
|
|
134
|
-
get_db = create_get_db(session_factory)
|
|
135
|
-
|
|
136
|
-
@router.get("/users/{user_id}")
|
|
137
|
-
async def get_user(user_id: int, db: AsyncSession = Depends(get_db)):
|
|
138
|
-
...
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
---
|
|
142
|
-
|
|
143
|
-
## security — JWT + bcrypt
|
|
144
|
-
|
|
145
|
-
```python
|
|
146
|
-
from dwyeapi.security import hash_password, verify_password, create_token, decode_token
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
所有函数无状态,密钥通过参数传入。
|
|
150
|
-
|
|
151
|
-
| 函数 | 签名 | 说明 |
|
|
152
|
-
|------|------|------|
|
|
153
|
-
| hash_password | `(password: str) -> str` | bcrypt 哈希 |
|
|
154
|
-
| verify_password | `(plain: str, hashed: str) -> bool` | 验证密码 |
|
|
155
|
-
| create_token | `(data: dict, secret: str, expires_minutes: int, algorithm="HS256") -> str` | 创建 JWT,自动添加 exp |
|
|
156
|
-
| decode_token | `(token: str, secret: str, algorithm="HS256") -> dict \| None` | 解码 JWT,失败返回 None |
|
|
157
|
-
|
|
158
|
-
```python
|
|
159
|
-
hashed = hash_password("my-password")
|
|
160
|
-
is_valid = verify_password("my-password", hashed)
|
|
161
|
-
|
|
162
|
-
token = create_token({"sub": str(user.id)}, settings.secret_key, settings.access_token_expire_minutes)
|
|
163
|
-
payload = decode_token(token, settings.secret_key) # {"sub": "1", "exp": ...} 或 None
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
---
|
|
167
|
-
|
|
168
|
-
## exceptions — 异常体系
|
|
169
|
-
|
|
170
|
-
```python
|
|
171
|
-
from dwyeapi.exceptions import (
|
|
172
|
-
AppError, NotFoundError, BusinessError, PermissionDeniedError, AuthenticationError,
|
|
173
|
-
register_exception_handlers,
|
|
174
|
-
)
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
### 异常层级
|
|
178
|
-
|
|
179
|
-
| 异常类 | HTTP 状态码 | code | message |
|
|
180
|
-
|--------|-----------|------|---------|
|
|
181
|
-
| AppError(message, code) | — | 自定义 | 自定义 |
|
|
182
|
-
| NotFoundError(resource) | 404 | `NOT_FOUND` | `{resource}不存在` |
|
|
183
|
-
| BusinessError(message, code) | 422 | 自定义 | 自定义 |
|
|
184
|
-
| PermissionDeniedError() | 403 | `PERMISSION_DENIED` | `权限不足` |
|
|
185
|
-
| AuthenticationError() | 401 | `AUTHENTICATION_FAILED` | `认证失败` |
|
|
186
|
-
|
|
187
|
-
### 注册到 FastAPI
|
|
188
|
-
|
|
189
|
-
```python
|
|
190
|
-
app = FastAPI()
|
|
191
|
-
register_exception_handlers(app)
|
|
192
|
-
# 之后在 service 层直接 raise NotFoundError("用户") 即可返回 404
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
响应格式统一为 `{"code": "ERROR_CODE", "message": "描述"}`。
|
|
196
|
-
|
|
197
|
-
### 使用原则
|
|
198
|
-
|
|
199
|
-
- **service 层**抛业务异常(NotFoundError, BusinessError 等)
|
|
200
|
-
- **router 层**不写 try/except,由 register_exception_handlers 统一处理
|
|
201
|
-
- **禁止**在 service 层抛 HTTPException
|
|
202
|
-
|
|
203
|
-
---
|
|
204
|
-
|
|
205
|
-
## response — 统一响应
|
|
206
|
-
|
|
207
|
-
```python
|
|
208
|
-
from dwyeapi.response import success, fail, paginated
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
| 函数 | 签名 | 返回结构 |
|
|
212
|
-
|------|------|---------|
|
|
213
|
-
| success | `(data=None, message="success") -> dict` | `{"code": 200, "message": ..., "data": ..., "timestamp": ...}` |
|
|
214
|
-
| fail | `(code=400, message="fail") -> dict` | `{"code": ..., "message": ..., "data": None, "timestamp": ...}` |
|
|
215
|
-
| paginated | `(items, total, page, page_size) -> dict` | success 包装 `{"items": [...], "total": N, "page": N, "page_size": N}` |
|
|
216
|
-
|
|
217
|
-
```python
|
|
218
|
-
@router.get("/users/{user_id}")
|
|
219
|
-
async def get_user(user_id: int, service=Depends(get_user_service)):
|
|
220
|
-
user = await service.get_by_id(user_id)
|
|
221
|
-
return success(data=UserResponse.model_validate(user))
|
|
222
|
-
|
|
223
|
-
@router.get("/users")
|
|
224
|
-
async def list_users(params: PaginationParams = Depends(), service=Depends(get_user_service)):
|
|
225
|
-
items, total = await service.list(params)
|
|
226
|
-
return paginated(items, total, params.page, params.page_size)
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
---
|
|
230
|
-
|
|
231
|
-
## pagination — 分页工具
|
|
232
|
-
|
|
233
|
-
```python
|
|
234
|
-
from dwyeapi.pagination import PaginationParams, paginate, OffsetLimit
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
### PaginationParams
|
|
238
|
-
|
|
239
|
-
FastAPI Depends 注入的查询参数模型。
|
|
240
|
-
|
|
241
|
-
| 字段 | 类型 | 默认 | 约束 |
|
|
242
|
-
|------|------|------|------|
|
|
243
|
-
| page | int | 1 | >= 1 |
|
|
244
|
-
| page_size | int | 20 | 1 ~ 100 |
|
|
245
|
-
|
|
246
|
-
### paginate(page, page_size) -> OffsetLimit
|
|
247
|
-
|
|
248
|
-
```python
|
|
249
|
-
offset_limit = paginate(params.page, params.page_size)
|
|
250
|
-
# OffsetLimit(offset=0, limit=20) — 可直接用于 SQLAlchemy .offset().limit()
|
|
251
|
-
stmt = select(User).offset(offset_limit.offset).limit(offset_limit.limit)
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
---
|
|
255
|
-
|
|
256
|
-
## cache — 异步 Redis
|
|
257
|
-
|
|
258
|
-
```python
|
|
259
|
-
from dwyeapi import cache
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
| 函数 | 签名 | 说明 |
|
|
263
|
-
|------|------|------|
|
|
264
|
-
| cache.configure | `(redis_url: str) -> None` | 设置 Redis URL(启动时调用一次) |
|
|
265
|
-
| cache.get_redis | `async () -> Redis` | 获取/创建共享连接(未 configure 时抛 RuntimeError) |
|
|
266
|
-
| cache.close_redis | `async () -> None` | 关闭连接(安全幂等) |
|
|
267
|
-
|
|
268
|
-
```python
|
|
269
|
-
# lifespan 中初始化
|
|
270
|
-
@asynccontextmanager
|
|
271
|
-
async def lifespan(app: FastAPI):
|
|
272
|
-
cache.configure(settings.redis_url)
|
|
273
|
-
yield
|
|
274
|
-
await cache.close_redis()
|
|
275
|
-
|
|
276
|
-
# 业务中使用
|
|
277
|
-
redis = await cache.get_redis()
|
|
278
|
-
await redis.set("key", "value", ex=3600)
|
|
279
|
-
val = await redis.get("key")
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
---
|
|
283
|
-
|
|
284
|
-
## dt — 全局时间工具
|
|
285
|
-
|
|
286
|
-
```python
|
|
287
|
-
from dwyeapi import dt
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
业务时间统一 Asia/Shanghai,**禁止在业务代码中直接使用 `datetime.now()`**,统一通过 `dt` 模块访问。
|
|
291
|
-
|
|
292
|
-
| 函数 | 签名 | 说明 |
|
|
293
|
-
|------|------|------|
|
|
294
|
-
| dt.now | `() -> datetime` | 当前中国时间(naive,用于数据库存储) |
|
|
295
|
-
| dt.now_str | `(fmt="%Y-%m-%d %H:%M:%S") -> str` | 格式化的当前中国时间字符串 |
|
|
296
|
-
| dt.today | `() -> date` | 当前中国日期 |
|
|
297
|
-
| dt.timestamp | `() -> float` | 当前 Unix 时间戳(秒) |
|
|
298
|
-
| dt.utc_now | `() -> datetime` | 当前 UTC 时间(aware,用于 JWT exp 等协议字段) |
|
|
299
|
-
|
|
300
|
-
```python
|
|
301
|
-
dt.now() # datetime(2026, 4, 8, 14, 30, 0) — naive, Asia/Shanghai
|
|
302
|
-
dt.now_str() # "2026-04-08 14:30:00"
|
|
303
|
-
dt.today() # date(2026, 4, 8)
|
|
304
|
-
dt.timestamp() # 1775625000.0
|
|
305
|
-
dt.utc_now() # datetime(2026, 4, 8, 6, 30, 0, tzinfo=UTC) — aware
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
常量:`dt.TZ`(Asia/Shanghai)、`dt.UTC`。
|
|
309
|
-
|
|
310
|
-
---
|
|
311
|
-
|
|
312
|
-
## masking — PII 数据脱敏
|
|
313
|
-
|
|
314
|
-
```python
|
|
315
|
-
from dwyeapi.masking import (
|
|
316
|
-
mask_phone, # 138****5678
|
|
317
|
-
mask_email, # z***@gmail.com
|
|
318
|
-
mask_id_card, # 420***********1234
|
|
319
|
-
mask_bank_card, # 6222********1234
|
|
320
|
-
mask_name, # 张*明
|
|
321
|
-
mask_address, # 浙江省杭州市西湖区****
|
|
322
|
-
mask_ip, # 192.168.1.*
|
|
323
|
-
mask_license_plate, # 浙A***8
|
|
324
|
-
mask_text, # 通用脱敏(自定义首尾保留位数)
|
|
325
|
-
)
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
纯函数模块,无外部依赖。所有函数对空字符串、格式不匹配的输入原样返回,不抛异常。
|
|
329
|
-
|
|
330
|
-
### 在 Response Schema 中使用
|
|
331
|
-
|
|
332
|
-
```python
|
|
333
|
-
class UserResponse(BaseModel):
|
|
334
|
-
phone: str
|
|
335
|
-
email: str
|
|
336
|
-
|
|
337
|
-
@field_validator("phone", mode="before")
|
|
338
|
-
@classmethod
|
|
339
|
-
def _mask_phone(cls, v: str) -> str:
|
|
340
|
-
return mask_phone(v) if v else v
|
|
341
|
-
|
|
342
|
-
@field_validator("email", mode="before")
|
|
343
|
-
@classmethod
|
|
344
|
-
def _mask_email(cls, v: str) -> str:
|
|
345
|
-
return mask_email(v) if v else v
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
### mask_text — 通用脱敏
|
|
349
|
-
|
|
350
|
-
```python
|
|
351
|
-
mask_text("hello", start=1, end=1, mask_char="*") # h***o
|
|
352
|
-
mask_text("1234567890", start=2, end=3) # 12*****890
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
---
|
|
356
|
-
|
|
357
|
-
## 典型项目接入流程
|
|
358
|
-
|
|
359
|
-
```python
|
|
360
|
-
# config.py
|
|
361
|
-
from dwyeapi.config import BaseSettings
|
|
362
|
-
|
|
363
|
-
class Settings(BaseSettings):
|
|
364
|
-
app_name: str = "My API"
|
|
365
|
-
|
|
366
|
-
settings = Settings()
|
|
367
|
-
|
|
368
|
-
# database.py
|
|
369
|
-
from dwyeapi.database import Base, create_async_engine_factory, create_session_factory
|
|
370
|
-
from dwyeapi.dependencies import create_get_db
|
|
371
|
-
|
|
372
|
-
engine = create_async_engine_factory(settings.database_url)
|
|
373
|
-
session_factory = create_session_factory(engine)
|
|
374
|
-
get_db = create_get_db(session_factory)
|
|
375
|
-
|
|
376
|
-
# main.py
|
|
377
|
-
from dwyeapi.exceptions import register_exception_handlers
|
|
378
|
-
from dwyeapi import cache
|
|
379
|
-
|
|
380
|
-
app = FastAPI(lifespan=lifespan)
|
|
381
|
-
register_exception_handlers(app)
|
|
382
|
-
```
|
|
383
|
-
|
|
384
|
-
---
|
|
385
|
-
|
|
386
|
-
## tasks — 异步任务处理 (需 `[tasks]` extra)
|
|
387
|
-
|
|
388
|
-
```python
|
|
389
|
-
from dwyeapi.tasks import setup_tasks, task_router, register, TaskContext
|
|
390
|
-
```
|
|
391
|
-
|
|
392
|
-
基于 ARQ 的全异步耗时任务系统。3 步接入:注册任务 → setup + 挂载路由 → 启动 Worker。
|
|
393
|
-
|
|
394
|
-
### 快速示例
|
|
395
|
-
|
|
396
|
-
```python
|
|
397
|
-
# 1. 注册任务
|
|
398
|
-
@register("process_data")
|
|
399
|
-
async def process_data(ctx: TaskContext, params: dict):
|
|
400
|
-
await ctx.log("开始处理")
|
|
401
|
-
await ctx.update_progress(50, "处理中...")
|
|
402
|
-
if await ctx.is_cancelled():
|
|
403
|
-
return
|
|
404
|
-
return {"result": "ok"} # 框架自动设为 SUCCESS
|
|
405
|
-
|
|
406
|
-
# 2. FastAPI 接入
|
|
407
|
-
await setup_tasks(app, settings, session_factory)
|
|
408
|
-
app.include_router(task_router)
|
|
409
|
-
|
|
410
|
-
# 3. Worker: arq app.worker.WorkerSettings
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
### 公共 API
|
|
414
|
-
|
|
415
|
-
| 导出项 | 用途 |
|
|
416
|
-
|--------|------|
|
|
417
|
-
| `setup_tasks(app, settings, session_factory)` | lifespan 中调用, 初始化 |
|
|
418
|
-
| `task_router` | 开箱即用 APIRouter (提交/查询/列表/取消) |
|
|
419
|
-
| `register(task_type)` | 装饰器, 注册异步任务 |
|
|
420
|
-
| `TaskContext` | 任务函数上下文 (db/log/progress/cancel) |
|
|
421
|
-
| `TaskStatus` | 枚举: PENDING/RUNNING/SUCCESS/FAILED/CANCELED |
|
|
422
|
-
| `create_worker_settings(settings, session_factory)` | 生成 ARQ Worker 配置 |
|
|
423
|
-
|
|
424
|
-
### API 接口
|
|
425
|
-
|
|
426
|
-
| 方法 | 路径 | 说明 |
|
|
427
|
-
|------|------|------|
|
|
428
|
-
| POST | `/tasks` | 提交任务 |
|
|
429
|
-
| GET | `/tasks/{task_id}` | 查询状态 |
|
|
430
|
-
| GET | `/tasks` | 列表 (分页 + 筛选) |
|
|
431
|
-
| POST | `/tasks/{task_id}/cancel` | 取消任务 |
|
|
432
|
-
|
|
433
|
-
> **完整文档:** [tasks-integration-guide.md](references/tasks-integration-guide.md) — 包含 TaskContext API、数据模型、Worker 配置、取消机制等详细说明。
|
|
434
|
-
|
|
435
|
-
---
|
|
436
|
-
|
|
437
|
-
## 其他模块速查
|
|
438
|
-
|
|
439
|
-
### logger — 基于 loguru 的全局日志
|
|
440
|
-
|
|
441
|
-
```python
|
|
442
|
-
from dwyeapi import logger
|
|
443
|
-
|
|
444
|
-
logger.configure(level="INFO", log_dir="./logs", filename="app")
|
|
445
|
-
log = logger.get_logger("users")
|
|
446
|
-
log.info("用户 %s 登录", user_id)
|
|
447
|
-
```
|
|
448
|
-
|
|
449
|
-
| 函数 | 说明 |
|
|
450
|
-
|------|------|
|
|
451
|
-
| `logger.configure(...)` | 启动时调用一次;支持按天+按大小轮转、JSON 序列化、拦截 stdlib logging |
|
|
452
|
-
| `logger.get_logger(name?)` | 返回 Logger 门面,支持 `%s` 占位符和 `exc_info=True` |
|
|
453
|
-
| `logger.close()` | 关闭并恢复 stdlib logger(lifespan 结束时调用) |
|
|
454
|
-
|
|
455
|
-
**详细配置参数**:读 `references/logger.md`
|
|
456
|
-
|
|
457
|
-
### health — 健康检查路由工厂
|
|
458
|
-
|
|
459
|
-
```python
|
|
460
|
-
from dwyeapi.health import create_health_router
|
|
461
|
-
|
|
462
|
-
router = create_health_router(service_name="my-api", version="1.0.0")
|
|
463
|
-
app.include_router(router)
|
|
464
|
-
# GET /health → {"service": "my-api", "version": "1.0.0", "status": "alive"}
|
|
465
|
-
```
|
|
466
|
-
|
|
467
|
-
只探活、不探依赖(防 DoS 放大)。业务如需 readiness 自行实现并限制内网访问。
|
|
468
|
-
|
|
469
|
-
### providers.email — 邮件验证码发送(可插拔)
|
|
470
|
-
|
|
471
|
-
内置仅 `resend`,业务可继承 `EmailProviderBase` 注入自定义通道(腾讯云 SES、AWS SES、SMTP 等)。验证码 + Redis + 品牌化模板由基类复用。
|
|
472
|
-
|
|
473
|
-
```python
|
|
474
|
-
from dwyeapi.providers.email import EmailSettings, make_email_provider, register_email_provider
|
|
475
|
-
```
|
|
476
|
-
|
|
477
|
-
| 导出项 | 说明 |
|
|
478
|
-
|--------|------|
|
|
479
|
-
| `EmailSettings` | 配置模型(code_ttl, brand_name, resend) |
|
|
480
|
-
| `make_email_provider(settings)` | 工厂函数,按 `.env EMAIL__PROVIDER` 创建实例 |
|
|
481
|
-
| `register_email_provider(name, factory)` | 注册自定义 provider |
|
|
482
|
-
| `EmailProviderBase` | 抽象基类,继承后实现 `_send` 即可 |
|
|
483
|
-
|
|
484
|
-
---
|
|
485
|
-
|
|
486
|
-
## 查阅源码
|
|
487
|
-
|
|
488
|
-
所有模块为单文件设计,直接阅读:
|
|
489
|
-
|
|
490
|
-
```
|
|
491
|
-
backend/src/dwyeapi/{module}.py
|
|
492
|
-
backend/src/dwyeapi/providers/email/
|
|
493
|
-
```
|
|
494
|
-
|
|
495
|
-
模块清单:config、database、dependencies、security、exceptions、response、pagination、cache、dt、masking、logger、health、tasks、providers/email。
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# health — 健康检查路由工厂
|
|
2
|
-
|
|
3
|
-
> 何时读这份:当用户需要添加健康检查端点、配置 k8s livenessProbe、或讨论 readiness vs liveness 设计时读取。
|
|
4
|
-
|
|
5
|
-
```python
|
|
6
|
-
from dwyeapi.health import create_health_router
|
|
7
|
-
|
|
8
|
-
router = create_health_router(
|
|
9
|
-
service_name="my-api",
|
|
10
|
-
version="1.0.0",
|
|
11
|
-
path="/health",
|
|
12
|
-
include_in_schema=True,
|
|
13
|
-
)
|
|
14
|
-
app.include_router(router)
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
## 设计取舍
|
|
18
|
-
|
|
19
|
-
端点**只**报告进程是否存活(liveness),**不连接 PostgreSQL / Redis** 等依赖。
|
|
20
|
-
|
|
21
|
-
原因:健康端点通常对公网开放,若内置 readiness / dependency check,攻击者可用低频高并发请求放大到数据库/缓存层,变成 DoS 放大器。
|
|
22
|
-
|
|
23
|
-
需要 readiness 语义的业务(如 k8s readinessProbe)应在业务项目里自行实现,并把该端点限制在内网或挂鉴权。
|
|
24
|
-
|
|
25
|
-
## create_health_router 参数
|
|
26
|
-
|
|
27
|
-
| 参数 | 类型 | 默认值 | 说明 |
|
|
28
|
-
|------|------|--------|------|
|
|
29
|
-
| service_name | str | **必填** | 服务名,通常取自 `Settings.service_name` |
|
|
30
|
-
| version | str | **必填** | 版本号,通常取自 `__version__` |
|
|
31
|
-
| path | str | `"/health"` | 端点路径 |
|
|
32
|
-
| include_in_schema | bool | True | 是否在 OpenAPI 文档暴露 |
|
|
33
|
-
|
|
34
|
-
## 响应示例
|
|
35
|
-
|
|
36
|
-
```json
|
|
37
|
-
{
|
|
38
|
-
"code": 200,
|
|
39
|
-
"message": "success",
|
|
40
|
-
"data": {
|
|
41
|
-
"service": "my-api",
|
|
42
|
-
"version": "1.0.0",
|
|
43
|
-
"status": "alive"
|
|
44
|
-
},
|
|
45
|
-
"timestamp": 1775625000
|
|
46
|
-
}
|
|
47
|
-
```
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
# logger — 基于 loguru 的全局日志
|
|
2
|
-
|
|
3
|
-
> 何时读这份:当用户需要配置日志输出、轮转策略、拦截 uvicorn/SQLAlchemy 日志、或使用 JSON Lines 格式对接日志聚合系统时读取。
|
|
4
|
-
|
|
5
|
-
```python
|
|
6
|
-
from dwyeapi import logger
|
|
7
|
-
|
|
8
|
-
# 启动时调用一次(通常在 lifespan 中)
|
|
9
|
-
logger.configure(
|
|
10
|
-
level="INFO",
|
|
11
|
-
log_dir="./logs",
|
|
12
|
-
filename="app",
|
|
13
|
-
max_bytes=100 * 1024 * 1024, # 100 MB
|
|
14
|
-
retention="30 days",
|
|
15
|
-
console=True,
|
|
16
|
-
serialize=False,
|
|
17
|
-
enqueue=True,
|
|
18
|
-
intercept_stdlib=True,
|
|
19
|
-
intercept_loggers=["uvicorn", "sqlalchemy"],
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
# 获取命名 logger
|
|
23
|
-
log = logger.get_logger("users")
|
|
24
|
-
log.info("用户 %s 登录", user_id)
|
|
25
|
-
log.error("操作失败", exc_info=True)
|
|
26
|
-
log.exception("未捕获异常") # 自动 exc_info=True
|
|
27
|
-
|
|
28
|
-
# 关闭(lifespan 结束时)
|
|
29
|
-
logger.close()
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## configure 参数
|
|
33
|
-
|
|
34
|
-
| 参数 | 类型 | 默认值 | 说明 |
|
|
35
|
-
|------|------|--------|------|
|
|
36
|
-
| level | str | `"INFO"` | 最低级别:DEBUG/INFO/WARNING/ERROR/CRITICAL |
|
|
37
|
-
| log_dir | str/Path/None | None | 日志文件目录,None 表示关闭文件输出 |
|
|
38
|
-
| filename | str | `"app"` | 文件基础名,最终为 `{filename}_YYYY-MM-DD.log` |
|
|
39
|
-
| max_bytes | int | `100*1024*1024` | 单文件大小上限(字节) |
|
|
40
|
-
| retention | str/int | `"30 days"` | 保留策略(如 `"1 week"`、整数个数) |
|
|
41
|
-
| console | bool | True | 是否输出彩色日志到 stderr |
|
|
42
|
-
| console_format | str/None | None | 自定义控制台格式 |
|
|
43
|
-
| file_format | str/None | None | 自定义文件格式 |
|
|
44
|
-
| serialize | bool | False | 文件 sink 是否按 JSON Lines 序列化 |
|
|
45
|
-
| enqueue | bool | True | 是否异步写入(非阻塞、多进程安全) |
|
|
46
|
-
| intercept_stdlib | bool | True | 是否拦截 stdlib `logging` |
|
|
47
|
-
| intercept_loggers | list[str]/None | None | 限定拦截的 logger 名前缀,None 表示拦截 root |
|
|
48
|
-
|
|
49
|
-
## Logger 门面 API
|
|
50
|
-
|
|
51
|
-
`get_logger()` 返回的 `Logger` 兼容 stdlib 语义:
|
|
52
|
-
|
|
53
|
-
- `debug(msg, *args, exc_info=False)`
|
|
54
|
-
- `info(msg, *args, exc_info=False)`
|
|
55
|
-
- `warning(msg, *args, exc_info=False)`
|
|
56
|
-
- `error(msg, *args, exc_info=False)`
|
|
57
|
-
- `exception(msg, *args)` — 等价于 `error(msg, *args, exc_info=True)`
|
|
58
|
-
- `critical(msg, *args, exc_info=False)`
|
|
59
|
-
|
|
60
|
-
所有方法支持 `%s` / `%d` 等 stdlib 占位符,空 args 时保留 msg 原样。
|
|
File without changes
|
|
File without changes
|
/package/templates/claude-global/rules/{dwy-flutter-network.md → Flutter/dwy-flutter-network.md}
RENAMED
|
File without changes
|
/package/templates/claude-global/rules/{dwy-flutter-router.md → Flutter/dwy-flutter-router.md}
RENAMED
|
File without changes
|
/package/templates/claude-global/rules/{dwy-flutter-testing.md → Flutter/dwy-flutter-testing.md}
RENAMED
|
File without changes
|
/package/templates/claude-global/rules/{dwy-python-testing.md → Python/dwy-python-testing.md}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|