fix: separate penalty czjilu from merchant recharge and correct finance stats

This commit is contained in:
XingQue
2026-06-27 18:50:32 +08:00
parent 65477592af
commit 54f8e23488
9 changed files with 249 additions and 240 deletions

81
products/czjilu_types.py Normal file
View File

@@ -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)
)

View File

@@ -0,0 +1,41 @@
# 历史罚款 czjilu 从 leixing=4 纠正为 6club_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),
]

View File

@@ -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='所属俱乐部',

View File

@@ -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