feat: 管事分佣明细v2与打手本人入账额展示

This commit is contained in:
XingQue committed 2026-08-10 18:45:18 +08:00
1 parent b49da23bd7
commit 712d497672
4 files changed
+185 -3

No files matched your search

+68 -1
View File
@@ -797,6 +797,11 @@ class DashouDingdanHuoquView1(APIView):
if wants_auto_settle_pending(data):
query &= Q(Status=8, AutoSettleEligible=True, AutoExpireAt__isnull=False)
# 佣金记录页:with_my_share=1 时追加本人入账额(不改 jine 语义)
with_my_share = str(data.get('with_my_share') or '').strip().lower() in (
'1', 'true', 'yes', 'y',
)
# 5. 执行查询,按创建时间倒序
dingdan_query = Order.query.filter(query).order_by('-CreateTime')
@@ -826,12 +831,18 @@ class DashouDingdanHuoquView1(APIView):
from orders.services.team_recruit import batch_team_list_flags
page_oids = [o.OrderID for o in current_page_orders]
team_flags = batch_team_list_flags(page_oids, viewer_uid=yonghuid)
settle_map = {}
if with_my_share and page_oids:
settle_map = self._batch_my_settle_amounts(page_oids, yonghuid)
order_list = []
for order in current_page_orders:
full_amt = float(order.PlayerCommission) if order.PlayerCommission else 0.0
order_data = {
'dingdan_id': order.OrderID,
'zhuangtai': order.Status,
'jine': float(order.PlayerCommission) if order.PlayerCommission else 0.0,
'jine': full_amt,
'jieshao': order.Description or '',
'tupian': order.ImageURL or '',
'nicheng': order.Nickname or '',
@@ -840,6 +851,15 @@ class DashouDingdanHuoquView1(APIView):
order_data.update(order_auto_settle_payload(order))
tf = team_flags.get(order.OrderID) or {'has_team': False}
order_data.update(tf)
if with_my_share:
is_team = bool(tf.get('has_team'))
my_amt = settle_map.get(order.OrderID)
if my_amt is None:
my_amt = full_amt
order_data['my_settle_amount'] = float(my_amt)
order_data['is_team'] = is_team
if 'team_role' not in order_data:
order_data['team_role'] = tf.get('team_role') or ''
order_list.append(order_data)
# 8. 判断是否还有更多数据
@@ -878,6 +898,53 @@ class DashouDingdanHuoquView1(APIView):
'data': None
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
@staticmethod
def _batch_my_settle_amounts(order_ids, user_uid):
"""
本人实际入账额:优先 team_settle_ledger;否则 TeamMember.share_amount;
都没有则由调用方回落 PlayerCommission。
返回 {order_id: float}
"""
from decimal import Decimal
from orders.models import TeamSettleLedger, TeamRecruit, TeamMember
oids = [str(x) for x in (order_ids or []) if x]
uid = (user_uid or '').strip()
if not oids or not uid:
return {}
out = {}
try:
for row in TeamSettleLedger.query.filter(order_id__in=oids, user_uid=uid):
oid = row.order_id
amt = Decimal(str(row.amount or 0))
out[oid] = float(out.get(oid, 0) + float(amt))
except Exception as e:
logger.warning('batch settle ledger 失败: %s', e)
missing = [oid for oid in oids if oid not in out]
if not missing:
return out
try:
recs = list(TeamRecruit.query.filter(order_id__in=missing).order_by('-id'))
best = {}
for r in recs:
if r.order_id not in best:
best[r.order_id] = r
if best:
rid_to_oid = {r.id: oid for oid, r in best.items()}
for m in TeamMember.query.filter(
recruit_id__in=list(rid_to_oid.keys()),
user_uid=uid,
join_status=TeamMember.JOIN_ACTIVE,
):
oid = rid_to_oid.get(m.recruit_id)
if oid and oid not in out:
out[oid] = float(Decimal(str(m.share_amount or 0)))
except Exception as e:
logger.warning('batch member share 失败: %s', e)
return out
class DashouCOSZhengshuView(APIView):
+2 -1
View File
@@ -4,7 +4,7 @@ from django.views.decorators.csrf import csrf_exempt
from .views import ShangpinHuoquView, ShangpinXiangqingHuoquView, DashouHuiyuanList, \
YajinGoumai, YajinHuitiao, YajinShibai, YajinLunxun, JifenBuchong, JifenHuitiao, JifenShibai, \
JifenLunxun, HuiyuanGoumai, HuiyuanHuitiao, HuiyuanShibai, HuiyuanLunxun, ShangjiaChenggong, \
ShangjiaChongzhi, ShangjiaHuitiao, ShangjiaShibai, GuanshiFenhongListAPI, AdGetAllDataView, ShangpinLeixingFabuView, \
ShangjiaChongzhi, ShangjiaHuitiao, ShangjiaShibai, GuanshiFenhongListAPI, GuanshiFenhongListV2API, AdGetAllDataView, ShangpinLeixingFabuView, \
ShangpinZhuanquFabuView, ShangpinFabuView, GuizeTupianShangchuanView, AdHuiyuanHqView, HuiyuanXiugaiView, \
AdHuiyuanFbView, AdShangpinHuQu, AdLeixingXiuGai, AdLunboXiuGai, AdZhuanquXiuGai, AdGonggaoXiuGai, \
AdShangpinGuizeXiuGai, AdShangpinXiuGai, AdShangpinNeiRongHuoQu, \
@@ -56,6 +56,7 @@ urlpatterns = [
path('sjshibai', ShangjiaShibai.as_view(), name='商家支付失败'), # 商家支付失败处理
path('sjcg', ShangjiaChenggong.as_view(), name='商家支付成功'), # 商家支付成功轮询
path('fenhonghq', GuanshiFenhongListAPI.as_view(), name='管事获取打手充值记录'),
path('guanshi-fenhong-v2', GuanshiFenhongListV2API.as_view(), name='管事分红明细v2'),
path('adhqlx', AdGetAllDataView.as_view(), name='管理员获取商品类型会员类型专区类型'),
path('leixingfabu', ShangpinLeixingFabuView.as_view(), name='添加商品类型'),
# 商品专区相关接口
+2 -1
View File
@@ -21,6 +21,7 @@ from .product_query import (
ShangpinXiangqingHuoquView,
DashouHuiyuanList,
GuanshiFenhongListAPI,
GuanshiFenhongListV2API,
AdGetAllDataView,
HuiyuanTypeListView,
HuiyuanRecordListView,
@@ -97,7 +98,7 @@ logger = logging.getLogger(__name__)
__all__ = [
# product_query
'ShangpinHuoquView', 'ShangpinXiangqingHuoquView', 'DashouHuiyuanList',
'GuanshiFenhongListAPI', 'AdGetAllDataView',
'GuanshiFenhongListAPI', 'GuanshiFenhongListV2API', 'AdGetAllDataView',
'HuiyuanTypeListView', 'HuiyuanRecordListView', 'ZuzhangFenhongView',
# yajin_payment
'YajinGoumai', 'YajinHuitiao', 'YajinShibai', 'YajinLunxun',
+113
View File
@@ -457,6 +457,119 @@ class GuanshiFenhongListAPI(APIView):
})
FENHONG_LEIXING_LABELS = {
1: '会员分红',
2: '押金分红',
3: '订单分佣',
}
class GuanshiFenhongListV2API(APIView):
"""
管事分红明细 v2(旧 fenhonghq 不动)。
POST /shangpin/guanshi-fenhong-v2
- 列表带类型/说明/订单号/双时间字段
- 可选 fenhong_leixing 筛选;分项统计始终按管事全量汇总
"""
permission_classes = [IsAuthenticated]
def post(self, request):
try:
from django.db.models import Sum, Count
yonghuid = request.user.UserUID
page = int(request.data.get('page', 1) or 1)
page_size = int(request.data.get('page_size', 50) or 50)
if page < 1:
page = 1
if page_size < 1 or page_size > 100:
page_size = 50
leixing_raw = request.data.get('fenhong_leixing', None)
leixing_filter = None
if leixing_raw is not None and str(leixing_raw).strip() != '':
try:
leixing_filter = int(leixing_raw)
except (TypeError, ValueError):
return Response({'code': 400, 'msg': 'fenhong_leixing 无效', 'data': {}})
if leixing_filter not in FENHONG_LEIXING_LABELS:
return Response({'code': 400, 'msg': 'fenhong_leixing 无效', 'data': {}})
base_qs = Gsfenhong.objects.filter(guanshi=yonghuid)
# 分项统计:始终全量(不被列表筛选缩小)
amount_by = {'1': '0.00', '2': '0.00', '3': '0.00'}
count_by = {'1': 0, '2': 0, '3': 0}
total_amount = Decimal('0.00')
for row in base_qs.values('fenhong_leixing').annotate(
amount=Sum('fenhong'), cnt=Count('id'),
):
key = str(int(row.get('fenhong_leixing') or 1))
amt = Decimal(str(row.get('amount') or 0)).quantize(Decimal('0.01'))
cnt = int(row.get('cnt') or 0)
if key in amount_by:
amount_by[key] = str(amt)
count_by[key] = cnt
total_amount += amt
list_qs = base_qs
if leixing_filter is not None:
list_qs = list_qs.filter(fenhong_leixing=leixing_filter)
zong_tiaoshu = list_qs.count()
chongzhi_dashou_shuliang = base_qs.values('dashouid').distinct().count()
offset = (page - 1) * page_size
rows = list(
list_qs.order_by('-CreateTime')
.values(
'dingdan_id', 'dashouid', 'avatar', 'nicheng', 'fenhong',
'shuoming', 'fenhong_leixing', 'CreateTime',
)[offset:offset + page_size]
)
formatted_list = []
for item in rows:
leixing = int(item.get('fenhong_leixing') or 1)
ts = ''
if item.get('CreateTime'):
ts = item['CreateTime'].strftime('%Y-%m-%d %H:%M:%S')
formatted_list.append({
'yonghuid': item.get('dashouid') or '',
'avatar': item.get('avatar') or '',
'nicheng': item.get('nicheng') or '未知用户',
'fenhong': str(item.get('fenhong') or '0'),
'CreateTime': ts,
'create_time': ts,
'fenhong_leixing': leixing,
'leixing_label': FENHONG_LEIXING_LABELS.get(leixing, '分红'),
'shuoming': (item.get('shuoming') or '').strip(),
'dingdan_id': item.get('dingdan_id') or '',
})
return Response({
'code': 200,
'msg': '成功',
'data': {
'list': formatted_list,
'total': zong_tiaoshu,
'chongzhi_dashou_shuliang': chongzhi_dashou_shuliang,
'page': page,
'page_size': page_size,
'has_more': len(formatted_list) == page_size,
'total_amount': str(total_amount.quantize(Decimal('0.01'))),
'amount_by_leixing': amount_by,
'count_by_leixing': count_by,
},
})
except Exception as e:
logger.error('查询管事分红记录 v2 失败: %s', e, exc_info=True)
return Response({
'code': 500,
'msg': f'服务器错误: {str(e)}',
'data': {},
})
# views.py - 商品模块视图
class AdGetAllDataView(APIView):
"""