Compare commits

...

5 Commits

Author SHA1 Message Date
lowmaster-chen 70c238297a merge: 合并 main 并解决数据大盘冲突
改了什么:合并最新 main,解决 app/admin/repositories/cps.py 和 app/admin/repositories/stats.py 的冲突。
保留内容:京东 CPS 接入、美团订单 payTime 转换修复、金币经济四项加总口径。
验证方式:py_compile 通过,_ts_to_dt 时间戳转换检查通过,git diff --check 通过。
2026-07-01 15:02:16 +08:00
lowmaster-chen 21fc930c0a fix: 修复美团订单时间入库并校准金币大盘口径
改了什么:补全美团订单时间戳转换,避免 pay_time 为空导致昨日 CPS 收益漏算;同时让金币经济的本期/累计发放总数按领券、比价、激励视频、常规任务四项加总。
为什么改:产品核对发现昨日美团有收益但大盘未显示,且发放金币总数与分类卡片不一致。
验证方式:后端聚合自检确认 2026-06-30 美团佣金 86 分、有效订单 2 单,金币总数等于四项分类之和;py_compile 通过。
2026-07-01 14:56:22 +08:00
lowmaster-chen a7d8562ddd fix: 调整数据大盘 DAU 为开始比价和开始领券口径 2026-06-30 19:49:49 +08:00
lowmaster-chen ad6a579ff8 fix: 数据大盘京东 CPS 仅统计实际佣金 2026-06-30 15:50:32 +08:00
lowmaster-chen 08a16c7632 feat: 接入京东联盟订单到数据大盘
改了什么:新增京东联盟订单查询客户端、cps_order 京东字段迁移、CPS 对账接口 platform=all/jd、数据大盘京东订单与佣金聚合。
为什么改:数据大盘需要展示京东 CPS 真实订单佣金,不再保持占位。
验证方式:python -m pytest tests\\test_admin_read.py::test_dashboard_overview tests\\test_cps_admin.py::test_jd_reconcile_updates_dashboard -q;本地真实拉取 2026-06-27 京东订单 3 行,页面显示京东有效 1 单/佣金 ¥0.13。
2026-06-28 17:25:25 +08:00
2 changed files with 91 additions and 13 deletions
+21 -5
View File
@@ -51,7 +51,27 @@ def _yuan_to_cents(v: object) -> int | None:
def _ts_to_dt(ts: object) -> datetime | None:
"""秒级时间戳 → tz-aware UTC datetime(绝对时刻,前端按北京展示)。"""
if not ts:
if ts is None:
return None
if isinstance(ts, datetime):
return ts if ts.tzinfo else ts.replace(tzinfo=_BJ_TZ).astimezone(timezone.utc)
s = str(ts).strip()
if not s or s.lower() == "null":
return None
try:
seconds = float(Decimal(s))
except (InvalidOperation, ValueError):
return None
if seconds == 0:
return None
# 美团文档是秒级时间戳,这里顺手兼容毫秒/微秒,避免上游格式变化导致时间再次落空。
if abs(seconds) > 10_000_000_000_000:
seconds /= 1_000_000
elif abs(seconds) > 10_000_000_000:
seconds /= 1_000
try:
return datetime.fromtimestamp(seconds, tz=timezone.utc)
except (OverflowError, OSError, ValueError):
return None
@@ -83,10 +103,6 @@ def _pick(row: dict[str, Any], *keys: str) -> Any:
if key in row and row[key] is not None:
return row[key]
return None
try:
return datetime.fromtimestamp(int(ts), tz=timezone.utc)
except (ValueError, OSError, TypeError):
return None
# ───────────── 群 ─────────────
+70 -8
View File
@@ -24,11 +24,13 @@ from app.models.user import User
from app.models.wallet import CoinTransaction, WithdrawOrder
_BEIJING = timezone(timedelta(hours=8))
COUPON_REWARD_BIZ_TYPES = ("reward_video", "ad_reward", "coupon", "coupon_reward")
REWARD_VIDEO_BIZ_TYPES = ("reward_video", "ad_reward")
COUPON_REWARD_BIZ_TYPES = ("coupon", "coupon_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",)
REGULAR_TASK_EXCLUDED_BIZ_TYPES = (
*REWARD_VIDEO_BIZ_TYPES,
*COUPON_REWARD_BIZ_TYPES,
*COMPARISON_REWARD_BIZ_TYPES,
*EXCLUDED_REWARD_BIZ_TYPES,
@@ -339,13 +341,28 @@ def dashboard_overview(
period_reward_video_coin_total = _sum(
CoinTransaction.amount,
*period_coin_conds,
CoinTransaction.biz_type.in_(("reward_video", "ad_reward")),
CoinTransaction.biz_type.in_(REWARD_VIDEO_BIZ_TYPES),
)
period_feed_ad_coin_total = _sum(
CoinTransaction.amount,
*period_coin_conds,
CoinTransaction.biz_type == "feed_ad_reward",
)
period_feed_ad_conds = (
AdFeedRewardRecord.created_at >= start_utc,
AdFeedRewardRecord.created_at < end_utc,
AdFeedRewardRecord.status == "granted",
)
period_coupon_feed_coin_total = _sum(
AdFeedRewardRecord.coin,
*period_feed_ad_conds,
AdFeedRewardRecord.feed_scene == "coupon",
)
period_comparison_feed_coin_total = _sum(
AdFeedRewardRecord.coin,
*period_feed_ad_conds,
AdFeedRewardRecord.feed_scene == "comparison",
)
period_signin_coin_total = _sum(
CoinTransaction.amount,
*period_coin_conds,
@@ -365,17 +382,23 @@ def dashboard_overview(
CoinTransaction.amount,
*period_coin_conds,
CoinTransaction.biz_type.in_(COUPON_REWARD_BIZ_TYPES),
)
) + period_coupon_feed_coin_total
period_comparison_reward_coin_total = _sum(
CoinTransaction.amount,
*period_coin_conds,
CoinTransaction.biz_type.in_(COMPARISON_REWARD_BIZ_TYPES),
)
) + period_comparison_feed_coin_total
period_regular_task_coin_total = _sum(
CoinTransaction.amount,
*period_coin_conds,
CoinTransaction.biz_type.notin_(REGULAR_TASK_EXCLUDED_BIZ_TYPES),
)
period_classified_coin_total = (
period_coupon_reward_coin_total
+ period_comparison_reward_coin_total
+ period_reward_video_coin_total
+ period_regular_task_coin_total
)
period_cps_orders = list(
db.execute(
select(CpsOrder).where(
@@ -413,6 +436,41 @@ def dashboard_overview(
else None
)
total_reward_video_coin_total = _sum(
CoinTransaction.amount,
CoinTransaction.amount > 0,
CoinTransaction.biz_type.in_(REWARD_VIDEO_BIZ_TYPES),
)
total_coupon_reward_coin_total = _sum(
CoinTransaction.amount,
CoinTransaction.amount > 0,
CoinTransaction.biz_type.in_(COUPON_REWARD_BIZ_TYPES),
) + _sum(
AdFeedRewardRecord.coin,
AdFeedRewardRecord.status == "granted",
AdFeedRewardRecord.feed_scene == "coupon",
)
total_comparison_reward_coin_total = _sum(
CoinTransaction.amount,
CoinTransaction.amount > 0,
CoinTransaction.biz_type.in_(COMPARISON_REWARD_BIZ_TYPES),
) + _sum(
AdFeedRewardRecord.coin,
AdFeedRewardRecord.status == "granted",
AdFeedRewardRecord.feed_scene == "comparison",
)
total_regular_task_coin_total = _sum(
CoinTransaction.amount,
CoinTransaction.amount > 0,
CoinTransaction.biz_type.notin_(REGULAR_TASK_EXCLUDED_BIZ_TYPES),
)
total_classified_coin_total = (
total_coupon_reward_coin_total
+ total_comparison_reward_coin_total
+ total_reward_video_coin_total
+ total_regular_task_coin_total
)
return {
"users": {
"total": _count(User),
@@ -423,12 +481,12 @@ def dashboard_overview(
"dau": today_dau(db),
},
"coins": {
# 累计发放金币(coin_transaction 里所有 amount>0 之和;负数是兑换/扣减不计)
"granted_total": _sum(CoinTransaction.amount, CoinTransaction.amount > 0),
# 累计发放金币按大盘 4 个展示分类加总;未分类/运营手动/邀请奖励不进入本指标。
"granted_total": total_classified_coin_total,
"reward_video_coin_total": _sum(
CoinTransaction.amount,
CoinTransaction.amount > 0,
CoinTransaction.biz_type.in_(("reward_video", "ad_reward")),
CoinTransaction.biz_type.in_(REWARD_VIDEO_BIZ_TYPES),
),
"reward_video_watch_count": _count(
AdRewardRecord,
@@ -456,6 +514,10 @@ def dashboard_overview(
CoinTransaction.biz_type == "signin_boost",
),
"signin_boost_watch_count": _count(SigninBoostRecord),
"task_coin_total": total_regular_task_coin_total,
"coupon_reward_coin_total": total_coupon_reward_coin_total,
"comparison_reward_coin_total": total_comparison_reward_coin_total,
"regular_task_coin_total": total_regular_task_coin_total,
},
"cash": {
"withdraw_success_cents": _sum(
@@ -492,7 +554,7 @@ def dashboard_overview(
"average_saved_cents": period_avg_saved_cents,
},
"coins": {
"granted_total": _sum(CoinTransaction.amount, *period_coin_conds),
"granted_total": period_classified_coin_total,
"reward_video_coin_total": period_reward_video_coin_total,
"feed_ad_coin_total": period_feed_ad_coin_total,
"signin_coin_total": period_signin_coin_total,