Compare commits

...

1 Commits

Author SHA1 Message Date
unknown ab05e00734 fix(admin): 按任务白名单聚合常规金币 2026-07-22 11:40:41 +08:00
2 changed files with 75 additions and 14 deletions
+16 -14
View File
@@ -6,10 +6,10 @@ user.last_login_at / comparison_record.status / withdraw_order.status)要加索
from __future__ import annotations from __future__ import annotations
from collections import Counter from collections import Counter
from datetime import date, datetime, time, timedelta, timezone from datetime import UTC, date, datetime, time, timedelta, timezone
from decimal import ROUND_HALF_UP, Decimal, InvalidOperation from decimal import ROUND_HALF_UP, Decimal, InvalidOperation
from sqlalchemy import case, func, select from sqlalchemy import case, func, or_, select
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from app.admin.repositories.coupon_data import _percentile from app.admin.repositories.coupon_data import _percentile
@@ -37,14 +37,13 @@ REWARD_VIDEO_BIZ_TYPES = ("reward_video", "ad_reward")
# ad_reward 是激励视频,单独成桶、不再混进领券奖励(历史误并会把激励视频金币双计进领券)。 # ad_reward 是激励视频,单独成桶、不再混进领券奖励(历史误并会把激励视频金币双计进领券)。
COUPON_REWARD_BIZ_TYPES = ("coupon", "coupon_reward") COUPON_REWARD_BIZ_TYPES = ("coupon", "coupon_reward")
COMPARISON_REWARD_BIZ_TYPES = ("comparison", "compare_reward", "comparison_reward") COMPARISON_REWARD_BIZ_TYPES = ("comparison", "compare_reward", "comparison_reward")
EXCLUDED_REWARD_BIZ_TYPES = ("invite_inviter", "invite_invitee", "admin_grant") # 常规任务必须按明确来源相加;不能从全部正向流水反减排除项,否则新增广告/运营
UNCLASSIFIED_FEED_BIZ_TYPES = ("feed_ad_reward",) # biz_type 时会在排除清单更新前自动混入该桶。task_ 前缀在查询处单独覆盖现有及未来任务。
REGULAR_TASK_EXCLUDED_BIZ_TYPES = ( REGULAR_TASK_EXACT_BIZ_TYPES = (
*REWARD_VIDEO_BIZ_TYPES, "signin",
*COUPON_REWARD_BIZ_TYPES, "signin_boost",
*COMPARISON_REWARD_BIZ_TYPES, "price_report_reward",
*EXCLUDED_REWARD_BIZ_TYPES, "feedback_reward",
*UNCLASSIFIED_FEED_BIZ_TYPES,
) )
MEITUAN_CPS_INVALID_STATUSES = ("4", "5") MEITUAN_CPS_INVALID_STATUSES = ("4", "5")
MEITUAN_CPS_SETTLED_STATUS = "6" MEITUAN_CPS_SETTLED_STATUS = "6"
@@ -61,7 +60,7 @@ def _beijing_today_start_utc() -> datetime:
"""北京时间今天 0 点对应的 UTC 时刻(DAU / 今日新增按北京时区切天)。""" """北京时间今天 0 点对应的 UTC 时刻(DAU / 今日新增按北京时区切天)。"""
now_bj = datetime.now(_BEIJING) now_bj = datetime.now(_BEIJING)
start_bj = now_bj.replace(hour=0, minute=0, second=0, microsecond=0) start_bj = now_bj.replace(hour=0, minute=0, second=0, microsecond=0)
return start_bj.astimezone(timezone.utc) return start_bj.astimezone(UTC)
def today_dau(db: Session) -> int: def today_dau(db: Session) -> int:
@@ -94,8 +93,8 @@ def _period_bounds(date_from: date, date_to: date) -> tuple[datetime, datetime,
""" """
start_bj = datetime.combine(date_from, time.min, tzinfo=_BEIJING) start_bj = datetime.combine(date_from, time.min, tzinfo=_BEIJING)
end_bj = datetime.combine(date_to + timedelta(days=1), time.min, tzinfo=_BEIJING) end_bj = datetime.combine(date_to + timedelta(days=1), time.min, tzinfo=_BEIJING)
start_utc = start_bj.astimezone(timezone.utc) start_utc = start_bj.astimezone(UTC)
end_utc = end_bj.astimezone(timezone.utc) end_utc = end_bj.astimezone(UTC)
return ( return (
start_utc, start_utc,
end_utc, end_utc,
@@ -505,7 +504,10 @@ def dashboard_overview(
period_regular_task_coin_total = _sum( period_regular_task_coin_total = _sum(
CoinTransaction.amount, CoinTransaction.amount,
*period_coin_conds, *period_coin_conds,
CoinTransaction.biz_type.notin_(REGULAR_TASK_EXCLUDED_BIZ_TYPES), or_(
CoinTransaction.biz_type.in_(REGULAR_TASK_EXACT_BIZ_TYPES),
CoinTransaction.biz_type.like(r"task\_%", escape="\\"),
),
) )
period_cps_orders = list( period_cps_orders = list(
db.execute( db.execute(
+59
View File
@@ -455,3 +455,62 @@ def test_period_coupon_reward_excludes_reward_video(
coins = r.json()["period"]["coins"] coins = r.json()["period"]["coins"]
assert coins["coupon_reward_coin_total"] == 0 # 激励视频不计入领券奖励 assert coins["coupon_reward_coin_total"] == 0 # 激励视频不计入领券奖励
assert coins["reward_video_coin_total"] == 50 # 仍计入激励视频卡 assert coins["reward_video_coin_total"] == 50 # 仍计入激励视频卡
def test_period_regular_task_coin_uses_explicit_allowlist(
admin_client: TestClient, admin_token: str
) -> None:
"""常规任务金币只加明确任务来源,领券/比价及未知新类型不得自动混入。"""
from datetime import datetime
from app.models.wallet import CoinTransaction
d = "2021-06-18"
included = {
"signin": 100,
"signin_boost": 200,
"task_enable_notification": 300,
"task_other": 400,
"price_report_reward": 500,
"feedback_reward": 600,
}
excluded = {
"feed_ad_reward_coupon": 700,
"feed_ad_reward_comparison": 800,
"feed_ad_reward": 900,
"reward_video": 1000,
"admin_grant": 1100,
"invite_inviter": 1200,
"future_unknown_reward": 1300,
}
db = SessionLocal()
try:
uid = user_repo.upsert_user_for_login(
db, phone="13800008804", register_channel="sms"
).id
balance = 0
rows = []
for index, (biz_type, amount) in enumerate((included | excluded).items(), start=1):
balance += amount
rows.append(CoinTransaction(
user_id=uid,
amount=amount,
balance_after=balance,
biz_type=biz_type,
ref_id=f"regular-task-{index}",
created_at=datetime(2021, 6, 18, 12, 0, index),
))
db.add_all(rows)
db.commit()
finally:
db.close()
response = admin_client.get(
"/admin/api/stats/overview",
params={"date_from": d, "date_to": d},
headers=_auth(admin_token),
)
assert response.status_code == 200, response.text
coins = response.json()["period"]["coins"]
assert coins["regular_task_coin_total"] == sum(included.values())
assert coins["task_coin_total"] == 700