feat(jituan): 订单列表修复、财务 club 过滤、数据范围 API
This commit is contained in:
41
jituan/management/commands/backfill_order_club_id.py
Normal file
41
jituan/management/commands/backfill_order_club_id.py
Normal file
@@ -0,0 +1,41 @@
|
||||
"""回填订单 ClubID(空值设为 xq)。"""
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.db import transaction
|
||||
|
||||
from jituan.constants import CLUB_ID_DEFAULT
|
||||
from orders.models import Order
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = '回填 Order.ClubID:空或缺省设为 xq'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--dry-run',
|
||||
action='store_true',
|
||||
help='仅统计不落库',
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
dry_run = options.get('dry_run', False)
|
||||
updated = 0
|
||||
skipped = 0
|
||||
|
||||
for order in Order.query.all().only('id', 'OrderID', 'ClubID'):
|
||||
current = getattr(order, 'ClubID', None) or ''
|
||||
if current:
|
||||
skipped += 1
|
||||
continue
|
||||
|
||||
if dry_run:
|
||||
self.stdout.write(f'[dry-run] {order.OrderID} -> ClubID={CLUB_ID_DEFAULT}')
|
||||
updated += 1
|
||||
continue
|
||||
|
||||
with transaction.atomic():
|
||||
order.ClubID = CLUB_ID_DEFAULT
|
||||
order.save(update_fields=['ClubID'])
|
||||
updated += 1
|
||||
|
||||
suffix = '(dry-run)' if dry_run else ''
|
||||
self.stdout.write(self.style.SUCCESS(f'完成{suffix}:更新 {updated},跳过 {skipped}'))
|
||||
@@ -5,8 +5,13 @@ from decimal import Decimal
|
||||
|
||||
from django.db.models import Sum
|
||||
|
||||
from jituan.services.club_context import resolve_club_id_from_request, resolve_club_scope
|
||||
from jituan.constants import DATA_SCOPE_ALL, CLUB_ID_DEFAULT
|
||||
from jituan.services.club_context import (
|
||||
filter_club_char_field,
|
||||
filter_queryset_by_club,
|
||||
resolve_club_id_from_request,
|
||||
resolve_club_scope,
|
||||
)
|
||||
from jituan.constants import DATA_SCOPE_ALL
|
||||
|
||||
from config.models import DailyIncomeStat, DailyPayoutStat, Szjilu
|
||||
from orders.models import Order
|
||||
@@ -50,53 +55,23 @@ def _build_szjilu_payload(request):
|
||||
|
||||
|
||||
def _order_qs(request):
|
||||
qs = Order.query.all()
|
||||
if resolve_club_scope(request) == DATA_SCOPE_ALL:
|
||||
return qs
|
||||
club_id = resolve_club_id_from_request(request)
|
||||
if hasattr(Order, 'ClubID'):
|
||||
return qs.filter(ClubID=club_id)
|
||||
return qs
|
||||
return filter_queryset_by_club(Order.query.all(), request, club_field='ClubID')
|
||||
|
||||
|
||||
def _czjilu_qs(request):
|
||||
qs = Czjilu.query.all()
|
||||
if resolve_club_scope(request) == DATA_SCOPE_ALL:
|
||||
return qs
|
||||
club_id = resolve_club_id_from_request(request)
|
||||
if hasattr(Czjilu, 'club_id'):
|
||||
return qs.filter(club_id=club_id)
|
||||
return qs
|
||||
return filter_club_char_field(Czjilu.query.all(), request, field='club_id')
|
||||
|
||||
|
||||
def _huiyuangoumai_qs(request):
|
||||
qs = Huiyuangoumai.query.all()
|
||||
if resolve_club_scope(request) == DATA_SCOPE_ALL:
|
||||
return qs
|
||||
club_id = resolve_club_id_from_request(request)
|
||||
if hasattr(Huiyuangoumai, 'club_id'):
|
||||
return qs.filter(club_id=club_id)
|
||||
return qs
|
||||
return filter_club_char_field(Huiyuangoumai.query.all(), request, field='club_id')
|
||||
|
||||
|
||||
def _daily_income_qs(request):
|
||||
qs = DailyIncomeStat.query.all()
|
||||
if resolve_club_scope(request) == DATA_SCOPE_ALL:
|
||||
return qs
|
||||
club_id = resolve_club_id_from_request(request)
|
||||
if hasattr(DailyIncomeStat, 'club_id'):
|
||||
return qs.filter(club_id=club_id)
|
||||
return qs
|
||||
return filter_club_char_field(DailyIncomeStat.query.all(), request, field='club_id')
|
||||
|
||||
|
||||
def _daily_payout_qs(request):
|
||||
qs = DailyPayoutStat.query.all()
|
||||
if resolve_club_scope(request) == DATA_SCOPE_ALL:
|
||||
return qs
|
||||
club_id = resolve_club_id_from_request(request)
|
||||
if hasattr(DailyPayoutStat, 'club_id'):
|
||||
return qs.filter(club_id=club_id)
|
||||
return qs
|
||||
return filter_club_char_field(DailyPayoutStat.query.all(), request, field='club_id')
|
||||
|
||||
|
||||
def build_caiwu_payload(request):
|
||||
|
||||
@@ -46,18 +46,40 @@ def get_active_club(club_id):
|
||||
return None
|
||||
|
||||
|
||||
def filter_club_char_field(qs, request, field='club_id'):
|
||||
"""按 club_id 字符字段过滤;xq 视图兼容历史空值。"""
|
||||
scope = resolve_club_scope(request)
|
||||
if scope == DATA_SCOPE_ALL:
|
||||
return qs
|
||||
if not hasattr(qs, 'filter'):
|
||||
return qs
|
||||
club_id = resolve_club_id_from_request(request)
|
||||
if club_id == CLUB_ID_DEFAULT:
|
||||
from django.db.models import Q
|
||||
return qs.filter(
|
||||
Q(**{field: club_id}) | Q(**{f'{field}__isnull': True}) | Q(**{field: ''})
|
||||
)
|
||||
return qs.filter(**{field: club_id})
|
||||
|
||||
|
||||
def filter_queryset_by_club(qs, request, club_field='club_id'):
|
||||
"""
|
||||
按请求上下文过滤 QuerySet / FluentQuery。
|
||||
集团 ALL_CLUBS 视图不过滤;子公司 SINGLE_CLUB 按 club_id 过滤。
|
||||
历史订单 ClubID 为空时视为 xq,避免列表被滤空。
|
||||
"""
|
||||
scope = resolve_club_scope(request)
|
||||
club_id = resolve_club_id_from_request(request)
|
||||
if scope == DATA_SCOPE_ALL:
|
||||
return qs
|
||||
if hasattr(qs, 'filter'):
|
||||
return qs.filter(**{club_field: club_id})
|
||||
return qs
|
||||
if not hasattr(qs, 'filter'):
|
||||
return qs
|
||||
if club_field == 'ClubID' and club_id == CLUB_ID_DEFAULT:
|
||||
from django.db.models import Q
|
||||
return qs.filter(
|
||||
Q(ClubID=club_id) | Q(ClubID__isnull=True) | Q(ClubID='')
|
||||
)
|
||||
return qs.filter(**{club_field: club_id})
|
||||
|
||||
|
||||
def club_id_for_write(request):
|
||||
|
||||
@@ -18,6 +18,7 @@ from backend.view import (
|
||||
from users.views import KefuPunishmentListView
|
||||
from jituan.views_display import ClubGonggaoView, ClubLunboListView, ClubLunboManageView
|
||||
from jituan.views import (
|
||||
ClubAdminAssignmentListView,
|
||||
ClubAuditLogListView,
|
||||
ClubCaiwuView,
|
||||
ClubChongzhiFinanceView,
|
||||
@@ -39,6 +40,7 @@ urlpatterns = [
|
||||
path('auth/kefu-login', ClubKefuLoginView.as_view(), name='jituan_kefu_login'),
|
||||
path('auth/me-context', ClubMeContextView.as_view(), name='jituan_me_context'),
|
||||
path('auth/dashou-register', ClubDashouRegisterView.as_view(), name='jituan_dashou_register'),
|
||||
path('houtai/admin-assignments', ClubAdminAssignmentListView.as_view(), name='jituan_admin_assignments'),
|
||||
path('houtai/caiwu', ClubCaiwuView.as_view(), name='jituan_caiwu'),
|
||||
path('houtai/szxx', ClubSzxxView.as_view(), name='jituan_szxx'),
|
||||
path('houtai/audit-log', ClubAuditLogListView.as_view(), name='jituan_audit_log'),
|
||||
|
||||
@@ -9,7 +9,8 @@ from rest_framework.views import APIView
|
||||
from rest_framework.throttling import AnonRateThrottle
|
||||
from rest_framework_simplejwt.tokens import RefreshToken
|
||||
|
||||
from jituan.models import Club
|
||||
from jituan.models import Club, AdminAssignment
|
||||
from jituan.constants import ADMIN_ROLE_LABELS
|
||||
from jituan.services.admin_context import build_admin_club_context
|
||||
from jituan.services.wechat_login import login_or_register_by_club
|
||||
from jituan.services.caiwu_stats import build_caiwu_payload
|
||||
@@ -193,6 +194,64 @@ class ClubMeContextView(APIView):
|
||||
})
|
||||
|
||||
|
||||
class ClubAdminAssignmentListView(APIView):
|
||||
"""POST /jituan/houtai/admin-assignments — 查看数据范围任职(非 gvsdsdk 功能权限)。"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
username = (request.data.get('phone') or request.data.get('username') or '').strip()
|
||||
if not username:
|
||||
return Response({'code': 401, 'msg': '缺少账号'}, status=401)
|
||||
kefu_obj, permissions = verify_kefu_permission(request, username)
|
||||
if kefu_obj is None:
|
||||
return permissions
|
||||
|
||||
user = request.user
|
||||
is_super = bool(user.IsSuperuser) or user.UserType == 'admin'
|
||||
if is_super:
|
||||
qs = AdminAssignment.query.filter(status=1).order_by('yonghuid', '-is_primary', 'club_id')
|
||||
else:
|
||||
qs = AdminAssignment.query.filter(yonghuid=user.UserUID, status=1).order_by('-is_primary')
|
||||
|
||||
club_name_map = {
|
||||
c.club_id: c.name for c in Club.query.filter(status=1)
|
||||
}
|
||||
uids = list({a.yonghuid for a in qs})
|
||||
user_map = {}
|
||||
if uids:
|
||||
for u in User.query.filter(UserUID__in=uids).only('UserUID', 'Phone', 'UserName'):
|
||||
user_map[u.UserUID] = u.Phone or u.UserName or u.UserUID
|
||||
|
||||
rows = []
|
||||
for a in qs:
|
||||
club_label = '集团(全部子公司)'
|
||||
if a.club_id:
|
||||
club_label = club_name_map.get(a.club_id, a.club_id)
|
||||
rows.append({
|
||||
'yonghuid': a.yonghuid,
|
||||
'phone': user_map.get(a.yonghuid, ''),
|
||||
'club_id': a.club_id,
|
||||
'club_label': club_label,
|
||||
'role_code': a.role_code,
|
||||
'role_name': ADMIN_ROLE_LABELS.get(a.role_code, a.role_code),
|
||||
'data_scope': a.data_scope,
|
||||
'is_primary': a.is_primary,
|
||||
})
|
||||
|
||||
return Response({
|
||||
'code': 0,
|
||||
'msg': 'ok',
|
||||
'data': {
|
||||
'list': rows,
|
||||
'perm_note': (
|
||||
'功能菜单权限(能进哪些页面)在「角色管理」配置;'
|
||||
'本表 admin_assignment 仅控制集团/子公司数据范围。'
|
||||
),
|
||||
'current_context': build_admin_club_context(user),
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
class ClubCaiwuView(APIView):
|
||||
"""
|
||||
俱乐部维度财务(新接口)。
|
||||
|
||||
@@ -7100,19 +7100,17 @@ class KefuGetOrderListView(APIView):
|
||||
'6': platform_orders.filter(Status=6).count(),
|
||||
}
|
||||
|
||||
# ---------- 分页查询 ----------
|
||||
total_count = club_orders.filter(q_conditions).count()
|
||||
|
||||
orders_qs = club_orders.filter(q_conditions).select_related(
|
||||
'pingtai_kuozhan'
|
||||
).only(
|
||||
'OrderID', 'Description', 'Status', 'ProductTypeID', 'Platform',
|
||||
'Amount', 'ImageURL', 'CreateTime', 'Nickname', 'PlayerID',
|
||||
'pingtai_kuozhan__BossID'
|
||||
).order_by('-CreateTime')[(page-1)*page_size : page*page_size]
|
||||
# ---------- 分页查询(避免 only+select_related 导致列表为空) ----------
|
||||
list_qs = club_orders.filter(q_conditions).select_related('pingtai_kuozhan').order_by('-CreateTime')
|
||||
total_count = list_qs.count()
|
||||
orders_page = list_qs[(page - 1) * page_size: page * page_size].to_list()
|
||||
|
||||
# ---------- 获取老板昵称 ----------
|
||||
laoban_ids = [o.pingtai_kuozhan.BossID for o in orders_qs if hasattr(o, 'pingtai_kuozhan') and o.pingtai_kuozhan.BossID]
|
||||
laoban_ids = [
|
||||
o.pingtai_kuozhan.BossID
|
||||
for o in orders_page
|
||||
if getattr(o, 'pingtai_kuozhan', None) and o.pingtai_kuozhan.BossID
|
||||
]
|
||||
nickname_map = {}
|
||||
if laoban_ids:
|
||||
users = User.query.filter(UserUID__in=laoban_ids).select_related('BossProfile').only(
|
||||
@@ -7122,7 +7120,7 @@ class KefuGetOrderListView(APIView):
|
||||
nickname_map[user.UserUID] = (user.BossProfile.nickname or '') if getattr(user, 'BossProfile', None) else ''
|
||||
|
||||
result_list = []
|
||||
for order in orders_qs:
|
||||
for order in orders_page:
|
||||
ext = getattr(order, 'pingtai_kuozhan', None)
|
||||
laoban_id = ext.BossID if ext else ''
|
||||
youxi_nicheng = nickname_map.get(laoban_id, order.Nickname or '')
|
||||
@@ -7183,8 +7181,8 @@ class KefuGetShangjiaOrderListView(APIView):
|
||||
def post(self, request):
|
||||
start_total = time.time()
|
||||
|
||||
# 1. 获取前端参数
|
||||
username = request.data.get('username', '').strip()
|
||||
# 1. 获取前端参数(兼容 phone / username)
|
||||
username = request.data.get('username', '').strip() or request.data.get('phone', '').strip()
|
||||
if not username:
|
||||
return Response({'code': 400, 'msg': '缺少username'})
|
||||
|
||||
@@ -7277,18 +7275,16 @@ class KefuGetShangjiaOrderListView(APIView):
|
||||
}
|
||||
|
||||
# ---------- 分页查询 ----------
|
||||
total_count = club_orders.filter(q_conditions).count()
|
||||
|
||||
orders_qs = club_orders.filter(q_conditions).select_related(
|
||||
'shangjia_kuozhan'
|
||||
).only(
|
||||
'OrderID', 'Description', 'Status', 'ProductTypeID', 'Platform',
|
||||
'Amount', 'ImageURL', 'CreateTime', 'Nickname', 'PlayerID',
|
||||
'shangjia_kuozhan__MerchantID'
|
||||
).order_by('-CreateTime')[(page-1)*page_size : page*page_size]
|
||||
list_qs = club_orders.filter(q_conditions).select_related('shangjia_kuozhan').order_by('-CreateTime')
|
||||
total_count = list_qs.count()
|
||||
orders_page = list_qs[(page - 1) * page_size: page * page_size].to_list()
|
||||
|
||||
# ---------- 获取商家昵称 ----------
|
||||
shangjia_ids = [o.shangjia_kuozhan.MerchantID for o in orders_qs if hasattr(o, 'shangjia_kuozhan') and o.shangjia_kuozhan.MerchantID]
|
||||
shangjia_ids = [
|
||||
o.shangjia_kuozhan.MerchantID
|
||||
for o in orders_page
|
||||
if getattr(o, 'shangjia_kuozhan', None) and o.shangjia_kuozhan.MerchantID
|
||||
]
|
||||
nickname_map = {}
|
||||
if shangjia_ids:
|
||||
users = User.query.filter(UserUID__in=shangjia_ids).select_related('ShopProfile').only(
|
||||
@@ -7298,7 +7294,7 @@ class KefuGetShangjiaOrderListView(APIView):
|
||||
nickname_map[user.UserUID] = user.ShopProfile.nicheng or '' if hasattr(user, 'ShopProfile') else ''
|
||||
|
||||
result_list = []
|
||||
for order in orders_qs:
|
||||
for order in orders_page:
|
||||
ext = getattr(order, 'shangjia_kuozhan', None)
|
||||
shangjia_id = ext.MerchantID if ext else ''
|
||||
youxi_nicheng = order.Nickname or '' # 商家订单的玩家昵称
|
||||
|
||||
Reference in New Issue
Block a user