feat(huya): 增加协作单礼包字典与名称编辑

This commit is contained in:
yml2213
2026-11-07 16:54:01 +08:00
parent afcbff2207
commit efa8bf2a46
12 changed files with 1183 additions and 44 deletions
+271 -2
View File
@@ -36,6 +36,7 @@ from ..huya_defaults import (
HUYA_DEFAULT_SID,
)
from ..models import (
HuyaCollabGiftCatalog,
HuyaCollabShop,
HuyaConfig,
HuyaExternalOrder,
@@ -2579,6 +2580,7 @@ def export_cks(
# --------------------------------------------------------------------------
SHOP_NAME_LIMIT = 64
GIFT_NAME_LIMIT = 128
def normalize_shop_name(value: Any) -> str:
@@ -2831,6 +2833,257 @@ def list_shops(db: Session) -> list[str]:
return sorted(shop.name for shop in list_shop_records(db))
def _gift_audit_target(gift: HuyaCollabGiftCatalog) -> str:
return f"huya_collab_gift:{gift.id}"
def _touch_gift(gift: HuyaCollabGiftCatalog, current: User | None) -> None:
gift.updated_by = current.id if current else None
gift.updated_at = _now()
def _clean_catalog_points(value: Any) -> int:
points = _clean_required_points(value)
if points is None:
raise CollabError("礼包所需积分不能为空", code="gift_points_invalid")
return points
def _catalog_points_for_name(db: Session, name: str) -> int | None:
clean = str(name or "").strip()
if not clean:
return None
gift = (
db.query(HuyaCollabGiftCatalog)
.filter(HuyaCollabGiftCatalog.name == clean)
.first()
)
return int(gift.points or 0) if gift is not None else None
def serialize_gift(
gift: HuyaCollabGiftCatalog, *, users: Mapping[int, str] | None = None
) -> dict:
names = users or {}
def name_of(value: int | None) -> str:
return names.get(int(value), "") if value else ""
return {
"id": gift.id,
"name": gift.name or "",
"points": int(gift.points or 0),
"sort_order": int(gift.sort_order or 0),
"is_active": bool(gift.is_active),
"remark": gift.remark or "",
"created_by_name": name_of(gift.created_by),
"updated_by_name": name_of(gift.updated_by),
"created_at": gift.created_at,
"updated_at": gift.updated_at,
}
def list_gift_records(db: Session) -> list[HuyaCollabGiftCatalog]:
"""全部礼包字典项(含停用;前端按 ``is_active`` 决定新建时是否可选)。"""
return (
db.query(HuyaCollabGiftCatalog)
.order_by(
HuyaCollabGiftCatalog.sort_order.asc(),
HuyaCollabGiftCatalog.name.asc(),
)
.all()
)
def get_gift(db: Session, gift_id: int) -> HuyaCollabGiftCatalog:
gift = (
db.query(HuyaCollabGiftCatalog)
.filter(HuyaCollabGiftCatalog.id == gift_id)
.first()
)
if gift is None:
raise CollabError(
"这个礼包不存在(可能已被别人删除)",
status_code=404,
code="gift_not_found",
)
return gift
def _assert_gift_name_available(
db: Session, name: str, *, exclude_id: int | None = None
) -> None:
query = db.query(HuyaCollabGiftCatalog).filter(HuyaCollabGiftCatalog.name == name)
if exclude_id is not None:
query = query.filter(HuyaCollabGiftCatalog.id != exclude_id)
other = query.first()
if other is not None:
raise CollabError(
f"礼包「{name}」已经在字典里了(第 {other.id} 行)",
status_code=409,
code="gift_name_conflict",
)
def create_gift(
db: Session,
current: User | None,
*,
name: Any,
points: Any,
sort_order: int = 0,
remark: Any = "",
commit: bool = True,
) -> HuyaCollabGiftCatalog:
clean_name = clean_text(name, field="gift_name", max_length=GIFT_NAME_LIMIT)
if not clean_name:
raise CollabError("礼包名不能为空", code="gift_name_required")
_assert_gift_name_available(db, clean_name)
gift = HuyaCollabGiftCatalog(
name=clean_name,
points=_clean_catalog_points(points),
sort_order=int(sort_order or 0),
is_active=True,
remark=clean_text(remark, field="gift_remark", max_length=255),
created_by=current.id if current else None,
updated_by=current.id if current else None,
)
db.add(gift)
db.flush()
record_audit(
db,
current,
action="huya:collab:gift_create",
target=_gift_audit_target(gift),
detail={
"name": gift.name,
"points": gift.points,
"sort_order": gift.sort_order,
},
)
if commit:
db.commit()
db.refresh(gift)
return gift
def update_gift(
db: Session,
current: User | None,
gift: HuyaCollabGiftCatalog,
payload: Mapping[str, Any],
*,
commit: bool = True,
) -> dict:
"""修改礼包字典,返回字段级 diff。
改名会级联所有历史订单上的 ``gift_name``;积分只改字典,不回填订单里
已经形成的 ``required_points`` 快照。
"""
changes: dict[str, dict[str, Any]] = {}
old_name = gift.name or ""
renamed_orders = 0
if "name" in payload:
new_name = clean_text(
payload["name"], field="gift_name", max_length=GIFT_NAME_LIMIT
)
if not new_name:
raise CollabError("礼包名不能为空", code="gift_name_required")
if new_name != old_name:
_assert_gift_name_available(db, new_name, exclude_id=gift.id)
changes["name"] = {"old": old_name, "new": new_name}
gift.name = new_name
if "points" in payload:
value = _clean_catalog_points(payload["points"])
if value != int(gift.points or 0):
changes["points"] = {"old": int(gift.points or 0), "new": value}
gift.points = value
if "sort_order" in payload:
value = int(payload["sort_order"] or 0)
if value != int(gift.sort_order or 0):
changes["sort_order"] = {"old": int(gift.sort_order or 0), "new": value}
gift.sort_order = value
if "is_active" in payload:
value = bool(payload["is_active"])
if value != bool(gift.is_active):
changes["is_active"] = {"old": bool(gift.is_active), "new": value}
gift.is_active = value
if "remark" in payload:
value = clean_text(payload["remark"], field="gift_remark", max_length=255)
if value != (gift.remark or ""):
changes["remark"] = {"old": gift.remark or "", "new": value}
gift.remark = value
if not changes:
return {}
if "name" in changes:
renamed_orders = (
db.query(HuyaExternalOrder)
.filter(HuyaExternalOrder.gift_name == old_name)
.update(
{HuyaExternalOrder.gift_name: changes["name"]["new"]},
synchronize_session=False,
)
)
_touch_gift(gift, current)
record_audit(
db,
current,
action="huya:collab:gift_update",
target=_gift_audit_target(gift),
detail={"changes": changes, "renamed_orders": renamed_orders},
)
if commit:
db.commit()
db.refresh(gift)
return changes
def delete_gift(
db: Session,
current: User | None,
gift: HuyaCollabGiftCatalog,
*,
commit: bool = True,
) -> dict:
"""删除未被订单引用的字典项;仍在使用时明确拒绝,避免历史名称失去标准口径。"""
gift_name = gift.name or ""
used = (
db.query(func.count(HuyaExternalOrder.id))
.filter(HuyaExternalOrder.gift_name == gift_name)
.scalar()
or 0
)
if used:
raise CollabError(
f"还有 {used} 个协作单使用「{gift_name}」,请先停用或改名",
code="gift_in_use",
)
snapshot = {
"id": gift.id,
"name": gift_name,
"points": int(gift.points or 0),
}
record_audit(
db,
current,
action="huya:collab:gift_delete",
target=_gift_audit_target(gift),
detail=snapshot,
)
db.delete(gift)
if commit:
db.commit()
return snapshot
def list_gift_names(db: Session) -> list[str]:
"""台账里出现过的礼包/皮肤名(去重、去空;筛选下拉用)。"""
rows = db.query(HuyaExternalOrder.gift_name).distinct().all()
@@ -2935,13 +3188,20 @@ def create_order(
# 草稿单不验 CK、不跑探针:先落一行占住订单号,资料到了在详情里补。
require_verified = False
clean_gift_name = clean_text(
gift_name, field="gift_name", max_length=GIFT_NAME_LIMIT
)
clean_required_points = _clean_required_points(required_points)
if clean_gift_name and clean_required_points is None:
clean_required_points = _catalog_points_for_name(db, clean_gift_name)
order = HuyaExternalOrder(
order_no=order_no,
shop=clean_text(shop, field="shop"),
owner_id=_clean_owner_id(owner_id) if owner_id is not None else current.id,
bind_douyu_account=clean_text(bind_douyu_account, field="bind_douyu_account"),
gift_name=clean_text(gift_name, field="gift_name"),
required_points=_clean_required_points(required_points),
gift_name=clean_gift_name,
required_points=clean_required_points,
remark=clean_text(remark, field="remark"),
bind_images=[],
home_images=home_images_keys,
@@ -3077,6 +3337,15 @@ def update_order(
# 改成字典里没有的名字也允许(自定义),顺手登记,筛选下拉不会漏。
ensure_shops_registered(db, current, [changes["shop"]["new"]])
if "gift_name" in changes and "required_points" not in payload:
catalog_points = _catalog_points_for_name(db, changes["gift_name"]["new"])
if catalog_points is not None and catalog_points != order.required_points:
changes["required_points"] = {
"old": order.required_points,
"new": catalog_points,
}
order.required_points = catalog_points
# 先记「人工改了什么」,再跑自动状态迁移:否则时间线会出现
# status_auto 排在 update 前面,像状态是被后一次操作改的。
record_audit(