| T-601 |
可用别名发现(portal 别名页 + GET /api/v1/models) |
T-202, T-301, T-503 |
让调用方能自助发现「能填哪些 model 能力别名」。① 新增 GET /api/v1/models(API Key 鉴权,继承 ExternalApiView,只复用认证失败限流;不要占用生成接口的 GenerateRateThrottle 额度),仅返回 ModelAlias.is_active=True 且 ai_model.is_active=True 的可调用别名;② 响应每项只含对外安全字段:alias、operation_type、capabilities、requires_image(按 api_type/provider 规则推导,如 images_edits=true)、pricing_status、prices[{resolution, points_cost}],缺 PricingRule 时标「暂未定价」而非报错;③ 实现时不得调用 AiModel.to_resolved_model() 或任何会解密 provider key 的路径,列表接口不依赖 AI_KEY_ENCRYPTION_KEY,也不读取/输出 api_key_encrypted;④ 不暴露具体 SKU/模型名/url/key/provider 原始配置,测试必须断言响应不含 ai_model.model、ai_model.url、api_key、api_key_encrypted、extra_body 等内部字段;⑤ portal 加只读「可用模型」页(session 鉴权),同字段、人类可读、单价以点数展示,并在导航中加入入口;⑥ 含测试:API Key 成功返回结构、缺/无效 Key 拒绝、Web session 不能调用外部 API、portal 未登录跳转、页面不泄露 SKU/URL/Key、缺定价别名显示暂未定价。标注:未来接 account_alias_permission(见 Backlog)后,本清单改为「只列该用户被授权的别名」,接口与页面均按 user 过滤 |
DONE |
| T-602 |
django-admin 中文化(第 1-3 层) |
- |
让运营后台表名/分组/框架文字显示中文,仅显示层、不改业务逻辑与 DB 结构。第 1 层:config/settings.py 设 LANGUAGE_CODE='zh-hans' + USE_I18N=True,使 Django 自带 admin 界面与内置 auth(用户/组/权限)中文化(无迁移)。第 2 层:各 app AppConfig.verbose_name 设中文分组名(users/billing/ai/api/portal),无迁移。第 3 层:各模型 Meta.verbose_name/verbose_name_plural 设中文表名(User/UserWallet/ApiKey/PointsLedger/CallRecord/PricingRule/ExchangeRate/RechargeOrder/AiModel/ModelAlias/AiConfigAuditLog)。不含第 4 层字段级 verbose_name(字段列名保持英文,见 T-603)。第 3 层会产出 AlterModelOptions 等无 DB 变更迁移;验收:makemigrations(仅 options 变更)→ migrate → check 0 issues → test 全绿 → /admin/ 目视中文化,并在 ../progress.md 留证据 |
DONE |
| T-603 |
django-admin 中文化(第 4 层·字段级) |
T-602 |
给各模型字段加中文 verbose_name,使 admin 列表/编辑页的字段标签显示中文,在 T-602(1-3 层)之上做。范围覆盖 users/billing/ai(及 portal 相关)模型的业务字段,例如 points_balance→点数余额、key_prefix→Key 前缀、points_delta→点数变动、balance_after→变动后余额、amount_money→金额、exchange_rate→汇率、points_granted→到账点数、api_type/capabilities/is_active/created_at 等;admin.py 里自定义显示方法(@admin.display(description=...))同步中文。仅显示层:verbose_name 只改展示,不改字段名、不改代码引用、英文字段名保持不变;产出的是 AlterField(metadata-only,无 DB schema 变更)迁移。验收:makemigrations(仅 AlterField,无 schema 变更)→ migrate → check 0 issues → test 全绿 → /admin/ 字段标签目视中文,并在 ../progress.md 留证据。已人工确认 admin 字段中文化,目标后台回归测试通过 |
DONE |
| T-605 |
落实「免邮箱验证」策略 |
T-501 |
把 ACCOUNT_EMAIL_VERIFICATION="none"(注册即可用、不发验证邮件、邮箱仍必填且唯一)作为既定策略清理落地:① config/settings.py 把 ACCOUNT_EMAIL_VERIFICATION = "none"#"mandatory" 改为干净的 "none"(去行内注释),并把免验证下无实际意义的 ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION 设为 False;② 更新 apps/portal/tests.py 里假设 mandatory 的 2 条测试(signup 不再依赖验证邮件、未验证也可直接登录),改为断言「注册后可直接登录」;③ 同步全项目文档口径(02-requirements/05-coding-rules/api/04-architecture/03-tech-stack/routes/env/deployment/00-ai-start-here 中「邮箱验证」→「免邮箱验证,邮箱仍唯一」)。验收:check 通过,apps.portal 26 tests OK;全量 manage.py test 已尝试,跑到 95/131 后因远程 MySQL 连接超时失败(WinError 10051/10060),非本任务断言失败,详见 ../progress.md |
DONE |
| T-604 |
中文敏感词本地过滤(本地 keyword provider) |
T-302, T-401 |
按 moderation.md 实施。范围收紧:T-604 只做输入 prompt 的本地敏感词快筛,不做云内容安全、不做输出审核、不做图片审核。关键时序:serializer 后先审 prompt,命中即 400 content_blocked;不得先下载 image_url,不得预扣点,不写 CallRecord / PointsLedger,不调上游。核心实现:新增 apps.moderation、SensitiveWord 模型/admin/迁移、keyword provider、归一化管线、Aho-Corasick matcher;ahocorapy 作为候选依赖,编码前必须验证 PyPI 可用性和 API 形状。缓存:matcher 进程内缓存,词库变更用共享 cache 版本号失效,不能只靠 post_save signal;生产依赖共享 cache。配置:MODERATION_ENABLED=false 默认 no-op;启用时 MODERATION_PROVIDER=keyword;MVP SensitiveWord.action 只支持 block。验收:check/test 全绿,覆盖 no-op、命中拦截且不扣点/不建记录/不调上游/不下载图片、归一化防绕过、词库变更后 matcher 重建;真实词库数据不进仓库,__pycache__ 不进 Git |
DONE |
| T-606 |
公开首页 + 客户端下载入口 |
T-501 |
给网站补「前门」并提供桌面端下载。路由改造:/ 从「重定向到 /dashboard」改为公开首页(匿名可访问、不跳登录);已登录用户显示「进入控制台」,匿名显示「注册/登录 + 下载客户端」。首页内容(SSR 模板):项目一句话介绍(生成标题/图片、按点数计费)+ 三步上手(注册→充值→建 API Key→桌面端填 Key)+ 下载入口 + 文档链接。DownloadRelease 模型:platform(如 windows)、version、file(FileField,前期存 MEDIA_ROOT、服务器托管) + external_url(URLField 可选,后续切对象存储/CDN 用,有则优先)、sha256、is_current(每平台仅一个当前版本)、release_notes、时间戳;admin 可上传安装包并标记当前版本;迁移。下载区块:展示当前 release 的版本、下载按钮、SHA256 校验值、可选 release notes;无 current release 时优雅提示「暂未发布」。托管策略:前期安装包放本服务器(生产由 Nginx 直接服务 media/下载文件、不走 Django,与 T-403 static/media serving 一致,大文件不占 gunicorn worker);external_url 预留,后续切对象存储只改后台链接不改代码。安全:下载走 HTTPS,页面展示 SHA256 供校验;代码签名作为决策登记——未签名 Windows 安装包会被 SmartScreen 拦「未知发布者」、macOS 被 Gatekeeper 拦,首页先给「如何忽略警告」说明,正式签名后续补(挂 Backlog / deployment)。含测试:/ 匿名 200 不跳登录、下载区展示当前 release、无 current release 优雅处理、已登录用户显示「进入控制台」。视觉原型(已定 v1):按 prototypes/cmhub-homepage-v1.svg 落地——「生成台」方向:靛蓝=生成 / 琥珀=点数;Hero 为「商品图 + 一句话 → 吸睛标题 + 生成主图 + 点数计量」转化图;四步上手 01–04、两张能力卡、深色计费 band、下载区(版本/SHA256/未签名提示);配色、间距、结构照此原型转成 Django 模板(Bootstrap + 本地 static)。视觉一致性(brand token):按 brand.md 抽出共享 CSS 变量(如 apps/portal/static/portal/brand.css),首页与现有 portal 页面(dashboard/记录/充值/API Key)一起套用同一套 token(把配色/字体变量灌进现有 Bootstrap,各页只引用变量不散写 hex),确保落地页与登录后控制台风格一致——不是只做漂亮首页。验收 check/test/init 全绿并在 ../progress.md 留证据 |
DONE |
| T-607 |
桌面端最新版本检查接口 |
T-606 |
给桌面端自动检查更新提供只读 JSON 合约。新增路由:GET /api/v1/client/releases/latest?platform=windows,公开匿名可访问,不需要 API Key、不读取用户、不扣点、不占用生成接口限流;只返回发布元数据,不返回本地文件系统路径、后台 ID、内部状态或任何用户数据。请求参数:platform 支持 windows / macos / linux,缺省按 windows;非法平台返回 400 bad_request。响应结构:有当前版本时返回 {platform, release:{version, download_url, sha256, release_notes, published_at}};无当前版本或当前版本没有下载地址时返回 {platform, release:null, message:"暂未发布"} 且 HTTP 200,方便客户端安静处理。数据来源:复用 DownloadRelease,只查 platform + is_current=True;download_url 继续按 external_url 优先,否则由 file.url 生成绝对 HTTPS URL;published_at 可先使用 updated_at。缓存与安全:可加短 TTL 公共缓存(如 60 秒);生产下载仍走 HTTPS + SHA256 校验;不得把 MEDIA_ROOT 或服务器路径暴露给客户端。含测试:匿名无 Key 可访问;当前 release 返回完整结构和绝对下载 URL;无 current release 返回 release:null;非法 platform 返回 400;external_url 优先于 file;响应不含本地路径、模型/密钥/用户字段;check/目标测试通过并在 ../progress.md 留证据 |
DONE |
| T-608 |
新用户注册赠送 100 点试用点数 |
T-501, T-203, T-401 |
把 2026-07-08 的产品口径落地为账本安全实现。业务口径:只对 T-608 上线后的新注册用户自动发放 100 点;历史用户是否补发不在本任务内,需单独审批和批处理任务。数据与服务:新增 PointsLedger.ChangeType.SIGNUP_BONUS(展示为注册赠点)和 MySQL 兼容的数据库级幂等兜底 SignupBonusGrant(user UNIQUE),不要用 partial unique / 条件唯一约束;新增 apps.billing.services.grant_signup_bonus(user, points=100),在事务内锁/创建钱包、发放点数、写 PointsLedger(signup_bonus,+100,balance_after,reason),重复调用或并发触发不得重复发放。接入点:allauth adapter 注册成功后调用 billing 服务,不得在 portal 直接写 points_balance;失败要清晰暴露并保持注册 / 账务一致性。页面与文案:公开首页、注册页、dashboard/记录页等用户端文案同步「注册送 100 点」;点数记录页能展示注册赠点正向流水;admin 可检索该流水和注册赠点记录。防刷:免邮箱验证策略下显式配置 ACCOUNT_SIGNUP_RATE_LIMIT(默认 20/m/ip),由 allauth signup rate limit 执行;图形验证码 / 人机验证仍作为上线前风控增强项。测试:注册后余额为 100 且有一条 signup_bonus 流水;重复调用服务不重复加点;并发触发不重复发放;现有用户不会自动补发;dashboard / 点数记录显示一致;check/迁移/目标测试通过并在 ../progress.md 留证据 |
DONE |
| T-609 |
桌面端版本检查接口增加强制更新标记 |
T-607 |
在 GET /api/v1/client/releases/latest?platform=windows 的当前版本响应中,为 release 对象新增布尔字段 force_update,用于桌面端判断是否必须升级。接口兼容:只新增字段,不删除既有 version / download_url / sha256 / release_notes / published_at 字段;无当前版本或无下载地址时仍返回 release:null,不返回 force_update。数据来源:在 DownloadRelease 增加 force_update 布尔字段,默认 false;django-admin 可编辑并在列表展示。示例:{platform:"windows", release:{version:"0.1.1", download_url:"...0.1.1.zip", release_notes:"优化了ai模块的生图的功能", force_update:true}}。安全:接口仍公开匿名只读,不读取用户、不扣点、不暴露后台 ID、本地文件路径或内部状态。文档:同步 api.md、routes.md、04-architecture.md、current-state.md、版本接口对接文档口径。测试:覆盖 force_update=true / 默认 false、无 current release 不返回该字段、响应字段白名单更新、admin 字段可见;makemigrations / migrate / check / 目标测试通过并在 ../progress.md 留证据 |
DONE |
| T-610 |
首页导入模板下载入口 |
T-606 |
在公开首页“下载客户端”按钮右侧新增“下载导入模板”链接,让用户下载桌面端导入商品数据使用的 Excel 模板。产品口径:模板下载是公开资源,匿名可见;不需要登录、不需要 API Key、不扣点、不占用生成接口限流。后台配置:新增独立 ImportTemplate(不要复用 DownloadRelease,两者生命周期不同),字段建议包含 name、file(FileField,MEDIA_ROOT/import_templates/)、external_url(可选,有则优先,后续切 CDN/对象存储)、sha256(可选)、is_current、notes、created_at、updated_at;admin 可上传/更新模板并标记当前模板;每次保存 current 时把其他 current 置为 false,避免使用 MySQL 不支持的条件唯一约束。首页行为:HomeView 同时读取 Windows 当前 DownloadRelease 和当前 ImportTemplate;有当前模板且有可下载地址时,在“下载客户端”右侧展示“下载导入模板”链接;无模板时不显示该链接或显示不可用提示,不能影响客户端下载入口和首页 200。托管策略:本地上传模板复用生产 Nginx /media/ 静态托管,不经 Django/Gunicorn 传大文件;external_url 优先于 file.url,响应/页面不得暴露本地 MEDIA_ROOT 或服务器文件路径。UI:保持 T-606 下载区现有布局,桌面端按钮为主按钮,导入模板为次级链接/按钮;移动端需换行不重叠。文档:同步 02-requirements.md、04-architecture.md、routes.md、api.md、current-state.md。测试:覆盖首页有模板时展示链接且 href 为绝对/可访问 media URL,external_url 优先,本地路径不泄露,无模板时首页正常且不显示错误;admin 字段可见;makemigrations / migrate / check / 目标测试通过并在 ../progress.md 留证据 |
DONE |