feat(huya): 协作台账支持待补资料一键建单与表单暂存
This commit is contained in:
@@ -267,6 +267,9 @@ def create_external_order(
|
||||
):
|
||||
"""新建一行(可同时贴一份外部 CK)。订单号唯一,重复会 409。
|
||||
|
||||
``draft=true``:先建单后补资料 —— 订单号 + 店铺即可,状态进「待补资料」,
|
||||
CK / 礼包名 / 主页截图稍后在详情里补(不跑探针)。
|
||||
|
||||
``require_verified``(默认 true):人工新建表单的严格模式 —— 订单编号 / 店铺 / CK /
|
||||
礼包名 / 主页截图必填,且 CK 必须探针通并读到绑定角色,否则 400
|
||||
(粘贴导入走另一个接口,不受影响)。
|
||||
@@ -284,6 +287,7 @@ def create_external_order(
|
||||
home_images=req.home_images,
|
||||
remark=req.remark,
|
||||
require_verified=req.require_verified,
|
||||
draft=req.draft,
|
||||
)
|
||||
except collab.CollabError as exc:
|
||||
raise _http_error(exc) from exc
|
||||
|
||||
@@ -1170,11 +1170,15 @@ class HuyaExternalOrderCreate(BaseModel):
|
||||
|
||||
``require_verified``(默认开):人工新建表单的严格模式 —— 订单编号 / 店铺 / CK /
|
||||
礼包名 / **主页截图**必填,且 CK 必须探针通并读到绑定角色;粘贴导入不走这条路。
|
||||
|
||||
``draft``(默认关):先建单后补资料 —— 只登记订单号 + 店铺,状态进「待补资料」,
|
||||
CK / 礼包名 / 主页截图都可以稍后在详情里补(2026-10-17 用户需求)。
|
||||
"""
|
||||
|
||||
order_no: str = Field(..., min_length=1, max_length=64)
|
||||
shop: str = Field("", max_length=64)
|
||||
require_verified: bool = True
|
||||
draft: bool = False
|
||||
owner_id: int | None = None
|
||||
ck_text: str = ""
|
||||
bind_douyu_account: str = Field("", max_length=128)
|
||||
|
||||
@@ -51,6 +51,9 @@ from .huya_service import build_web_cookie_from_raw, parse_huya_cookie_line
|
||||
# 业务状态机(一单卡在哪一步)
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
# 「待补资料」是流程链的起点:只登记订单号 + 店铺,CK / 礼包 / 主页截图等客户资料
|
||||
# 到了再补(2026-10-17 用户需求)。资料齐全后才能进「待处理」。
|
||||
STATUS_INCOMPLETE = "incomplete"
|
||||
STATUS_PENDING = "pending"
|
||||
STATUS_BOUND = "bound"
|
||||
STATUS_RECHARGED = "recharged"
|
||||
@@ -60,6 +63,7 @@ STATUS_FAILED = "failed"
|
||||
STATUS_VOID = "void"
|
||||
|
||||
ORDER_STATUSES: tuple[str, ...] = (
|
||||
STATUS_INCOMPLETE,
|
||||
STATUS_PENDING,
|
||||
STATUS_BOUND,
|
||||
STATUS_RECHARGED,
|
||||
@@ -70,6 +74,7 @@ ORDER_STATUSES: tuple[str, ...] = (
|
||||
)
|
||||
|
||||
ORDER_STATUS_LABELS = {
|
||||
STATUS_INCOMPLETE: "待补资料",
|
||||
STATUS_PENDING: "待处理",
|
||||
# 人工核对过「探针读到的绑定角色」与「主页截图」一致,才允许进这一步(见 _assert_can_enter_bound)
|
||||
STATUS_BOUND: "已确认绑定",
|
||||
@@ -84,6 +89,7 @@ ORDER_STATUS_LABELS = {
|
||||
|
||||
# 流程链(时间戳同步与「能不能向前走」都只看这条链);failed / void 是**旁路**终态。
|
||||
STATUS_FLOW: tuple[str, ...] = (
|
||||
STATUS_INCOMPLETE,
|
||||
STATUS_PENDING,
|
||||
STATUS_BOUND,
|
||||
STATUS_RECHARGED,
|
||||
@@ -93,6 +99,7 @@ STATUS_FLOW: tuple[str, ...] = (
|
||||
|
||||
# 「向前一步」的下一站;done 是终点,failed / void 是任意态可达的旁路,都不在这条链上。
|
||||
NEXT_STATUS = {
|
||||
STATUS_INCOMPLETE: STATUS_PENDING,
|
||||
STATUS_PENDING: STATUS_BOUND,
|
||||
STATUS_BOUND: STATUS_RECHARGED,
|
||||
STATUS_RECHARGED: STATUS_SCRIPTED,
|
||||
@@ -101,11 +108,12 @@ NEXT_STATUS = {
|
||||
|
||||
# 阶段序号:用来同步时间戳(回退时要清掉后面阶段的时间戳,否则状态与时间戳互相矛盾)。
|
||||
STAGE_INDEX = {
|
||||
STATUS_PENDING: 0,
|
||||
STATUS_BOUND: 1,
|
||||
STATUS_RECHARGED: 2,
|
||||
STATUS_SCRIPTED: 3,
|
||||
STATUS_DONE: 4,
|
||||
STATUS_INCOMPLETE: 0,
|
||||
STATUS_PENDING: 1,
|
||||
STATUS_BOUND: 2,
|
||||
STATUS_RECHARGED: 3,
|
||||
STATUS_SCRIPTED: 4,
|
||||
STATUS_DONE: 5,
|
||||
}
|
||||
|
||||
STATUS_TIMESTAMP_FIELDS = {
|
||||
@@ -451,6 +459,26 @@ def _assert_can_enter_bound(order: HuyaExternalOrder) -> None:
|
||||
)
|
||||
|
||||
|
||||
def _assert_can_leave_incomplete(order: HuyaExternalOrder) -> None:
|
||||
"""离开「待补资料」的前置校验:客户资料必须齐全。
|
||||
|
||||
只在 ``incomplete`` 这个起点用:CK / 礼包名 / 主页截图至少各一份,
|
||||
否则「待处理」只是个空壳(后面谁都处理不了)。作废 / 判失败是旁路,不拦。
|
||||
"""
|
||||
missing: list[str] = []
|
||||
if not str(order.cookie or "").strip():
|
||||
missing.append("外部 CK")
|
||||
if not str(order.gift_name or "").strip():
|
||||
missing.append("礼包名")
|
||||
if not images_of(order, "home_images"):
|
||||
missing.append("主页截图(至少 1 张)")
|
||||
if missing:
|
||||
raise CollabError(
|
||||
f"「{ORDER_STATUS_LABELS[STATUS_INCOMPLETE]}」还不能往前改,缺少:{'、'.join(missing)}",
|
||||
code="incomplete_precondition",
|
||||
)
|
||||
|
||||
|
||||
def _sync_stage_timestamps(
|
||||
order: HuyaExternalOrder, target: str, now: datetime
|
||||
) -> None:
|
||||
@@ -520,6 +548,13 @@ def set_status(
|
||||
code="status_from_void",
|
||||
)
|
||||
|
||||
if from_status == STATUS_INCOMPLETE and target not in (
|
||||
STATUS_VOID,
|
||||
STATUS_FAILED,
|
||||
):
|
||||
# 「待补资料」是起点:客户资料没齐之前不许往前,否则下一个环节无从下手。
|
||||
_assert_can_leave_incomplete(order)
|
||||
|
||||
if target == STATUS_BOUND:
|
||||
_assert_can_enter_bound(order)
|
||||
|
||||
@@ -2045,6 +2080,7 @@ def create_order(
|
||||
home_images: Any = None,
|
||||
remark: str = "",
|
||||
require_verified: bool = False,
|
||||
draft: bool = False,
|
||||
client: Any | None = None,
|
||||
commit: bool = True,
|
||||
) -> HuyaExternalOrder:
|
||||
@@ -2053,6 +2089,10 @@ def create_order(
|
||||
**负责人不填就是当前登录用户**(创建人)—— 新建时不用选,谁建的单归谁;
|
||||
要转给别人在列表里点一下改负责人即可(负责人不是权限边界,见设计文档 §7.1)。
|
||||
|
||||
``draft=True``(**先建单后补资料**):只要求订单号,店铺也允许先空着;
|
||||
状态直接进「待补资料」,CK / 礼包名 / 主页截图都可以等客户资料到了再补。
|
||||
这条路径不跑探针、不做严格校验(2026-10-17 用户需求)。
|
||||
|
||||
``require_verified=True``(**人工新建表单用**,粘贴导入不传)时按"人工必填项"严格校验:
|
||||
**订单编号 / 店铺 / CK / 礼包名 / 主页截图**都不许空,且 CK 必须探针通、
|
||||
**读到绑定角色**才让建;读不到角色直接 400 —— 这条在服务层做,前端只是体验(AGENTS §2)。
|
||||
@@ -2065,6 +2105,10 @@ def create_order(
|
||||
_assert_order_no_available(db, order_no)
|
||||
home_images_keys = normalize_images(home_images, field="home_images")
|
||||
|
||||
if draft:
|
||||
# 草稿单不验 CK、不跑探针:先落一行占住订单号,资料到了在详情里补。
|
||||
require_verified = False
|
||||
|
||||
order = HuyaExternalOrder(
|
||||
order_no=order_no,
|
||||
shop=clean_text(shop, field="shop"),
|
||||
@@ -2074,7 +2118,7 @@ def create_order(
|
||||
remark=clean_text(remark, field="remark"),
|
||||
bind_images=[],
|
||||
home_images=home_images_keys,
|
||||
status=STATUS_PENDING,
|
||||
status=STATUS_INCOMPLETE if draft else STATUS_PENDING,
|
||||
ck_status=CK_UNVERIFIED,
|
||||
created_by=current.id,
|
||||
updated_by=current.id,
|
||||
@@ -2140,6 +2184,7 @@ def create_order(
|
||||
"gift_name": order.gift_name,
|
||||
"bind_game_role": order.bind_game_role or "",
|
||||
"home_images": len(order.home_images or []),
|
||||
"draft": bool(draft),
|
||||
# 这个虎牙号还挂在哪些单上(允许同一份 CK 服务多单,留个痕)
|
||||
"uid_reused_in": uid_reused,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user