from decimal import Decimal, InvalidOperation from django.db import transaction from django.db.models import Sum from rest_framework import status from rest_framework.decorators import api_view, permission_classes from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from apps.common.api import get_current_team from .models import CreditAccount, CreditLedger from .serializers import CreditAccountSerializer, CreditLedgerSerializer @api_view(["GET"]) @permission_classes([IsAuthenticated]) def summary(request): team = get_current_team(request.user) account, _ = CreditAccount.objects.get_or_create(team=team) charged = CreditLedger.objects.filter(team=team, ledger_type=CreditLedger.Type.CHARGE).aggregate( total=Sum("amount") )["total"] or 0 return Response( { "account": CreditAccountSerializer(account).data, "charged_total": charged, } ) @api_view(["GET"]) @permission_classes([IsAuthenticated]) def ledgers(request): team = get_current_team(request.user) queryset = CreditLedger.objects.filter(team=team).select_related("user", "project", "task").order_by("-created_at") project_id = request.query_params.get("project") user_id = request.query_params.get("user") if project_id: queryset = queryset.filter(project_id=project_id) if user_id: queryset = queryset.filter(user_id=user_id) return Response(CreditLedgerSerializer(queryset[:100], many=True).data) @api_view(["POST"]) @permission_classes([IsAuthenticated]) def recharge(request): team = get_current_team(request.user) try: amount = Decimal(str(request.data.get("amount", "0"))) bonus = Decimal(str(request.data.get("bonus", "0"))) except (InvalidOperation, TypeError): return Response({"detail": "invalid amount"}, status=status.HTTP_400_BAD_REQUEST) if amount <= 0: return Response({"detail": "amount must be positive"}, status=status.HTTP_400_BAD_REQUEST) if bonus < 0: return Response({"detail": "bonus cannot be negative"}, status=status.HTTP_400_BAD_REQUEST) channel = str(request.data.get("channel") or "manual")[:32] credited = amount + bonus with transaction.atomic(): account, _ = CreditAccount.objects.select_for_update().get_or_create(team=team) account.balance += credited account.save(update_fields=["balance", "updated_at"]) ledger = CreditLedger.objects.create( team=team, user=request.user, ledger_type=CreditLedger.Type.RECHARGE, amount=credited, balance_after=account.balance, reason="团队充值", metadata={"channel": channel, "paid_amount": str(amount), "bonus": str(bonus)}, ) return Response( { "account": CreditAccountSerializer(account).data, "ledger": CreditLedgerSerializer(ledger).data, }, status=status.HTTP_201_CREATED, )