A8 多套交付包(packaging_task.py): - 修复交付包只打第1条混乱note的bug,按ImageCandidate.strategy分A/B/C组 - 每组生独立note_0N夹(6图+文案.txt),同seq留最新去重,老数据兼容 - task74端到端验:3套各6图,独立agent7项交叉验证全过 M4 归档(tasks.py/exports.py/前端): - list_tasks加date_from/date_to/product_id筛选+product_name批量填(防N+1) - 新增exports.py:产品JSON导出+标杆CSV导出(UTF-8 BOM) - 前端HistoryFilters日期/产品筛选+产品列+打回原因红banner - response.py加raise_param_error;独立agent验A1/A2/A9通过 R5 产品多图(product_images.py/020迁移/前端): - product_images表+5端点(上传/列/改场景/设主图/删图) - 生图按ROLE_SCENE_PREFERENCE选对应场景图,回落primary - 前端ProductImageManager多图画廊 R6 账号config拆页(settings/): - 配置页按角色拆/settings(运营+组长+admin)+/config(仅admin) - Key只显末4位不显余额(守红线) 核销表对齐真实代码状态:D1改稿框/M7裂变/E12评图分纠偏为已完成(曾漏回写) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
68 lines
2.1 KiB
Python
68 lines
2.1 KiB
Python
"""
|
||
app/core/response.py — 统一响应包络
|
||
成功:{ "code": 0, "data": {...} }
|
||
失败:{ "code": <错误码>, "message": "用户可读信息" }
|
||
HTTP 状态码与业务 code 分离(契约§0)。
|
||
"""
|
||
|
||
from typing import Any
|
||
|
||
from fastapi import HTTPException
|
||
from fastapi.responses import JSONResponse
|
||
|
||
from app.constants.enums import ErrorCode
|
||
|
||
|
||
def ok(data: Any = None) -> dict:
|
||
"""标准成功响应。"""
|
||
return {"code": ErrorCode.SUCCESS, "data": data}
|
||
|
||
|
||
def err(code: int, message: str) -> dict:
|
||
"""标准失败响应体(不含 HTTP 状态,由调用方决定)。"""
|
||
return {"code": code, "message": message}
|
||
|
||
|
||
def paginate(items: list, total: int, page: int, page_size: int) -> dict:
|
||
"""分页数据包装。"""
|
||
return {
|
||
"items": items,
|
||
"total": total,
|
||
"page": page,
|
||
"page_size": page_size,
|
||
}
|
||
|
||
|
||
# ── 常用异常 ──────────────────────────────────────────────
|
||
class CloverHTTPException(HTTPException):
|
||
"""携带业务 code 的 HTTP 异常,供全局 handler 格式化。"""
|
||
|
||
def __init__(self, http_status: int, code: int, message: str):
|
||
super().__init__(status_code=http_status, detail=message)
|
||
self.biz_code = code
|
||
self.biz_message = message
|
||
|
||
|
||
def raise_not_found(message: str = "资源不存在") -> None:
|
||
raise CloverHTTPException(404, ErrorCode.NOT_FOUND, message)
|
||
|
||
|
||
def raise_forbidden(message: str = "无权限访问") -> None:
|
||
raise CloverHTTPException(403, ErrorCode.FORBIDDEN, message)
|
||
|
||
|
||
def raise_unauthorized(message: str = "未认证或 Token 失效") -> None:
|
||
raise CloverHTTPException(401, ErrorCode.UNAUTHORIZED, message)
|
||
|
||
|
||
def raise_business(message: str) -> None:
|
||
raise CloverHTTPException(422, ErrorCode.BUSINESS_ERROR, message)
|
||
|
||
|
||
def raise_param_error(message: str = "参数非法") -> None:
|
||
raise CloverHTTPException(400, ErrorCode.PARAM_INVALID, message)
|
||
|
||
|
||
def raise_state_invalid(message: str = "状态机非法流转") -> None:
|
||
raise CloverHTTPException(409, ErrorCode.STATE_INVALID, message)
|