create-dwy 0.5.1 → 0.6.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.
Files changed (156) hide show
  1. package/bin/index.js +1 -1
  2. package/package.json +2 -2
  3. package/src/create.js +61 -37
  4. package/src/sync.js +212 -61
  5. package/templates/claude-global/CLAUDE.md +92 -0
  6. package/templates/claude-global/rules/dwy-android-core.md +610 -0
  7. package/templates/claude-global/rules/dwy-api-design.md +110 -0
  8. package/templates/claude-global/rules/dwy-db-migration.md +46 -0
  9. package/templates/claude-global/rules/dwy-flutter-core.md +564 -0
  10. package/templates/claude-global/rules/dwy-flutter-network.md +222 -0
  11. package/templates/claude-global/rules/dwy-flutter-router.md +97 -0
  12. package/templates/claude-global/rules/dwy-flutter-testing.md +94 -0
  13. package/templates/claude-global/rules/dwy-git-commit-convention.md +82 -0
  14. package/templates/claude-global/rules/dwy-git-security.md +53 -0
  15. package/templates/claude-global/rules/dwy-payload-limits.md +178 -0
  16. package/templates/claude-global/rules/{prefer-open-source.md → dwy-prefer-open-source.md} +4 -0
  17. package/templates/claude-global/rules/dwy-project-structure.md +181 -0
  18. package/templates/claude-global/rules/{python-code-style.md → dwy-python-core.md} +292 -358
  19. package/templates/claude-global/rules/dwy-python-fastapi.md +272 -0
  20. package/templates/claude-global/rules/dwy-python-orm.md +61 -0
  21. package/templates/claude-global/rules/{backend-security.md → dwy-python-security.md} +99 -72
  22. package/templates/claude-global/rules/dwy-python-testing.md +53 -0
  23. package/templates/claude-global/rules/{server-security.md → dwy-server-security.md} +1 -2
  24. package/templates/claude-global/rules/dwy-tdd-development.md +70 -0
  25. package/templates/claude-global/rules/dwy-tech-stack.md +60 -0
  26. package/templates/claude-global/rules/dwy-vue-api.md +146 -0
  27. package/templates/claude-global/rules/{vue-code-style.md → dwy-vue-core.md} +338 -152
  28. package/templates/claude-global/rules/dwy-vue-pinia.md +75 -0
  29. package/templates/claude-global/rules/dwy-vue-testing.md +102 -0
  30. package/templates/claude-global/skills/dwy-deploy-audit/SKILL.md +481 -0
  31. package/templates/claude-global/skills/dwy-deploy-audit/references/checks-data.md +117 -0
  32. package/templates/claude-global/skills/dwy-deploy-audit/references/checks-network.md +74 -0
  33. package/templates/claude-global/skills/dwy-deploy-audit/references/checks-runtime.md +184 -0
  34. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_base.sh +39 -0
  35. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_capacity.sh +278 -0
  36. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_db.sh +122 -0
  37. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_docker.sh +249 -0
  38. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_env.sh +64 -0
  39. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_https.sh +60 -0
  40. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_logs.sh +209 -0
  41. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_nginx.sh +92 -0
  42. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_resilience.sh +157 -0
  43. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_secrets.sh +327 -0
  44. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_services.sh +77 -0
  45. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_ssh.sh +56 -0
  46. package/templates/claude-global/skills/dwy-deploy-audit/scripts/run_all.sh +105 -0
  47. package/templates/claude-global/skills/dwy-docker/SKILL.md +187 -0
  48. package/templates/claude-global/skills/dwy-docker/references/ask-templates.md +142 -0
  49. package/templates/claude-global/skills/dwy-docker/references/templates.md +479 -0
  50. package/templates/claude-global/skills/dwy-docker/references/version-rules.md +76 -0
  51. package/templates/claude-global/skills/dwy-docker/scripts/query_dockerhub.py +204 -0
  52. package/templates/claude-global/skills/dwy-docker/scripts/scan_images.sh +185 -0
  53. package/templates/claude-global/skills/dwy-eapi/SKILL.md +433 -0
  54. package/templates/claude-global/skills/dwy-eapi/references/tasks-integration-guide.md +387 -0
  55. package/templates/claude-global/skills/dwy-ekit/SKILL.md +85 -0
  56. package/templates/claude-global/skills/dwy-ekit/references/data.md +135 -0
  57. package/templates/claude-global/skills/dwy-ekit/references/form.md +68 -0
  58. package/templates/claude-global/skills/dwy-ekit/references/request.md +183 -0
  59. package/templates/claude-global/skills/dwy-ekit/references/ui.md +109 -0
  60. package/templates/claude-global/skills/{dwy-frontend-eui → dwy-eui}/SKILL.md +129 -15
  61. package/templates/claude-global/skills/dwy-eui/references/eui-design-guide.md +257 -0
  62. package/templates/claude-global/skills/dwy-eui/references/eui-integration-guide.md +302 -0
  63. package/templates/claude-global/skills/dwy-eui/references/eui-landing-design-guide.md +437 -0
  64. package/templates/claude-global/skills/dwy-fullstack-scaffold/SKILL.md +214 -0
  65. package/templates/claude-global/skills/dwy-mirror-source/SKILL.md +275 -0
  66. package/templates/claude-global/skills/dwy-mirror-source/references/dockerfile-snippets.md +228 -0
  67. package/templates/claude-global/skills/dwy-mirror-source/references/mirror-providers.md +150 -0
  68. package/templates/claude-global/skills/dwy-mirror-source/scripts/__pycache__/check_mirrors.cpython-313.pyc +0 -0
  69. package/templates/claude-global/skills/dwy-mirror-source/scripts/apply_mirrors.py +666 -0
  70. package/templates/claude-global/skills/dwy-mirror-source/scripts/check_mirrors.py +868 -0
  71. package/templates/claude-global/skills/dwy-pentest/SKILL.md +344 -0
  72. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__init__.py +1 -0
  73. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/__init__.cpython-311.pyc +0 -0
  74. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/config.cpython-311.pyc +0 -0
  75. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/executor.cpython-311.pyc +0 -0
  76. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/severity.cpython-311.pyc +0 -0
  77. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/validator.cpython-311.pyc +0 -0
  78. package/templates/claude-global/skills/dwy-pentest/scripts/lib/config.py +109 -0
  79. package/templates/claude-global/skills/dwy-pentest/scripts/lib/executor.py +157 -0
  80. package/templates/claude-global/skills/dwy-pentest/scripts/lib/severity.py +96 -0
  81. package/templates/claude-global/skills/dwy-pentest/scripts/lib/validator.py +113 -0
  82. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_auth.py +289 -0
  83. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_recon.py +217 -0
  84. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_report.py +343 -0
  85. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_scan.py +205 -0
  86. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_ssl.py +208 -0
  87. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_vuln.py +205 -0
  88. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_web.py +330 -0
  89. package/templates/claude-global/skills/dwy-pentest/scripts/pyproject.toml +14 -0
  90. package/templates/claude-global/skills/dwy-pentest/scripts/setup_tools.sh +97 -0
  91. package/templates/claude-global/skills/dwy-pentest/scripts/uv.lock +415 -0
  92. package/templates/claude-global/skills/{dwy-release → dwy-publish}/SKILL.md +42 -18
  93. package/templates/claude-global/skills/dwy-vue-core/SKILL.md +890 -0
  94. package/templates/project/backend/.dwy.hbs +5 -0
  95. package/templates/project/backend/.python-version +1 -0
  96. package/templates/project/backend/CLAUDE.md.hbs +40 -0
  97. package/templates/project/{web/backend → backend}/alembic/script.py.mako +2 -0
  98. package/templates/project/{web/backend → backend}/alembic.ini.hbs +0 -1
  99. package/templates/project/backend/deploy.sh.hbs +18 -0
  100. package/templates/project/{web → backend}/docker-compose.prod.yml.hbs +1 -1
  101. package/templates/project/{web/backend → backend}/pyproject.toml.hbs +4 -5
  102. package/templates/project/{web/backend → backend}/src/app/config.py.hbs +1 -1
  103. package/templates/project/backend/src/app/database.py +10 -0
  104. package/templates/project/backend/src/app/dependencies.py +5 -0
  105. package/templates/project/{web/backend → backend}/src/app/main.py.hbs +3 -3
  106. package/templates/project/backend/src/app/models/base.py +5 -0
  107. package/templates/project/web/CLAUDE.md.hbs +29 -36
  108. package/templates/project/web/{frontend → apps/web}/index.html.hbs +1 -0
  109. package/templates/project/web/{frontend → apps/web}/package.json.hbs +4 -3
  110. package/templates/project/web/apps/web/src/App.vue +9 -0
  111. package/templates/project/web/apps/web/src/api/client.ts +5 -0
  112. package/templates/project/web/apps/web/src/composables/.gitkeep +0 -0
  113. package/templates/project/web/apps/web/src/layouts/DefaultLayout.vue +8 -0
  114. package/templates/project/web/{frontend → apps/web}/src/router/index.ts +1 -1
  115. package/templates/project/web/{frontend → apps/web}/src/stores/auth.ts +4 -2
  116. package/templates/project/web/apps/web/src/style.css +2 -0
  117. package/templates/project/web/apps/web/src/types/router.d.ts +8 -0
  118. package/templates/project/web/apps/web/src/views/HomeView.vue +8 -0
  119. package/templates/project/web/apps/web/tsconfig.app.json +18 -0
  120. package/templates/project/web/apps/web/tsconfig.json +6 -0
  121. package/templates/project/web/{frontend → apps/web}/vite.config.ts.hbs +7 -1
  122. package/templates/project/web/package.json.hbs +16 -0
  123. package/templates/project/web/packages/.gitkeep +0 -0
  124. package/templates/project/web/pnpm-workspace.yaml +3 -0
  125. package/templates/claude-global/hooks/post-sync-claude-global.sh +0 -72
  126. package/templates/claude-global/rules/agent-team-execution.md +0 -138
  127. package/templates/claude-global/rules/agent-team-patterns.md +0 -157
  128. package/templates/claude-global/rules/git-commit-convention.md +0 -106
  129. package/templates/claude-global/rules/git-security.md +0 -144
  130. package/templates/claude-global/skills/dwy-backend-base/SKILL.md +0 -279
  131. package/templates/claude-global/skills/dwy-frontend-core/SKILL.md +0 -181
  132. package/templates/project/web/.claude/commands/deploy.md +0 -4
  133. package/templates/project/web/backend/src/app/models/base.py +0 -5
  134. package/templates/project/web/deploy.sh.hbs +0 -113
  135. package/templates/project/web/frontend/src/App.vue +0 -3
  136. package/templates/project/web/frontend/src/api/client.ts +0 -13
  137. package/templates/project/web/frontend/src/style.css +0 -1
  138. package/templates/project/web/frontend/src/views/HomeView.vue +0 -5
  139. package/templates/project/web/frontend/tsconfig.app.json +0 -14
  140. package/templates/project/web/frontend/tsconfig.json +0 -4
  141. /package/templates/{project/web/backend → claude-global/skills/dwy-pentest/scripts}/.python-version +0 -0
  142. /package/templates/project/{web/backend → backend}/Dockerfile +0 -0
  143. /package/templates/project/{web/backend → backend}/alembic/env.py.hbs +0 -0
  144. /package/templates/{claude-global/skills → project/backend/alembic/versions}/.gitkeep +0 -0
  145. /package/templates/project/{web → backend}/docker-compose.dev.yml.hbs +0 -0
  146. /package/templates/project/{web/backend → backend}/src/app/__init__.py +0 -0
  147. /package/templates/project/{web/backend → backend}/src/app/models/__init__.py +0 -0
  148. /package/templates/project/{web/backend → backend}/src/app/routers/__init__.py +0 -0
  149. /package/templates/project/{web/backend → backend}/src/app/routers/health.py +0 -0
  150. /package/templates/project/{web/backend → backend}/src/app/schemas/__init__.py +0 -0
  151. /package/templates/project/{web/backend → backend}/src/app/services/__init__.py +0 -0
  152. /package/templates/project/{web/backend → backend}/tests/__init__.py +0 -0
  153. /package/templates/project/{web/backend → backend}/tests/conftest.py.hbs +0 -0
  154. /package/templates/project/{web/backend/alembic/versions → mobile}/.gitkeep +0 -0
  155. /package/templates/project/web/{frontend → apps/web}/src/components/.gitkeep +0 -0
  156. /package/templates/project/web/{frontend → apps/web}/src/main.ts +0 -0
@@ -0,0 +1,433 @@
1
+ ---
2
+ name: dwy-eapi
3
+ description: "dwyeapi FastAPI 后端基础设施速查。触发条件:使用 FastAPI 构建后端、配置数据库/Redis/JWT/异常处理、耗时任务处理时。"
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 配置、取消机制等详细说明。