fix: 商品基础数据容错避免500;类型专区按俱乐部上架并管专区

具体俱乐部只返回已上架类型;上架/下架允许 7007a 或 8080a。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
XingQue
2026-07-21 03:33:49 +08:00
parent f1db185d03
commit 98b58f8058
3 changed files with 178 additions and 135 deletions

View File

@@ -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': '获取商品基础数据失败,请稍后重试'})

View File

@@ -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': '获取类型专区失败,请稍后重试'})

View File

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