From 54f8e234885f638b58604b47e8dd4147e48173b0 Mon Sep 17 00:00:00 2001 From: XingQue Date: Sat, 27 Jun 2026 18:50:32 +0800 Subject: [PATCH] fix: separate penalty czjilu from merchant recharge and correct finance stats --- backend/view.py | 219 +----------------- jituan/services/club_penalty.py | 6 + jituan/services/finance_detail_stats.py | 88 +++++-- jituan/services/szjilu_accounting.py | 6 +- products/czjilu_types.py | 81 +++++++ .../0010_backfill_penalty_czjilu_leixing.py | 41 ++++ products/models.py | 5 +- products/views.py | 28 ++- users/views.py | 15 +- 9 files changed, 249 insertions(+), 240 deletions(-) create mode 100644 products/czjilu_types.py create mode 100644 products/migrations/0010_backfill_penalty_czjilu_leixing.py diff --git a/backend/view.py b/backend/view.py index 3486aaa..9b6fb0c 100644 --- a/backend/view.py +++ b/backend/view.py @@ -7663,228 +7663,29 @@ class CwqtczhqView(APIView): if 'qtczcaiwu' not in permissions: return Response({'code': 403, 'msg': '无其他充值财务查看权限'}, status=403) + from jituan.services.finance_detail_stats import build_chongzhi_finance_payload + granularity = request.data.get('granularity', 'day') year = request.data.get('year') month = request.data.get('month') - types = request.data.get('types', [2, 3, 4, 5, 6]) # 默认全部 + types = request.data.get('types', [2, 3, 4, 5, 6]) summary_date = request.data.get('summary_date') if isinstance(types, list) and len(types) == 0: types = [2, 3, 4, 5, 6] - now = date.today() + data, err = build_chongzhi_finance_payload( + request, granularity, year, month, types, summary_date, + ) + if err: + return Response({'code': 1, 'msg': err}, status=400) - # ---------- 时间范围(用于曲线图) ---------- - if granularity == 'day': - if not year or not month: - return Response({'code': 1, 'msg': '按日统计需要年份和月份'}, status=400) - start_date = date(year, month, 1) - end_date = date(year, month + 1, 1) if month < 12 else date(year + 1, 1, 1) - trunc_func = TruncDate('CreateTime') - # 默认汇总日期 - if not summary_date: - summary_date = now.strftime('%Y-%m-%d') - summary_q = datetime.strptime(summary_date, '%Y-%m-%d').date() - elif granularity == 'month': - if not year: - return Response({'code': 1, 'msg': '按月统计需要年份'}, status=400) - start_date = date(year, 1, 1) - end_date = date(year + 1, 1, 1) - trunc_func = TruncMonth('CreateTime') - if not summary_date: - summary_date = now.strftime('%Y-%m') - summary_q = datetime.strptime(summary_date + '-01', '%Y-%m-%d').date() - elif granularity == 'year': - year = year or now.year - start_date = date(year, 1, 1) - end_date = date(year + 1, 1, 1) - trunc_func = TruncYear('CreateTime') - if not summary_date: - summary_date = str(year) - summary_q = date(year, 1, 1) - else: - return Response({'code': 1, 'msg': '无效的颗粒度'}, status=400) - - start_dt = datetime.combine(start_date, datetime.min.time()) - end_dt = datetime.combine(end_date, datetime.min.time()) - - # ---------- 构建时间序列 ---------- - time_series_map = {} # period_str -> { type_code: {count, amount, profit, fenhong_count, fenhong_amount} } - - def ensure_date(ds): - if ds not in time_series_map: - time_series_map[ds] = {} - return time_series_map[ds] - - # 1. 处理充值记录(Czjilu)—— 完全不变 - cz_types = [t for t in types if t in [2, 3, 4, 5]] - if cz_types: - cz_qs = filter_queryset_by_club( - Czjilu.query.filter( - CreateTime__gte=start_dt, - CreateTime__lt=end_dt, - leixing__in=cz_types, - zhuangtai=3, - ), - request, - ).annotate( - period=trunc_func - ).values('period', 'leixing').annotate( - total_count=Count('id'), - total_amount=Sum('jine') - ).order_by('period', 'leixing') - - for item in cz_qs: - period_str = item['period'].strftime('%Y-%m-%d') if granularity == 'day' else \ - item['period'].strftime('%Y-%m') if granularity == 'month' else \ - item['period'].strftime('%Y') - lx = item['leixing'] - amount = float(item['total_amount'] or 0) - count = item['total_count'] - # 收益:积分(3)收益为金额本身,其他为0 - profit = amount if lx == 3 else 0.0 - - day_data = ensure_date(period_str) - day_data[str(lx)] = { - 'count': count, - 'amount': amount, - 'profit': profit, - 'fenhong_count': 0, # 充值类型无分红 - 'fenhong_amount': 0.0 - } - - # 2. 处理罚款(Penalty)—— 修改:直接从 Penalty 表获取分红总额和分红笔数 - if 6 in types: - # 使用一次聚合查询,同时获取罚款笔数、罚款金额、分红总额、分红笔数 - penalty_qs = filter_penalty_qs( - Penalty.query.filter( - CreateTime__gte=start_dt, - CreateTime__lt=end_dt, - Status=2, - ), - request, - ).annotate( - period=trunc_func - ).values('period').annotate( - penalty_count=Count('id'), - penalty_amount=Sum('FineAmount'), - # 直接使用罚单表中的申请人分红金额字段 - fenhong_amount=Sum('ApplicantBonusAmount'), - fenhong_count=Count('id', filter=Q(ApplicantBonusAmount__gt=0)) - ).order_by('period') - - for item in penalty_qs: - period_str = item['period'].strftime('%Y-%m-%d') if granularity == 'day' else \ - item['period'].strftime('%Y-%m') if granularity == 'month' else \ - item['period'].strftime('%Y') - day_data = ensure_date(period_str) - penalty_amt = float(item['penalty_amount'] or 0) - fenhong_amt = float(item['fenhong_amount'] or 0) - profit = round(penalty_amt - fenhong_amt, 2) - day_data['6'] = { - 'count': item['penalty_count'], - 'amount': penalty_amt, - 'profit': profit, - 'fenhong_count': item['fenhong_count'], - 'fenhong_amount': fenhong_amt - } - - # 构造有序时间序列列表 - time_series = [] - for period_str in sorted(time_series_map.keys()): - entry = {'date': period_str, 'types': {}} - for type_code in sorted(time_series_map[period_str].keys()): - entry['types'][type_code] = time_series_map[period_str][type_code] - time_series.append(entry) - - # ---------- 汇总指定日期(summary)—— 完全保留原逻辑,仅将罚款部分改为从 Penalty 表直接取分红 ---------- - summary_result = {'by_type': {}, 'total_count': 0, 'total_amount': 0.0, 'total_profit': 0.0} - - # 汇总充值类型(完全不变) - for lx in cz_types: - filter_kwargs = { - 'leixing': lx, - 'zhuangtai': 3, - } - if granularity == 'day': - filter_kwargs['CreateTime__date'] = summary_q - elif granularity == 'month': - filter_kwargs['CreateTime__year'] = summary_q.year - filter_kwargs['CreateTime__month'] = summary_q.month - else: - filter_kwargs['CreateTime__year'] = summary_q.year - agg = filter_queryset_by_club( - Czjilu.query.filter(**filter_kwargs), request, - ).aggregate(count=Count('id'), amount=Sum('jine')) - count = agg['count'] or 0 - amount = float(agg['amount'] or 0) - profit = amount if lx == 3 else 0.0 - summary_result['by_type'][str(lx)] = { - 'count': count, - 'amount': round(amount, 2), - 'profit': round(profit, 2), - 'fenhong_count': 0, - 'fenhong_amount': 0.0 - } - summary_result['total_count'] += count - summary_result['total_amount'] += amount - summary_result['total_profit'] += profit - - # 汇总罚款(修改:从 Penalty 表直接取分红,不再查询 PenaltyBonus) - if 6 in types: - penalty_filter = { - 'Status': 2, - } - if granularity == 'day': - penalty_filter['CreateTime__date'] = summary_q - elif granularity == 'month': - penalty_filter['CreateTime__year'] = summary_q.year - penalty_filter['CreateTime__month'] = summary_q.month - else: - penalty_filter['CreateTime__year'] = summary_q.year - - # 一次聚合:罚款笔数、金额、分红总额、分红笔数 - agg = filter_penalty_qs( - Penalty.query.filter(**penalty_filter), request, - ).aggregate( - count=Count('id'), - amount=Sum('FineAmount'), - fenhong_amount=Sum('ApplicantBonusAmount'), - fenhong_count=Count('id', filter=Q(ApplicantBonusAmount__gt=0)) - ) - penalty_count = agg['count'] or 0 - penalty_amount = float(agg['amount'] or 0) - fh_amount = float(agg['fenhong_amount'] or 0) - fh_count = agg['fenhong_count'] or 0 - profit = round(penalty_amount - fh_amount, 2) - - summary_result['by_type']['6'] = { - 'count': penalty_count, - 'amount': round(penalty_amount, 2), - 'profit': profit, - 'fenhong_count': fh_count, - 'fenhong_amount': round(fh_amount, 2) - } - summary_result['total_count'] += penalty_count - summary_result['total_amount'] += penalty_amount - summary_result['total_profit'] += profit - - summary_result['total_amount'] = round(summary_result['total_amount'], 2) - summary_result['total_profit'] = round(summary_result['total_profit'], 2) - - return Response({ - 'code': 0, - 'msg': 'ok', - 'data': { - 'time_series': time_series, - 'summary': summary_result - } - }) + return Response({'code': 0, 'msg': 'ok', 'data': data}) except Exception as e: logger.exception("CwqtczhqView 接口错误") return Response({'code': 1, 'msg': f'服务器内部错误: {str(e)}'}, status=500) -'''class CwqtczhqView(APIView): +'''class CwqtczhqView_LEGACY(APIView): """ 其他充值/罚款数据统计(非会员充值) POST /houtai/cwqtczhq diff --git a/jituan/services/club_penalty.py b/jituan/services/club_penalty.py index 8c96bcf..10f6c38 100644 --- a/jituan/services/club_penalty.py +++ b/jituan/services/club_penalty.py @@ -20,6 +20,12 @@ def _club_id_from_user_uid(uid): return None +def penalty_cz_club_id(fadan): + """罚款支付 czjilu.club_id:与罚单归属一致(申请人俱乐部)。""" + cid = (getattr(fadan, 'ClubID', None) or '').strip() + return cid or CLUB_ID_DEFAULT + + def resolve_penalty_club_id( order=None, penalized_user_id=None, diff --git a/jituan/services/finance_detail_stats.py b/jituan/services/finance_detail_stats.py index 6d6a8df..470f059 100644 --- a/jituan/services/finance_detail_stats.py +++ b/jituan/services/finance_detail_stats.py @@ -1,7 +1,8 @@ """财务子模块统计(订单/会员/其他充值),供 /jituan/houtai/*;旧 /houtai/* 不变。""" from datetime import date, datetime +from decimal import Decimal -from django.db.models import Count, Q, Sum +from django.db.models import Case, Count, DecimalField, Q, Sum, Value, When from django.db.models.functions import TruncDate, TruncMonth, TruncYear from jituan.constants import DATA_SCOPE_ALL @@ -13,6 +14,7 @@ from jituan.services.club_context import ( ) from jituan.services.club_penalty import filter_gsfenhong_qs, filter_penalty_qs from orders.models import Order, Penalty +from products.czjilu_types import CZJILU_LEIXING_SHANGJIA, exclude_penalty_from_cz_qs from products.models import Czjilu, Gsfenhong, Huiyuan, ShangpinLeixing from rank.models import Bankuai @@ -77,6 +79,57 @@ def _summary_date_filter(granularity, summary_q, field_prefix='CreateTime'): return {f'{field_prefix}__year': summary_q.year} +def _penalty_trunc_func(granularity): + if granularity == 'day': + return TruncDate('UpdateTime') + if granularity == 'month': + return TruncMonth('UpdateTime') + return TruncYear('UpdateTime') + + +def _penalty_fenhong_aggregates(): + """罚款分红只统计已入账(BonusCredited),与 process_fadan_fenhong 一致。""" + zero = Value(Decimal('0.00'), output_field=DecimalField(max_digits=12, decimal_places=2)) + return { + 'fenhong_amount': Sum( + Case( + When(BonusCredited=True, then='ApplicantBonusAmount'), + default=zero, + output_field=DecimalField(max_digits=12, decimal_places=2), + ) + ), + 'fenhong_count': Count( + 'id', + filter=Q(BonusCredited=True, ApplicantBonusAmount__gt=0), + ), + } + + +def _czjilu_finance_qs(request, start_dt, end_dt, cz_types): + """充值财务 czjilu:已支付;type4 排除罚款误标。""" + qs = filter_queryset_by_club( + Czjilu.query.filter( + CreateTime__gte=start_dt, + CreateTime__lt=end_dt, + leixing__in=cz_types, + zhuangtai=3, + ), + request, + ) + if CZJILU_LEIXING_SHANGJIA in cz_types: + qs = exclude_penalty_from_cz_qs(qs) + return qs + + +def _czjilu_summary_qs(request, lx, granularity, summary_q): + filter_kwargs = {'leixing': lx, 'zhuangtai': 3} + filter_kwargs.update(_summary_date_filter(granularity, summary_q)) + qs = filter_queryset_by_club(Czjilu.query.filter(**filter_kwargs), request) + if lx == CZJILU_LEIXING_SHANGJIA: + qs = exclude_penalty_from_cz_qs(qs) + return qs + + def build_order_types_payload(): types = ShangpinLeixing.query.filter(shenhezhuangtai=1).values('id', 'jieshao') return list(types) @@ -209,15 +262,9 @@ def build_chongzhi_finance_payload(request, granularity, year, month, types=None cz_types = [t for t in types if t in [2, 3, 4, 5]] if cz_types: - cz_qs = filter_queryset_by_club( - Czjilu.query.filter( - CreateTime__gte=start_dt, - CreateTime__lt=end_dt, - leixing__in=cz_types, - zhuangtai=3, - ), - request, - ).annotate(period=trunc_func).values('period', 'leixing').annotate( + cz_qs = _czjilu_finance_qs(request, start_dt, end_dt, cz_types).annotate( + period=trunc_func, + ).values('period', 'leixing').annotate( total_count=Count('id'), total_amount=Sum('jine'), ).order_by('period', 'leixing') @@ -242,14 +289,18 @@ def build_chongzhi_finance_payload(request, granularity, year, month, types=None } if 6 in types: + penalty_trunc = _penalty_trunc_func(granularity) penalty_qs = filter_penalty_qs( - Penalty.query.filter(CreateTime__gte=start_dt, CreateTime__lt=end_dt, Status=2), + Penalty.query.filter( + UpdateTime__gte=start_dt, + UpdateTime__lt=end_dt, + Status=2, + ), request, - ).annotate(period=trunc_func).values('period').annotate( + ).annotate(period=penalty_trunc).values('period').annotate( penalty_count=Count('id'), penalty_amount=Sum('FineAmount'), - fenhong_amount=Sum('ApplicantBonusAmount'), - fenhong_count=Count('id', filter=Q(ApplicantBonusAmount__gt=0)), + **_penalty_fenhong_aggregates(), ).order_by('period') for item in penalty_qs: @@ -279,9 +330,7 @@ def build_chongzhi_finance_payload(request, granularity, year, month, types=None summary_result = {'by_type': {}, 'total_count': 0, 'total_amount': 0.0, 'total_profit': 0.0} for lx in cz_types: - filter_kwargs = {'leixing': lx, 'zhuangtai': 3} - filter_kwargs.update(_summary_date_filter(granularity, summary_q)) - agg = filter_queryset_by_club(Czjilu.query.filter(**filter_kwargs), request).aggregate( + agg = _czjilu_summary_qs(request, lx, granularity, summary_q).aggregate( count=Count('id'), amount=Sum('jine'), ) count = agg['count'] or 0 @@ -300,12 +349,11 @@ def build_chongzhi_finance_payload(request, granularity, year, month, types=None if 6 in types: penalty_filter = {'Status': 2} - penalty_filter.update(_summary_date_filter(granularity, summary_q)) + penalty_filter.update(_summary_date_filter(granularity, summary_q, field_prefix='UpdateTime')) agg = filter_penalty_qs(Penalty.query.filter(**penalty_filter), request).aggregate( count=Count('id'), amount=Sum('FineAmount'), - fenhong_amount=Sum('ApplicantBonusAmount'), - fenhong_count=Count('id', filter=Q(ApplicantBonusAmount__gt=0)), + **_penalty_fenhong_aggregates(), ) penalty_count = agg['count'] or 0 penalty_amount = float(agg['amount'] or 0) diff --git a/jituan/services/szjilu_accounting.py b/jituan/services/szjilu_accounting.py index 2e077ff..c3d4e1e 100644 --- a/jituan/services/szjilu_accounting.py +++ b/jituan/services/szjilu_accounting.py @@ -309,8 +309,8 @@ def sync_szjilu_income_from_logs(): # 订单已付款且非退款(repair 错误地把 Status=5 退款单也计入了收入) _ORDER_INCOME_STATUSES = [1, 2, 3, 4, 6, 7, 8] -# 微信充值类 czjilu:会员/押金/积分/商家/罚单/考核 -_CZ_WECHAT_LEIXING = [1, 2, 3, 4, 5] +# 微信充值类 czjilu:会员/押金/积分/商家/考核/罚款 +_CZ_WECHAT_LEIXING = [1, 2, 3, 4, 5, 6] def collect_canonical_wechat_income_for_date(stat_date, existing_refs=None): @@ -701,7 +701,7 @@ def reset_all_daily_szjilu(): # 合法微信支付收入(不含退款 Status=5) _LEGIT_ORDER_STATUSES = [1, 2, 3, 4, 6, 7, 8] -_LEGIT_CZ_LEIXING = [1, 2, 3, 4, 5] +_LEGIT_CZ_LEIXING = [1, 2, 3, 4, 5, 6] def collect_legitimate_wechat_payments(since_year, until_year): diff --git a/products/czjilu_types.py b/products/czjilu_types.py new file mode 100644 index 0000000..1fcfb11 --- /dev/null +++ b/products/czjilu_types.py @@ -0,0 +1,81 @@ +"""czjilu 充值类型常量与查询辅助(避免罚款与商家充值混用 leixing=4)。""" +from django.conf import settings +from django.db.models import Q + +# 与 czjilu.leixing 一致 +CZJILU_LEIXING_HUIYUAN = 1 +CZJILU_LEIXING_YAJIN = 2 +CZJILU_LEIXING_JIFEN = 3 +CZJILU_LEIXING_SHANGJIA = 4 +CZJILU_LEIXING_KAOHE = 5 +CZJILU_LEIXING_FAKUAN = 6 + +CZJILU_PAID_STATUS = 3 + +FAKUAN_ORDER_PREFIX = 'FK' +PENALTY_SHUOMING_MARKER = '罚款' +PENALTY_FADAN_TAG = '#fadan:' + + +def penalty_cz_shuoming(fadan_id): + """罚款微信支付/抵扣流水说明(含罚单 ID 便于对账)。""" + desc = getattr(settings, 'FAKUAN_PAY_DESCRIPTION', '罚款缴纳') + return f'{desc}{PENALTY_FADAN_TAG}{fadan_id}' + + +def is_legacy_penalty_leixing4(cz): + """历史误写 leixing=4 的罚款流水。""" + dingdan = (getattr(cz, 'dingdan_id', None) or '').strip() + shuoming = (getattr(cz, 'shuoming', None) or '') + if dingdan.startswith(FAKUAN_ORDER_PREFIX): + return True + if PENALTY_SHUOMING_MARKER in shuoming: + return True + if PENALTY_FADAN_TAG in shuoming: + return True + return False + + +def is_penalty_czjilu(cz): + leixing = getattr(cz, 'leixing', None) + if leixing == CZJILU_LEIXING_FAKUAN: + return True + if leixing == CZJILU_LEIXING_SHANGJIA: + return is_legacy_penalty_leixing4(cz) + return False + + +def is_merchant_recharge_czjilu(cz): + return ( + getattr(cz, 'leixing', None) == CZJILU_LEIXING_SHANGJIA + and not is_legacy_penalty_leixing4(cz) + ) + + +def exclude_penalty_from_cz_qs(qs): + """从 czjilu 查询中排除罚款流水(含历史 leixing=4 误标)。""" + return qs.exclude( + Q(leixing=CZJILU_LEIXING_FAKUAN) + | Q(leixing=CZJILU_LEIXING_SHANGJIA, dingdan_id__startswith=FAKUAN_ORDER_PREFIX) + | Q(leixing=CZJILU_LEIXING_SHANGJIA, shuoming__icontains=PENALTY_SHUOMING_MARKER) + | Q(leixing=CZJILU_LEIXING_SHANGJIA, shuoming__icontains=PENALTY_FADAN_TAG) + ) + + +def merchant_recharge_cz_qs(qs): + """仅真·商家余额充值(微信 CzSJ… 或 leixing=4 且非罚款)。""" + prefix = getattr(settings, 'SHANGJIA_CZ_PREFIX', 'CzSJ') + return exclude_penalty_from_cz_qs(qs).filter( + leixing=CZJILU_LEIXING_SHANGJIA, + dingdan_id__startswith=prefix, + ) + + +def penalty_payment_cz_qs(qs): + """罚款支付流水(微信 FK… 或 leixing=6 / 历史误标)。""" + return qs.filter( + Q(leixing=CZJILU_LEIXING_FAKUAN) + | Q(leixing=CZJILU_LEIXING_SHANGJIA, dingdan_id__startswith=FAKUAN_ORDER_PREFIX) + | Q(leixing=CZJILU_LEIXING_SHANGJIA, shuoming__icontains=PENALTY_SHUOMING_MARKER) + | Q(leixing=CZJILU_LEIXING_SHANGJIA, shuoming__icontains=PENALTY_FADAN_TAG) + ) diff --git a/products/migrations/0010_backfill_penalty_czjilu_leixing.py b/products/migrations/0010_backfill_penalty_czjilu_leixing.py new file mode 100644 index 0000000..6017741 --- /dev/null +++ b/products/migrations/0010_backfill_penalty_czjilu_leixing.py @@ -0,0 +1,41 @@ +# 历史罚款 czjilu 从 leixing=4 纠正为 6,club_id 对齐罚单申请人俱乐部 + +from django.db import migrations + +from products.czjilu_types import ( + CZJILU_LEIXING_FAKUAN, + CZJILU_LEIXING_SHANGJIA, + is_legacy_penalty_leixing4, +) + + +def _backfill_penalty_czjilu(apps, schema_editor): + Czjilu = apps.get_model('products', 'Czjilu') + Penalty = apps.get_model('orders', 'Penalty') + + for cz in Czjilu.objects.filter(leixing=CZJILU_LEIXING_SHANGJIA).iterator(): + if not is_legacy_penalty_leixing4(cz): + continue + updates = {'leixing': CZJILU_LEIXING_FAKUAN} + shuoming = (cz.shuoming or '') + if '#fadan:' in shuoming: + try: + fadan_id = int(shuoming.split('#fadan:')[1].split()[0].strip()) + fadan = Penalty.objects.filter(id=fadan_id).first() + if fadan and getattr(fadan, 'ClubID', None): + updates['club_id'] = fadan.ClubID + except (ValueError, IndexError): + pass + Czjilu.objects.filter(pk=cz.pk).update(**updates) + + +class Migration(migrations.Migration): + + dependencies = [ + ('products', '0006_member_trial_fields'), + ('orders', '0009_penalty_bonus_lock_fields'), + ] + + operations = [ + migrations.RunPython(_backfill_penalty_czjilu, migrations.RunPython.noop), + ] diff --git a/products/models.py b/products/models.py index efce06d..6afc506 100644 --- a/products/models.py +++ b/products/models.py @@ -215,7 +215,10 @@ class Czjilu(QModel): zhuangtai = models.IntegerField(null=True, blank=True, verbose_name='订单状态') yonghuid = models.CharField(max_length=7, db_index=True, verbose_name='用户ID') jine = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='订单金额') - leixing = models.IntegerField(null=True, blank=True, verbose_name='充值类型,1:会员,2:押金,3:积分,4:商家') + leixing = models.IntegerField( + null=True, blank=True, + verbose_name='充值类型,1:会员,2:押金,3:积分,4:商家充值,5:考核,6:罚款缴纳', + ) shuoming = models.CharField(max_length=100, null=True, blank=True, verbose_name='订单说明') club_id = models.CharField( max_length=16, default='xq', db_index=True, verbose_name='所属俱乐部', diff --git a/products/views.py b/products/views.py index 26fa336..7140900 100644 --- a/products/views.py +++ b/products/views.py @@ -2278,6 +2278,14 @@ class ShangjiaHuitiao(View): logger.error(f"商家订单不存在: {out_trade_no}") return self.wechat_response('FAIL', '订单不存在') + from products.czjilu_types import is_merchant_recharge_czjilu + if not is_merchant_recharge_czjilu(order): + logger.error( + '非商家充值订单误入商家回调: %s, leixing=%s, shuoming=%s', + out_trade_no, order.leixing, order.shuoming, + ) + return self.wechat_response('FAIL', '订单类型错误') + # 5. 检查订单状态是否为未支付(9) if order.zhuangtai != 9: logger.warning(f"商家订单状态不是未支付: {order.dingdan_id}, 当前状态: {order.zhuangtai}") @@ -5986,16 +5994,30 @@ class DsqrgmdhView(APIView): action_desc = f"缴纳罚款 {target_price}元" shuoming = f"{identity['name']}抵扣 {action_desc},抵扣金额{required}元" + cz_leixing = leixing + cz_club_id = club_id + cz_zhuangtai = 1 + if leixing == 4: + from jituan.services.club_penalty import penalty_cz_club_id + from products.czjilu_types import ( + CZJILU_LEIXING_FAKUAN, + CZJILU_PAID_STATUS, + PENALTY_FADAN_TAG, + ) + cz_leixing = CZJILU_LEIXING_FAKUAN + cz_club_id = penalty_cz_club_id(fadan_obj) + cz_zhuangtai = CZJILU_PAID_STATUS + shuoming = f"{shuoming}{PENALTY_FADAN_TAG}{fadan_obj.id}" member_purchase_days = 0 czjilu_kwargs = { 'dingdan_id': dingdan_id, - 'zhuangtai': 1, + 'zhuangtai': cz_zhuangtai, 'yonghuid': yonghuid, 'jine': required, - 'leixing': leixing, + 'leixing': cz_leixing, 'shuoming': shuoming, - 'club_id': club_id, + 'club_id': cz_club_id, } if leixing == 1: from jituan.services.member_recharge import club_huiyuan_sellable diff --git a/users/views.py b/users/views.py index 3157933..eaa9828 100644 --- a/users/views.py +++ b/users/views.py @@ -12011,8 +12011,10 @@ class FaKuanPayView(APIView): random_num = random.randint(0, 99) dingdanid = f"FK{timestamp_ms:011d}{timestamp_ns:06d}{random_num:02d}" - from jituan.services.club_write import resolve_club_id_for_write + from jituan.services.club_penalty import penalty_cz_club_id from jituan.services.club_user import get_payment_openid + from products.czjilu_types import CZJILU_LEIXING_FAKUAN, penalty_cz_shuoming + pay_openid, _ = get_payment_openid(request) if not pay_openid: return Response({ @@ -12025,9 +12027,9 @@ class FaKuanPayView(APIView): zhuangtai=9, yonghuid=request.user.UserUID, jine=jine, - leixing=4, - shuoming=f'{settings.FAKUAN_PAY_DESCRIPTION}#fadan:{fadan.id}', - club_id=resolve_club_id_for_write(request, request.user), + leixing=CZJILU_LEIXING_FAKUAN, + shuoming=penalty_cz_shuoming(fadan.id), + club_id=penalty_cz_club_id(fadan), ) try: @@ -12233,6 +12235,11 @@ class FaKuanHuitiaoView(View): logger.error(f"订单不存在: {out_trade_no}") return self.wechat_response('FAIL', '订单不存在') + from products.czjilu_types import is_penalty_czjilu + if not is_penalty_czjilu(order): + logger.error(f"非罚款支付订单误入罚款回调: {out_trade_no}, leixing={order.leixing}") + return self.wechat_response('FAIL', '订单类型错误') + # 5. 检查订单状态是否为未支付(9) if order.zhuangtai != 9: logger.warning(f"订单状态不是未支付: {order.dingdan_id}, 当前状态: {order.zhuangtai}")