baseline: 批次A-D 成果 + membership 半成品(测试红)

This commit is contained in:
agent
2026-09-11 23:11:35 +08:00
commit b3f3095d53
311 changed files with 40540 additions and 0 deletions
+81
View File
@@ -0,0 +1,81 @@
"""订阅到期处理(Dramatiq actor + cron 每日调用)。"""
from __future__ import annotations
from datetime import date, timedelta
import dramatiq
from django.utils import timezone
from .models import Plan, Subscription
def expire_trials() -> dict:
"""扫描到期试用 → 降级 free 并把状态置为 expired,同时发通知。"""
from django.db.models import Q
from apps.notify.services import send_notification
now = timezone.now()
qs = Subscription.objects.filter(
status=Subscription.STATUS_TRIAL,
trial_ends_at__isnull=False,
trial_ends_at__lt=now,
).select_related("tenant", "plan")
free = Plan.objects.filter(code="free", is_active=True).first()
downgraded = 0
for sub in qs:
sub.status = Subscription.STATUS_EXPIRED
if free is not None:
sub.plan = free
sub.save(update_fields=["status", "plan", "updated_at"])
try:
send_notification(
tenant=sub.tenant,
title="【套餐】免费试用已到期",
content=(
"30 天全功能试用已结束,当前已回落至免费版配额"
"(1 用户 / 100 商品 / 50 单据每月)。"
"升级基础版 ¥998/月 可解除限制,历史数据全部保留。"
),
category="warning",
link="/#/pricing",
extra_data={"kind": "trial_expired"},
)
except Exception:
pass
downgraded += 1
# 付费到期(未续费)→ 同样降级
today = date.today()
paid_expired = Subscription.objects.filter(
status=Subscription.STATUS_ACTIVE,
period_end__isnull=False,
period_end__lt=today,
).select_related("tenant", "plan")
for sub in paid_expired:
sub.status = Subscription.STATUS_EXPIRED
if free is not None:
sub.plan = free
sub.save(update_fields=["status", "plan", "updated_at"])
try:
send_notification(
tenant=sub.tenant,
title="【套餐】订阅已到期",
content=f"套餐已于 {sub.period_end} 到期,当前回落至免费版配额,请及时续费。",
category="warning",
link="/#/pricing",
extra_data={"kind": "subscription_expired"},
)
except Exception:
pass
downgraded += 1
return {"expired_count": downgraded, "checked_at": now.isoformat()}
@dramatiq.actor(max_retries=2, time_limit=300_000)
def expire_trials_and_notify():
"""Dramatiq actor 包装(供 config/dramatiq.py 的 cron 调用/手动 send)。"""
return expire_trials()