fix(huya): 作废订单后失效旧积分快照
This commit is contained in:
@@ -2554,6 +2554,76 @@ class TestCollabRecharge(TestCollabBase):
|
||||
assert "还差 100 分" in first.points_check_message
|
||||
assert first.points_check_reason == second.points_check_reason == "shortage"
|
||||
|
||||
def test_void_member_invalidates_stale_group_points_snapshot(self):
|
||||
first = self._ready("RC-VOID-1", "1101", gift_name="礼包A", required_points=988)
|
||||
second = self._ready(
|
||||
"RC-VOID-2", "1101", gift_name="礼包B", required_points=988
|
||||
)
|
||||
self._check(
|
||||
[first, second],
|
||||
FakeRechargeClient(
|
||||
FakeBindResp(nick=self.BIND, role=self.ROLE),
|
||||
FakeBindResp(nick=self.BIND, role=self.ROLE),
|
||||
score=self._score(988),
|
||||
),
|
||||
)
|
||||
assert first.status == second.status == collab.STATUS_POINTS_ERROR
|
||||
assert first.points_required_total == second.points_required_total == 1976
|
||||
|
||||
collab.set_status(
|
||||
self.db,
|
||||
self.user,
|
||||
second,
|
||||
collab.STATUS_VOID,
|
||||
reason="客户退款",
|
||||
)
|
||||
|
||||
assert second.status == collab.STATUS_VOID
|
||||
assert first.status == collab.STATUS_TO_RECHARGE
|
||||
for order in (first, second):
|
||||
assert order.points_checked_at is None
|
||||
assert order.points_balance is None
|
||||
assert order.points_required_total is None
|
||||
assert order.points_surplus is None
|
||||
assert order.points_threshold is None
|
||||
assert order.points_check_detail is None
|
||||
assert order.points_check_reason == "stale_group"
|
||||
assert "原积分统计已失效" in order.points_check_message
|
||||
assert "huya:collab:recharge_invalidate" in [
|
||||
row.action for row in self._audits(first)
|
||||
]
|
||||
|
||||
result = self._check(
|
||||
[first],
|
||||
FakeRechargeClient(
|
||||
FakeBindResp(nick=self.BIND, role=self.ROLE),
|
||||
score=self._score(988),
|
||||
),
|
||||
)
|
||||
|
||||
assert result["groups"][0]["required_total"] == 988
|
||||
assert result["groups"][0]["surplus"] == 0
|
||||
assert first.status == collab.STATUS_RECHARGED
|
||||
|
||||
def test_scripted_transition_keeps_completed_points_snapshot(self):
|
||||
order = self._ready(
|
||||
"RC-SCRIPT-1", "1102", gift_name="礼包A", required_points=988
|
||||
)
|
||||
self._check(
|
||||
[order],
|
||||
FakeRechargeClient(
|
||||
FakeBindResp(nick=self.BIND, role=self.ROLE),
|
||||
score=self._score(988),
|
||||
),
|
||||
)
|
||||
checked_at = order.points_checked_at
|
||||
|
||||
collab.set_status(self.db, self.user, order, collab.STATUS_SCRIPTED)
|
||||
|
||||
assert order.status == collab.STATUS_SCRIPTED
|
||||
assert order.points_checked_at == checked_at
|
||||
assert order.points_required_total == 988
|
||||
|
||||
def test_excess_marks_whole_group_points_error(self):
|
||||
order = self._ready("RC-6", "1004", gift_name="礼包A", required_points=500)
|
||||
client = FakeRechargeClient(
|
||||
|
||||
+143
-2
@@ -9,7 +9,9 @@
|
||||
并确认迁移新增的复合查询索引确实落库。
|
||||
"""
|
||||
|
||||
import json
|
||||
import secrets
|
||||
from datetime import UTC, datetime
|
||||
from pathlib import Path
|
||||
from urllib.parse import quote_plus
|
||||
|
||||
@@ -22,13 +24,13 @@ from sqlalchemy.orm import Session
|
||||
import web.backend.database as database_module
|
||||
from tests.support.db import get_mysql_credentials, get_test_engine
|
||||
from web.backend.database import Base
|
||||
from web.backend.models import User, WeiboAccount
|
||||
from web.backend.models import AuditLog, HuyaExternalOrder, User, WeiboAccount
|
||||
|
||||
TESTS_DIR = Path(__file__).resolve().parent
|
||||
PROJECT_ROOT = TESTS_DIR.parent
|
||||
VERSIONS_DIR = PROJECT_ROOT / "web" / "backend" / "migrations" / "versions"
|
||||
|
||||
HEAD_REVISION = "20261112_0077"
|
||||
HEAD_REVISION = "20261112_0078"
|
||||
|
||||
# 迁移新增、但不声明在模型里的复合查询索引。
|
||||
EXTRA_INDEXES = (
|
||||
@@ -267,3 +269,142 @@ class TestMigrationSmoke:
|
||||
mig_engine.dispose()
|
||||
with admin_engine.begin() as conn:
|
||||
conn.execute(text(f"DROP DATABASE IF EXISTS `{mig_db}`;"))
|
||||
|
||||
def test_0078_cleans_only_pre_void_points_snapshots(self):
|
||||
creds = get_mysql_credentials()
|
||||
mig_db = f"douyu_login_mig_{secrets.token_hex(4)}"
|
||||
mig_url = (
|
||||
f"mysql+pymysql://{quote_plus(creds['user'])}:"
|
||||
f"{quote_plus(creds['password'])}@{creds['host']}:{creds['port']}/"
|
||||
f"{mig_db}?charset=utf8mb4"
|
||||
)
|
||||
|
||||
admin_engine = get_test_engine()
|
||||
with admin_engine.begin() as conn:
|
||||
conn.execute(
|
||||
text(
|
||||
f"CREATE DATABASE `{mig_db}` "
|
||||
"CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;"
|
||||
)
|
||||
)
|
||||
|
||||
original_url = database_module.DATABASE_URL
|
||||
mig_engine = create_engine(mig_url)
|
||||
try:
|
||||
database_module.DATABASE_URL = mig_url
|
||||
cfg = _migration_config()
|
||||
cfg.set_main_option("sqlalchemy.url", mig_url)
|
||||
command.upgrade(cfg, "20261112_0077")
|
||||
|
||||
with Session(mig_engine) as session:
|
||||
user = User(
|
||||
username="migration-collab",
|
||||
password_hash="hash",
|
||||
role="super_admin",
|
||||
is_active=True,
|
||||
)
|
||||
session.add(user)
|
||||
session.commit()
|
||||
|
||||
def make_order(
|
||||
order_no: str,
|
||||
*,
|
||||
status: str,
|
||||
checked_at: datetime,
|
||||
required_total: int,
|
||||
surplus: int,
|
||||
) -> HuyaExternalOrder:
|
||||
return HuyaExternalOrder(
|
||||
order_no=order_no,
|
||||
shop="向日葵",
|
||||
uid="1199665363355",
|
||||
cookie="C" * 32,
|
||||
raw_cookie="raw",
|
||||
ck_status="valid",
|
||||
bind_game_account="泡泡",
|
||||
bind_game_role="厶婕",
|
||||
gift_name="套装-芝士鼠鼠",
|
||||
required_points=988,
|
||||
home_images=["collab/2026/11/12/home.png"],
|
||||
status=status,
|
||||
points_checked_at=checked_at,
|
||||
points_balance=988,
|
||||
points_required_total=required_total,
|
||||
points_surplus=surplus,
|
||||
points_threshold=100,
|
||||
points_check_reason="shortage" if surplus < 0 else "ok",
|
||||
points_check_message="旧核账",
|
||||
points_check_detail={"orders": []},
|
||||
created_by=user.id,
|
||||
updated_by=user.id,
|
||||
)
|
||||
|
||||
stale = make_order(
|
||||
"MIG-0078-STALE",
|
||||
status="points_error",
|
||||
checked_at=datetime(2026, 11, 12, 1, 46, tzinfo=UTC),
|
||||
required_total=1976,
|
||||
surplus=-988,
|
||||
)
|
||||
voided = make_order(
|
||||
"MIG-0078-VOID",
|
||||
status="void",
|
||||
checked_at=datetime(2026, 11, 12, 1, 46, tzinfo=UTC),
|
||||
required_total=1976,
|
||||
surplus=-988,
|
||||
)
|
||||
current = make_order(
|
||||
"MIG-0078-CURRENT",
|
||||
status="recharged",
|
||||
checked_at=datetime(2026, 11, 12, 7, 51, 50, tzinfo=UTC),
|
||||
required_total=988,
|
||||
surplus=0,
|
||||
)
|
||||
session.add_all([stale, voided, current])
|
||||
session.commit()
|
||||
session.add(
|
||||
AuditLog(
|
||||
user_id=user.id,
|
||||
username=user.username,
|
||||
action="huya:collab:status",
|
||||
target=f"huya_external_order:{voided.id}",
|
||||
detail=json.dumps({"to": "void"}),
|
||||
success=True,
|
||||
created_at=datetime(2026, 11, 12, 1, 49, 53, tzinfo=UTC),
|
||||
)
|
||||
)
|
||||
session.commit()
|
||||
|
||||
command.upgrade(cfg, "20261112_0078")
|
||||
|
||||
with mig_engine.connect() as conn:
|
||||
rows = {
|
||||
row.order_no: row
|
||||
for row in conn.execute(
|
||||
text(
|
||||
"SELECT order_no, status, points_checked_at, "
|
||||
"points_required_total, points_surplus, "
|
||||
"points_check_reason, points_check_detail "
|
||||
"FROM huya_external_orders"
|
||||
)
|
||||
)
|
||||
}
|
||||
assert rows["MIG-0078-VOID"].status == "void"
|
||||
assert rows["MIG-0078-VOID"].points_checked_at is None
|
||||
assert rows["MIG-0078-VOID"].points_required_total is None
|
||||
assert rows["MIG-0078-VOID"].points_check_reason == "stale_group"
|
||||
|
||||
assert rows["MIG-0078-STALE"].status == "to_recharge"
|
||||
assert rows["MIG-0078-STALE"].points_checked_at is None
|
||||
assert rows["MIG-0078-STALE"].points_required_total is None
|
||||
assert rows["MIG-0078-STALE"].points_check_reason == "stale_group"
|
||||
|
||||
assert rows["MIG-0078-CURRENT"].status == "recharged"
|
||||
assert rows["MIG-0078-CURRENT"].points_required_total == 988
|
||||
assert rows["MIG-0078-CURRENT"].points_surplus == 0
|
||||
assert rows["MIG-0078-CURRENT"].points_check_reason == "ok"
|
||||
finally:
|
||||
database_module.DATABASE_URL = original_url
|
||||
mig_engine.dispose()
|
||||
with admin_engine.begin() as conn:
|
||||
conn.execute(text(f"DROP DATABASE IF EXISTS `{mig_db}`;"))
|
||||
|
||||
Reference in New Issue
Block a user