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

View File

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