fix: 俱乐部ID统一解析;会员支付/分红/展示按club隔离

This commit is contained in:
XingQue
2026-06-25 01:37:58 +08:00
parent cca53bbc96
commit 0c4b28ea45
5 changed files with 76 additions and 28 deletions

View File

@@ -3290,8 +3290,8 @@ class CheckPhoneAuthView(APIView):
return False
if dashou.yajin and dashou.yajin > 0:
return True
from jituan.services.club_context import resolve_club_id_from_request
club_id = resolve_club_id_from_request(self.request)
from jituan.services.club_context import resolve_effective_club_id
club_id = resolve_effective_club_id(self.request, user)
if Huiyuangoumai.query.filter(yonghu_id=user.UserUID, club_id=club_id).exists():
return True
if (dashou.jiedanzongliang or 0) > 0 or (dashou.chengjiaozongliang or 0) > 0:

View File

@@ -82,9 +82,29 @@ def filter_queryset_by_club(qs, request, club_field='club_id'):
return qs.filter(**{club_field: club_id})
def club_id_for_write(request):
def resolve_effective_club_id(request, user=None):
"""
小程序/API 实际生效俱乐部(展示价、支付价、分红须一致)。
优先级Header X-Club-Id > 用户归属 ClubID > 默认 xq。
勿用 middleware 注入的 request.club_id无 Header 时恒为 xq
"""
header = request.META.get(f'HTTP_{CLUB_HEADER.upper().replace("-", "_")}', '')
if not header and hasattr(request, 'headers'):
header = request.headers.get(CLUB_HEADER, '')
header = (header or '').strip()
if header:
return header
if user is not None:
from jituan.services.club_user import get_user_club_id
user_club = get_user_club_id(user)
if user_club:
return user_club
return CLUB_ID_DEFAULT
def club_id_for_write(request, user=None):
"""写入业务流水时使用的 club_id集团汇总视图写入仍须指定目标 club"""
return resolve_club_id_from_request(request)
return resolve_effective_club_id(request, user)
def orders_for_request(request):

View File

@@ -12,7 +12,7 @@ def resolve_club_id_for_write(request=None, user=None, club_id=None):
if club_id:
return club_id
if request is not None:
return club_id_for_write(request)
return club_id_for_write(request, user)
if user is not None:
return get_user_club_id(user)
return CLUB_ID_DEFAULT

View File

@@ -1,6 +1,11 @@
"""轮播 / 公告 / 弹窗按俱乐部读取与写入。"""
from jituan.constants import CLUB_ID_DEFAULT, DATA_SCOPE_ALL
from jituan.services.club_context import filter_queryset_by_club, resolve_club_id_from_request, resolve_club_scope
from jituan.services.club_context import (
filter_queryset_by_club,
resolve_club_id_from_request,
resolve_club_scope,
resolve_effective_club_id,
)
from rest_framework.response import Response
# 定稿 B 方案 page_key
@@ -46,16 +51,23 @@ def forbid_popup_page_out_of_scope(request, page):
return None
def _display_club_id(request):
user = getattr(request, 'user', None)
if user is not None and getattr(user, 'is_authenticated', False):
return resolve_effective_club_id(request, user)
return resolve_effective_club_id(request, None)
def get_gonggao_content(request, notice_type=1):
from config.models import Gonggao
club_id = resolve_club_id_from_request(request)
club_id = _display_club_id(request)
obj = Gonggao.query.filter(club_id=club_id, NoticeType=notice_type).first()
return obj.Content if obj and obj.Content else ''
def get_lunbo_urls(request, page_key=None, image_type=1):
from config.models import Lunbo
club_id = resolve_club_id_from_request(request)
club_id = _display_club_id(request)
pk = normalize_page_key(page_key, image_type=image_type)
qs = Lunbo.query.filter(club_id=club_id, ImageType=image_type, page_key=pk)
return [row.ImageURL for row in qs.order_by('id') if row.ImageURL]
@@ -67,13 +79,11 @@ def list_response_meta(request):
def get_tupianpeizhi_url(request, image_type):
"""按俱乐部读图片配置(无则 xq 回退,避免 xzj 未配时全空)。"""
"""按俱乐部读图片配置(各俱乐部独立,不回退 xq避免串台)。"""
from config.models import Tupianpeizhi
club_id = resolve_club_id_from_request(request)
club_id = _display_club_id(request)
row = Tupianpeizhi.query.filter(club_id=club_id, ImageType=image_type).first()
if not row and club_id != CLUB_ID_DEFAULT:
row = Tupianpeizhi.query.filter(club_id=CLUB_ID_DEFAULT, ImageType=image_type).first()
return (row.ImageURL or '').strip() if row else ''

View File

@@ -59,7 +59,7 @@ from jituan.services.club_config import (
get_huiyuan_price,
get_huiyuan_fenchong,
)
from jituan.services.club_context import resolve_club_id_from_request
from jituan.services.club_context import resolve_effective_club_id
from jituan.services.club_write import resolve_club_id_for_write
from jituan.services.club_user import get_payment_openid
from jituan.services.club_penalty import resolve_gsfenhong_club_id
@@ -269,7 +269,7 @@ class DashouHuiyuanList(APIView):
# 3. 仅返回本俱乐部可售会员club_huiyuan_price 已配置且启用)
from jituan.services.member_recharge import club_huiyuan_sellable
club_id = resolve_club_id_from_request(request)
club_id = resolve_effective_club_id(request, request.user)
huiyuan_queryset = Huiyuan.query.all().only(
'huiyuan_id', 'jieshao', 'jtjieshao', 'jiage',
).order_by('jiage')
@@ -290,7 +290,8 @@ class DashouHuiyuanList(APIView):
return Response({
'code': 200,
'message': '获取会员列表成功',
'data': huiyuan_list # 对应前端接收的列表
'data': huiyuan_list,
'club_id': club_id,
}, status=status.HTTP_200_OK)
except Exception as e:
@@ -1411,7 +1412,7 @@ class HuiyuanGoumai(APIView):
'message': '会员信息不存在'
}, status=status.HTTP_400_BAD_REQUEST)
club_id = resolve_club_id_from_request(request)
club_id = resolve_effective_club_id(request, request.user)
from jituan.services.member_recharge import club_huiyuan_sellable
sellable, club_price = club_huiyuan_sellable(club_id, huiyuanid)
if not sellable:
@@ -1444,7 +1445,8 @@ class HuiyuanGoumai(APIView):
)
logger.info(
f"创建会员充值订单: 用户{request.user.UserUID}, 订单{dingdanid}, 会员{huiyuanid}, 金额{jine}")
f"创建会员充值订单: 用户{request.user.UserUID}, 订单{dingdanid}, "
f"会员{huiyuanid}, 俱乐部{club_id}, 金额{jine}")
# 7. 生成微信支付参数
try:
@@ -1471,8 +1473,10 @@ class HuiyuanGoumai(APIView):
return Response({
'code': 200,
'message': '支付参数生成成功',
'payParams': pay_params, # 对应前端payParams
'dingdanid': dingdanid # 对应前端dingdanid
'payParams': pay_params,
'dingdanid': dingdanid,
'club_id': club_id,
'jine': jine,
}, status=status.HTTP_200_OK)
except Exception as e:
@@ -5738,7 +5742,7 @@ class CzhqdyView(APIView):
# 会员
try:
huiyuan = Huiyuan.query.get(huiyuan_id=huiyuan_id)
club_id = resolve_club_id_from_request(request)
club_id = resolve_effective_club_id(request, request.user)
target_price = get_huiyuan_price(club_id, huiyuan_id, huiyuan)
except Huiyuan.DoesNotExist:
return Response({'code': 404, 'msg': '会员不存在'}, status=status.HTTP_404_NOT_FOUND)
@@ -5758,7 +5762,7 @@ class CzhqdyView(APIView):
# 获取所有费率(打手佣金、管事分红、组长分红、打手押金)
rates = {}
club_id = resolve_club_id_from_request(request)
club_id = resolve_effective_club_id(request, request.user)
for key in ['5', '6', '8', '11']:
rates[key] = get_commission_rate(club_id, key)
@@ -5873,7 +5877,7 @@ class DsqrgmdhView(APIView):
return Response({'code': 400, 'msg': 'yajin_jine 必须为正数'}, status=status.HTTP_400_BAD_REQUEST)
target_price = None
club_id = resolve_club_id_from_request(request)
club_id = resolve_effective_club_id(request, request.user)
if leixing == 1:
try:
huiyuan = Huiyuan.query.get(huiyuan_id=huiyuan_id)
@@ -5894,12 +5898,12 @@ class DsqrgmdhView(APIView):
return Response({'code': 404, 'msg': '罚单不存在或不属于当前用户'}, status=status.HTTP_404_NOT_FOUND)
identity = IDENTITY_CONFIG.get(shenfen_id)
from jituan.services.club_config import get_commission_rate
try:
rate_obj = CommissionRate.query.get(Platform=identity['rate_key'])
rate = rate_obj.Rate
rate = get_commission_rate(club_id, identity['rate_key'])
if rate >= 1:
return Response({'code': 400, 'msg': f"{identity['name']}费率异常"}, status=status.HTTP_400_BAD_REQUEST)
except CommissionRate.DoesNotExist:
except Exception:
return Response({'code': 400, 'msg': f"{identity['name']}费率未配置"}, status=status.HTTP_400_BAD_REQUEST)
logger.info(f"目标价格={target_price}, 费率={rate}")
@@ -5968,7 +5972,20 @@ class DsqrgmdhView(APIView):
response_data = {}
if leixing == 1:
huiyuan_record, is_first_buy = self._process_member_purchase(yonghuid, huiyuan_id, huiyuan)
huiyuan_record, is_first_buy = self._process_member_purchase(
yonghuid, huiyuan_id, huiyuan, club_id=club_id,
)
try:
from jituan.services.member_recharge import (
resolve_member_purchase_cishu,
_apply_member_fenhong,
)
purchase_cishu = resolve_member_purchase_cishu(
yonghuid, huiyuan_id, dingdan_id, club_id,
)
_apply_member_fenhong(czjilu, huiyuan, purchase_cishu)
except Exception as exc:
logger.error('余额购会员分红失败 dingdan=%s: %s', dingdan_id, exc, exc_info=True)
response_data['huiyuan'] = {
'huiyuanid': huiyuan_id,
'huiyuanming': huiyuan.jieshao,
@@ -6034,7 +6051,7 @@ class DsqrgmdhView(APIView):
logger.error(f"接口顶层异常: {str(e)}", exc_info=True)
return Response({'code': 500, 'msg': f'系统错误: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
def _process_member_purchase(self, yonghuid, huiyuan_id, huiyuan_obj):
def _process_member_purchase(self, yonghuid, huiyuan_id, huiyuan_obj, club_id=None):
is_first_buy_any = False
try:
record = Huiyuangoumai.query.filter(
@@ -6050,13 +6067,14 @@ class DsqrgmdhView(APIView):
other_count = Huiyuangoumai.query.filter(yonghu_id=yonghuid).exclude(huiyuan_id=huiyuan_id).count()
if other_count == 0:
is_first_buy_any = True
resolved_club = club_id or get_user_club_id(User.query.filter(UserUID=yonghuid).first())
record = Huiyuangoumai.query.create(
yonghu_id=yonghuid,
huiyuan_id=huiyuan_id,
jieshao=huiyuan_obj.jieshao,
huiyuan_zhuangtai=1,
daoqi_time=timezone.now() + timedelta(days=30),
club_id=get_user_club_id(User.query.filter(UserUID=yonghuid).first()),
club_id=resolved_club,
)
return record, is_first_buy_any
except Exception as e: