feat: grant signup bonus points

This commit is contained in:
QiuSW
2026-07-08 15:13:23 +08:00
parent 02aa12e8b9
commit ac8bec7b9c
29 changed files with 399 additions and 61 deletions
+71 -1
View File
@@ -11,7 +11,13 @@ from django.utils import timezone
from apps.users.models import UserWallet
from .models import CallRecord, PointsLedger, RechargeOrder, normalize_resolution
from .models import (
CallRecord,
PointsLedger,
RechargeOrder,
SignupBonusGrant,
normalize_resolution,
)
from .pricing import quote_recharge_points
@@ -147,6 +153,16 @@ class WalletAdjustment:
balance_after: int
@dataclass(frozen=True)
class SignupBonusGrantResult:
wallet: UserWallet
grant: SignupBonusGrant
ledger_entry: PointsLedger | None
points_granted: int
balance_after: int
granted: bool
def _validate_positive_points(points: int) -> int:
if isinstance(points, bool) or not isinstance(points, int) or points <= 0:
raise ValueError("points must be a positive integer")
@@ -333,6 +349,60 @@ def adjust_wallet_points(
)
def grant_signup_bonus(
*,
user,
points: int = 100,
reason: str = "new_user_registration",
) -> SignupBonusGrantResult:
points = _validate_positive_points(points)
ledger_reason = str(reason or "new_user_registration").strip()
with transaction.atomic():
wallet = _locked_wallet_for_user(user)
grant, created = SignupBonusGrant.objects.get_or_create(
user=user,
defaults={"points_granted": points},
)
if not created:
existing_ledger = (
PointsLedger.objects.filter(
user=user,
change_type=PointsLedger.ChangeType.SIGNUP_BONUS,
)
.order_by("id")
.first()
)
return SignupBonusGrantResult(
wallet=wallet,
grant=grant,
ledger_entry=existing_ledger,
points_granted=0,
balance_after=wallet.points_balance,
granted=False,
)
wallet.points_balance += points
wallet.save(update_fields=("points_balance", "updated_at"))
ledger_entry = PointsLedger.objects.create(
user=user,
change_type=PointsLedger.ChangeType.SIGNUP_BONUS,
points_delta=points,
balance_after=wallet.points_balance,
reason=ledger_reason,
)
return SignupBonusGrantResult(
wallet=wallet,
grant=grant,
ledger_entry=ledger_entry,
points_granted=points,
balance_after=ledger_entry.balance_after,
granted=True,
)
def apply_recharge_payment(payment: RechargePayment) -> RechargeResult:
order_no = str(payment.order_no or "").strip()
pay_method = _normalize_pay_method(payment.pay_method)