From 98b58f8058f3711c29f2bdbac6a9b1b7271935be Mon Sep 17 00:00:00 2001 From: XingQue Date: Tue, 21 Jul 2026 03:33:49 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=95=86=E5=93=81=E5=9F=BA=E7=A1=80?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=AE=B9=E9=94=99=E9=81=BF=E5=85=8D500?= =?UTF-8?q?=EF=BC=9B=E7=B1=BB=E5=9E=8B=E4=B8=93=E5=8C=BA=E6=8C=89=E4=BF=B1?= =?UTF-8?q?=E4=B9=90=E9=83=A8=E4=B8=8A=E6=9E=B6=E5=B9=B6=E7=AE=A1=E4=B8=93?= =?UTF-8?q?=E5=8C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 具体俱乐部只返回已上架类型;上架/下架允许 7007a 或 8080a。 Co-authored-by: Cursor --- backend/views/products.py | 163 ++++++++++++++++++++--------------- backend/views/system.py | 135 ++++++++++++++++------------- jituan/views_club_catalog.py | 15 +++- 3 files changed, 178 insertions(+), 135 deletions(-) diff --git a/backend/views/products.py b/backend/views/products.py index 5a7ece1..6f30038 100644 --- a/backend/views/products.py +++ b/backend/views/products.py @@ -146,83 +146,104 @@ class GetProductBaseDataView(APIView): from jituan.services.club_context import filter_club_char_field, resolve_club_scope from jituan.constants import DATA_SCOPE_ALL from jituan.services.club_write import resolve_club_id_for_write - from shop.services.shenhe_config import get_or_create_shenhe_config, is_zhi_kan_shenhe_enabled - type_qs = ShangpinLeixing.query.all().order_by('paixu') - # 批量聚合商品数量,避免循环内 N+1 count(按俱乐部) - _public_sp = filter_club_char_field( - Shangpin.query.filter( - dianpu__isnull=True, dianpu_leixing__isnull=True, leixing_id__isnull=False - ), - request, - field='club_id', - ) - _type_counts = { - item['leixing_id']: item['cnt'] - for item in _public_sp.values('leixing_id').annotate(cnt=Count('id')) - } - type_list = [] - for t in type_qs: - product_count = _type_counts.get(t.id, 0) - type_list.append({ - 'id': t.id, - 'jieshao': t.jieshao or '', - 'tupian_url': t.tupian_url or '', - 'paixu': t.paixu, - 'shenhezhuangtai': t.shenhezhuangtai, - 'product_count': product_count, - }) - - # 获取专区(按俱乐部隔离;集团汇总看全部) - zone_qs = filter_club_char_field( - ShangpinZhuanqu.query.all().order_by('paixu'), request, field='club_id' - ) - _zone_counts = { - item['zhuanqu_id']: item['cnt'] - for item in filter_club_char_field( + try: + type_qs = ShangpinLeixing.query.all().order_by('paixu') + # 批量聚合商品数量,避免循环内 N+1 count(按俱乐部) + _public_sp = filter_club_char_field( Shangpin.query.filter( - dianpu__isnull=True, dianpu_leixing__isnull=True, zhuanqu_id__isnull=False + dianpu__isnull=True, dianpu_leixing__isnull=True, leixing_id__isnull=False ), request, field='club_id', - ).values('zhuanqu_id').annotate(cnt=Count('id')) - } - zone_list = [] - for z in zone_qs: - product_count = _zone_counts.get(z.id, 0) - zone_list.append({ - 'id': z.id, - 'mingzi': z.mingzi or '', - 'leixing_id': z.leixing_id, - 'paixu': z.paixu, - 'shenhezhuangtai': z.shenhezhuangtai, - 'product_count': product_count, - 'club_id': getattr(z, 'club_id', '') or '', - }) - - # 获取所有会员(只需 ID、介绍、价格) - member_list = list(Huiyuan.query.all().values('huiyuan_id', 'jieshao', 'jiage')) - - # 本俱乐部「只看审核」开关(集团汇总不下发可写状态) - club_scoped = resolve_club_scope(request) != DATA_SCOPE_ALL - zhi_kan_shenhe = True - club_id = '' - if club_scoped: - club_id = resolve_club_id_for_write(request) or '' - get_or_create_shenhe_config(club_id) - zhi_kan_shenhe = is_zhi_kan_shenhe_enabled(club_id) - - return Response({ - 'code': 0, - 'data': { - 'type_list': type_list, - 'zone_list': zone_list, - 'member_list': member_list, - 'zhi_kan_shenhe': zhi_kan_shenhe, - 'club_scoped': club_scoped, - 'club_id': club_id, + ) + # 用 Django QS 聚合,避免 Fluent values 链在部分环境下异常 + _type_counts = { + item['leixing_id']: item['cnt'] + for item in _public_sp.values('leixing_id').annotate(cnt=Count('id')) } - }) + type_list = [] + for t in type_qs: + product_count = _type_counts.get(t.id, 0) + type_list.append({ + 'id': t.id, + 'jieshao': t.jieshao or '', + 'tupian_url': t.tupian_url or '', + 'paixu': t.paixu, + 'shenhezhuangtai': t.shenhezhuangtai, + 'product_count': product_count, + }) + + # 获取专区(按俱乐部隔离;集团汇总看全部) + zone_qs = filter_club_char_field( + ShangpinZhuanqu.query.all().order_by('paixu'), request, field='club_id' + ) + _zone_counts = { + item['zhuanqu_id']: item['cnt'] + for item in filter_club_char_field( + Shangpin.query.filter( + dianpu__isnull=True, dianpu_leixing__isnull=True, zhuanqu_id__isnull=False + ), + request, + field='club_id', + ).values('zhuanqu_id').annotate(cnt=Count('id')) + } + zone_list = [] + for z in zone_qs: + product_count = _zone_counts.get(z.id, 0) + zone_list.append({ + 'id': z.id, + 'mingzi': z.mingzi or '', + 'leixing_id': z.leixing_id, + 'paixu': z.paixu, + 'shenhezhuangtai': z.shenhezhuangtai, + 'product_count': product_count, + 'club_id': getattr(z, 'club_id', '') or '', + }) + + # 获取所有会员(只需 ID、介绍、价格) + member_list = [] + for m in Huiyuan.query.all().values('huiyuan_id', 'jieshao', 'jiage'): + member_list.append({ + 'huiyuan_id': m['huiyuan_id'], + 'jieshao': m['jieshao'] or '', + 'jiage': float(m['jiage'] or 0), + }) + + # 本俱乐部「只看审核」开关(集团汇总不下发可写状态) + club_scoped = resolve_club_scope(request) != DATA_SCOPE_ALL + zhi_kan_shenhe = True + club_id = '' + if club_scoped: + club_id = resolve_club_id_for_write(request) or '' + try: + from shop.services.shenhe_config import ( + get_or_create_shenhe_config, + is_zhi_kan_shenhe_enabled, + ) + get_or_create_shenhe_config(club_id) + zhi_kan_shenhe = is_zhi_kan_shenhe_enabled(club_id) + except Exception: + logging.getLogger(__name__).exception( + '读取只看审核配置失败 club_id=%s(请确认已 migrate shop 0004)', + club_id, + ) + zhi_kan_shenhe = True + + return Response({ + 'code': 0, + 'data': { + 'type_list': type_list, + 'zone_list': zone_list, + 'member_list': member_list, + 'zhi_kan_shenhe': zhi_kan_shenhe, + 'club_scoped': club_scoped, + 'club_id': club_id, + } + }) + except Exception: + logging.getLogger(__name__).exception('GetProductBaseDataView 失败') + return Response({'code': 500, 'msg': '获取商品基础数据失败,请稍后重试'}) diff --git a/backend/views/system.py b/backend/views/system.py index 6f4a255..c55585c 100644 --- a/backend/views/system.py +++ b/backend/views/system.py @@ -237,69 +237,84 @@ class GetProductTypeZoneView(APIView): if '7007a' not in permissions: return Response({'code': 403, 'msg': '您无权访问此页面'}) - # 查询专区数量(按俱乐部隔离;集团汇总看全部) - zone_qs_for_count = filter_club_char_field( - ShangpinZhuanqu.query.all(), request, field='club_id' - ) - zone_count_rows = zone_qs_for_count.values('leixing_id').annotate(cnt=Count('id')) - zone_count_map = {row['leixing_id']: row['cnt'] for row in zone_count_rows} - - types = ShangpinLeixing.query.all().order_by('-paixu') # paixu 越大越靠前 - type_list = [] - for t in types: - # 从预聚合的 map 中取专区数量 - zone_count = zone_count_map.get(t.id, 0) - type_list.append({ - 'id': t.id, - 'jieshao': t.jieshao or '', - 'tupian_url': t.tupian_url or '', - 'yaoqiuleixing': t.yaoqiuleixing, - 'huiyuan_id': t.huiyuan_id or '', - 'yongjin': float(t.yongjin) if t.yongjin is not None else None, - 'shenhezhuangtai': t.shenhezhuangtai, - 'paixu': t.paixu, - 'product_count': zone_count, - }) - - # 查询专区(按俱乐部隔离) - zones = filter_club_char_field( - ShangpinZhuanqu.query.all().order_by('-paixu'), request, field='club_id' - ) - zone_list = [ - { - 'id': z.id, - 'mingzi': z.mingzi or '', - 'leixing_id': z.leixing_id, - 'shenhezhuangtai': z.shenhezhuangtai, - 'paixu': z.paixu, - 'club_id': getattr(z, 'club_id', '') or '', - } - for z in zones - ] - - # 查询所有会员(只返回 id、介绍、价格) - members = Huiyuan.query.all().values('huiyuan_id', 'jieshao', 'jiage') - member_list = [ - { - 'huiyuan_id': m['huiyuan_id'], - 'jieshao': m['jieshao'], - 'jiage': float(m['jiage']), - } - for m in members - ] - from jituan.constants import DATA_SCOPE_ALL - from jituan.services.club_context import resolve_club_scope + from jituan.services.club_context import resolve_club_scope, resolve_club_id_from_request + from jituan.services.club_shangpin_leixing import build_club_leixing_admin_list - return Response({ - 'code': 0, - 'data': { - 'type_list': type_list, - 'zone_list': zone_list, - 'member_list': member_list, - 'club_scoped': resolve_club_scope(request) != DATA_SCOPE_ALL, + club_scoped = resolve_club_scope(request) != DATA_SCOPE_ALL + club_id = resolve_club_id_from_request(request) + + try: + zone_qs_for_count = filter_club_char_field( + ShangpinZhuanqu.query.all(), request, field='club_id' + ) + zone_count_map = { + row['leixing_id']: row['cnt'] + for row in zone_qs_for_count.values('leixing_id').annotate(cnt=Count('id')) } - }) + + if club_scoped: + # 具体俱乐部:仅本俱乐部已上架类型(与抢单端一致) + admin_payload = build_club_leixing_admin_list(request) + type_list = [] + for item in (admin_payload.get('list') or []): + type_list.append({ + **item, + 'product_count': zone_count_map.get(item['id'], 0), + 'club_enabled': True, + }) + else: + type_list = [] + for t in ShangpinLeixing.query.all().order_by('-paixu'): + type_list.append({ + 'id': t.id, + 'jieshao': t.jieshao or '', + 'tupian_url': t.tupian_url or '', + 'yaoqiuleixing': t.yaoqiuleixing, + 'huiyuan_id': t.huiyuan_id or '', + 'yongjin': float(t.yongjin) if t.yongjin is not None else None, + 'shenhezhuangtai': t.shenhezhuangtai, + 'paixu': t.paixu, + 'product_count': zone_count_map.get(t.id, 0), + }) + + zones = filter_club_char_field( + ShangpinZhuanqu.query.all().order_by('-paixu'), request, field='club_id' + ) + zone_list = [ + { + 'id': z.id, + 'mingzi': z.mingzi or '', + 'leixing_id': z.leixing_id, + 'shenhezhuangtai': z.shenhezhuangtai, + 'paixu': z.paixu, + 'club_id': getattr(z, 'club_id', '') or '', + } + for z in zones + ] + + member_list = [ + { + 'huiyuan_id': m['huiyuan_id'], + 'jieshao': m['jieshao'] or '', + 'jiage': float(m['jiage'] or 0), + } + for m in Huiyuan.query.all().values('huiyuan_id', 'jieshao', 'jiage') + ] + + return Response({ + 'code': 0, + 'data': { + 'type_list': type_list, + 'zone_list': zone_list, + 'member_list': member_list, + 'club_scoped': club_scoped, + 'club_id': club_id if club_scoped else '', + } + }) + except Exception: + logging.getLogger(__name__).exception('GetProductTypeZoneView 失败') + return Response({'code': 500, 'msg': '获取类型专区失败,请稍后重试'}) diff --git a/jituan/views_club_catalog.py b/jituan/views_club_catalog.py index c6ba68d..68603c4 100644 --- a/jituan/views_club_catalog.py +++ b/jituan/views_club_catalog.py @@ -6,6 +6,13 @@ from rest_framework.permissions import IsAuthenticated from backend.view import verify_kefu_permission +# 小程序配置 8080a 或 商品类型专区 7007a 均可上架/下架 +CLUB_LEIXING_PERMS = ('8080a', '7007a') + + +def _has_club_leixing_perm(permissions): + return any(p in permissions for p in CLUB_LEIXING_PERMS) + class ClubShangpinLeixingListView(APIView): """POST /jituan/houtai/club-leixing-list""" @@ -19,7 +26,7 @@ class ClubShangpinLeixingListView(APIView): kefu, permissions = verify_kefu_permission(request, phone) if kefu is None: return permissions - if '8080a' not in permissions: + if not _has_club_leixing_perm(permissions): return Response({'code': 403, 'msg': '无权限管理小程序商品类型'}) from jituan.services.club_shangpin_leixing import build_club_leixing_admin_list payload = build_club_leixing_admin_list(request) @@ -38,7 +45,7 @@ class ClubShangpinLeixingCatalogView(APIView): kefu, permissions = verify_kefu_permission(request, phone) if kefu is None: return permissions - if '8080a' not in permissions: + if not _has_club_leixing_perm(permissions): return Response({'code': 403, 'msg': '无权限'}) from jituan.services.club_shangpin_leixing import build_club_leixing_catalog return Response({'code': 0, 'data': build_club_leixing_catalog(request)}) @@ -57,7 +64,7 @@ class ClubShangpinLeixingEnableView(APIView): kefu, permissions = verify_kefu_permission(request, phone) if kefu is None: return permissions - if '8080a' not in permissions: + if not _has_club_leixing_perm(permissions): return Response({'code': 403, 'msg': '无权限'}) from jituan.services.club_shangpin_leixing import enable_leixing_for_club data, err = enable_leixing_for_club(request, int(leixing_id), request.data.get('paixu')) @@ -79,7 +86,7 @@ class ClubShangpinLeixingDisableView(APIView): kefu, permissions = verify_kefu_permission(request, phone) if kefu is None: return permissions - if '8080a' not in permissions: + if not _has_club_leixing_perm(permissions): return Response({'code': 403, 'msg': '无权限'}) from jituan.services.club_shangpin_leixing import disable_leixing_for_club data, err = disable_leixing_for_club(request, int(leixing_id))