feat: 只看审核按俱乐部隔离,商品管理支持俱乐部视图

未绑店出货开关按 club_id 配置;后台商品列表可在具体小程序下管理并切换审核展示。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
XingQue
2026-07-21 03:20:03 +08:00
parent ed627fa6f7
commit 6515568ec0
9 changed files with 249 additions and 42 deletions

View File

@@ -5,7 +5,7 @@ from .view import (GetRolePermissionView,
ModifyRolePermissionView, AddRoleView, GetAdminRolesView, GetAdminUserListView, AddAdminUserView,
ModifyAdminUserView, KefuGetDashouListView, KefuGetDashouDetailView, KefuUpdateDashouView, KefuGetShangjiaListView,
KefuGetShangjiaDetailView, KefuUpdateShangjiaView, GetProductBaseDataView, GetProductListView, SaveProductOrderView,
AddProductView, DeleteProductView, GetProductDetailView, UpdateProductView, UpdateMemberView, GetMemberListView,
AddProductView, DeleteProductView, GetProductDetailView, UpdateProductView, UpdateZhiKanShenheView, UpdateMemberView, GetMemberListView,
AddMemberView, GetGuanliListView, GetGuanliDetailView, PromoteGuanshiToZuzhangView, ChangeInviterView, UpdateGuanliView, GetWithdrawSettingsView,
UpdateWithdrawSettingsView, GetZuzhangListView, GetZuzhangDetailView, UpdateZuzhangView, GetOperationLogListView, GetProductTypeZoneView,
ModifyProductTypeZoneView, GetRateView, ModifyRateView, PopupNoticeListAPIView, PopupNoticeModifyAPIView,ShopListView,
@@ -44,6 +44,7 @@ urlpatterns = [
path('htscsp', DeleteProductView.as_view(), name='后台删除商品'),
path('htsphqxq', GetProductDetailView.as_view(), name='后台获取商品详情'),
path('bcspxg', UpdateProductView.as_view(), name='后台更新商品数据'),
path('htxgzhikan', UpdateZhiKanShenheView.as_view(), name='后台更新只看审核开关'),
path('hthqhylb', GetMemberListView.as_view(), name='后台获取会员列表'),
path('htxghyxx', UpdateMemberView.as_view(), name='后台更新会员信息'),

View File

@@ -41,6 +41,7 @@ from .views.kefu import (
from .views.products import (
GetProductBaseDataView, GetProductListView, SaveProductOrderView,
AddProductView, DeleteProductView, GetProductDetailView, UpdateProductView,
UpdateZhiKanShenheView,
)
from .views.members import (
GetMemberListView, UpdateMemberView, AddMemberView,
@@ -103,6 +104,7 @@ __all__ = [
# products
'GetProductBaseDataView', 'GetProductListView', 'SaveProductOrderView',
'AddProductView', 'DeleteProductView', 'GetProductDetailView', 'UpdateProductView',
'UpdateZhiKanShenheView',
# members
'GetMemberListView', 'UpdateMemberView', 'AddMemberView',
'ClubMemberCatalogView', 'ClubMemberEnableView', 'ClubMemberDisableView',

View File

@@ -142,14 +142,24 @@ class GetProductBaseDataView(APIView):
if '2200a' not in permissions:
return Response({'code': 403, 'msg': '无权限管理商品'})
# 获取所有商品类型(按 paixu 升序)
# 获取所有商品类型(按 paixu 升序);商品数按当前俱乐部统计
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
# 批量聚合商品数量,避免循环内 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 Shangpin.query.filter(
dianpu__isnull=True, dianpu_leixing__isnull=True, leixing_id__isnull=False
).values('leixing_id').annotate(cnt=Count('id'))
for item in _public_sp.values('leixing_id').annotate(cnt=Count('id'))
}
type_list = []
for t in type_qs:
@@ -164,7 +174,6 @@ class GetProductBaseDataView(APIView):
})
# 获取专区(按俱乐部隔离;集团汇总看全部)
from jituan.services.club_context import filter_club_char_field
zone_qs = filter_club_char_field(
ShangpinZhuanqu.query.all().order_by('paixu'), request, field='club_id'
)
@@ -194,12 +203,24 @@ class GetProductBaseDataView(APIView):
# 获取所有会员(只需 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,
}
})
@@ -701,6 +722,14 @@ class AddProductView(APIView):
zhuanqu_obj = ShangpinZhuanqu.query.get(id=zhuanqu_id)
if zhuanqu_obj.leixing_id != leixing_id:
return Response({'code': 400, 'msg': '专区不属于该商品类型'})
from jituan.constants import DATA_SCOPE_ALL
from jituan.services.club_context import resolve_club_scope
from jituan.services.club_write import resolve_club_id_for_write
if resolve_club_scope(request) != DATA_SCOPE_ALL:
write_club = resolve_club_id_for_write(request)
zone_club = getattr(zhuanqu_obj, 'club_id', None) or 'xq'
if zone_club != write_club:
return Response({'code': 400, 'msg': '专区不属于当前俱乐部,请先切换俱乐部或选择本俱乐部专区'})
except ShangpinZhuanqu.DoesNotExist:
return Response({'code': 400, 'msg': '商品专区不存在'})
@@ -1134,6 +1163,52 @@ class UpdateProductView(APIView):
return Response({'code': 0, 'msg': '商品修改成功', 'data': {'id': product.id}})
class UpdateZhiKanShenheView(APIView):
"""
商品管理页:按俱乐部更新「只看审核商品」开关。
权限2200a
POST /houtai/htxgzhikan
"""
permission_classes = []
parser_classes = [JSONParser]
def post(self, request):
username = request.data.get('username', '').strip()
if not username:
return Response({'code': 401, 'msg': '缺少username'})
kefu, permissions = verify_kefu_permission(request, username)
if kefu is None:
return permissions
if '2200a' not in permissions:
return Response({'code': 403, 'msg': '无权限管理商品'})
val = request.data.get('zhi_kan_shenhe')
if val is None:
return Response({'code': 400, 'msg': '缺少参数 zhi_kan_shenhe'})
from jituan.constants import DATA_SCOPE_ALL
from jituan.services.club_context import resolve_club_scope
from jituan.services.club_write import resolve_club_id_for_write
from shop.services.shenhe_config import get_or_create_shenhe_config
if resolve_club_scope(request) == DATA_SCOPE_ALL:
return Response({'code': 400, 'msg': '请先切换到具体俱乐部再修改只看审核开关'})
try:
with transaction.atomic():
club_id = resolve_club_id_for_write(request)
config = get_or_create_shenhe_config(club_id)
config = DianpuShangpinShenheShezhi.objects.select_for_update().get(pk=config.pk)
config.zhi_kan_shenhe = bool(val)
config.save(update_fields=['zhi_kan_shenhe'])
return Response({
'code': 0,
'msg': '只看审核开关已更新',
'data': {'club_id': club_id, 'zhi_kan_shenhe': bool(val)},
})
except Exception:
logging.getLogger(__name__).exception('更新只看审核开关失败')
return Response({'code': 500, 'msg': '服务器错误'})

View File

@@ -421,17 +421,38 @@ class ShopPublicTypeAndAuditView(APIView):
logger.exception("查询公共商品类型失败")
return Response({'code': 500, 'msg': '服务器错误,请稍后重试'})
# ---------- 获取审核模式配置 ----------
audit_config = DianpuShangpinShenheShezhi.query.filter(id=1).first()
kaiqi_shenhe = audit_config.kaiqi_shenhe if audit_config else False
zhi_kan_shenhe = getattr(audit_config, 'zhi_kan_shenhe', True) if audit_config else True
# ---------- 获取本俱乐部审核模式配置 ----------
from jituan.services.club_write import resolve_club_id_for_write
from shop.services.shenhe_config import get_or_create_shenhe_config
from jituan.constants import DATA_SCOPE_ALL
from jituan.services.club_context import resolve_club_scope
if resolve_club_scope(request) == DATA_SCOPE_ALL:
# 集团汇总:不自动创建,返回默认值提示前端切到具体俱乐部
return Response({
'code': 0,
'data': {
'public_types': type_list,
'kaiqi_shenhe': False,
'zhi_kan_shenhe': True,
'club_scoped': False,
'msg': '请切换到具体俱乐部查看/修改审核展示开关',
}
})
club_id = resolve_club_id_for_write(request)
audit_config = get_or_create_shenhe_config(club_id)
kaiqi_shenhe = audit_config.kaiqi_shenhe
zhi_kan_shenhe = bool(getattr(audit_config, 'zhi_kan_shenhe', True))
return Response({
'code': 0,
'data': {
'public_types': type_list,
'kaiqi_shenhe': kaiqi_shenhe,
'zhi_kan_shenhe': bool(zhi_kan_shenhe),
'zhi_kan_shenhe': zhi_kan_shenhe,
'club_id': club_id,
'club_scoped': True,
}
})
@@ -640,18 +661,28 @@ class ShopProductModifyView(APIView):
if kaiqi is None:
return Response({'code': 400, 'msg': '缺少参数 kaiqi_shenhe'})
from jituan.constants import DATA_SCOPE_ALL
from jituan.services.club_context import resolve_club_scope
from jituan.services.club_write import resolve_club_id_for_write
from shop.services.shenhe_config import get_or_create_shenhe_config
if resolve_club_scope(request) == DATA_SCOPE_ALL:
return Response({'code': 400, 'msg': '请先切换到具体俱乐部再修改审核模式'})
try:
config, _ = DianpuShangpinShenheShezhi.objects.select_for_update().get_or_create(id=1)
club_id = resolve_club_id_for_write(request)
config = get_or_create_shenhe_config(club_id)
config = DianpuShangpinShenheShezhi.objects.select_for_update().get(pk=config.pk)
config.kaiqi_shenhe = bool(kaiqi)
config.save(update_fields=['kaiqi_shenhe'])
return Response({'code': 0, 'msg': '审核模式已更新'})
return Response({'code': 0, 'msg': '审核模式已更新', 'data': {'club_id': club_id}})
except Exception as e:
logger.exception("更新审核模式失败")
return Response({'code': 500, 'msg': '服务器错误'})
@transaction.atomic
def _update_zhi_kan_shenhe(self, request, user_perms):
"""点单端:未绑店用户是否按身份只看审核专用商品。"""
"""点单端:未绑店用户是否按身份只看审核专用商品(按俱乐部)"""
if not has_perm_code(user_perms, '1199ab'):
return Response({'code': 403, 'msg': '无权限修改只看审核开关'})
@@ -659,11 +690,21 @@ class ShopProductModifyView(APIView):
if val is None:
return Response({'code': 400, 'msg': '缺少参数 zhi_kan_shenhe'})
from jituan.constants import DATA_SCOPE_ALL
from jituan.services.club_context import resolve_club_scope
from jituan.services.club_write import resolve_club_id_for_write
from shop.services.shenhe_config import get_or_create_shenhe_config
if resolve_club_scope(request) == DATA_SCOPE_ALL:
return Response({'code': 400, 'msg': '请先切换到具体俱乐部再修改只看审核开关'})
try:
config, _ = DianpuShangpinShenheShezhi.objects.select_for_update().get_or_create(id=1)
club_id = resolve_club_id_for_write(request)
config = get_or_create_shenhe_config(club_id)
config = DianpuShangpinShenheShezhi.objects.select_for_update().get(pk=config.pk)
config.zhi_kan_shenhe = bool(val)
config.save(update_fields=['zhi_kan_shenhe'])
return Response({'code': 0, 'msg': '只看审核开关已更新'})
return Response({'code': 0, 'msg': '只看审核开关已更新', 'data': {'club_id': club_id}})
except Exception:
logger.exception("更新只看审核开关失败")
return Response({'code': 500, 'msg': '服务器错误'})