完成后台多俱乐部数据隔离:订单/会员/财务/板块,并添加星之界初始化命令
This commit is contained in:
41
jituan/management/commands/seed_club_xzj.py
Normal file
41
jituan/management/commands/seed_club_xzj.py
Normal file
@@ -0,0 +1,41 @@
|
||||
"""一键初始化星之界(xzj)俱乐部及客服数据范围。"""
|
||||
from django.core.management import call_command
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
|
||||
from jituan.models import Club
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = '创建星之界电竞俱乐部(xzj)并分配客服数据范围(需先 seed_club_xq)'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--wx-appid', default='wxdefa454152e78a03', help='星之界小程序 appid',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--skip-assignments', action='store_true', help='跳过客服 admin_assignment',
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
wx_appid = (options.get('wx_appid') or '').strip()
|
||||
club_id = 'xzj'
|
||||
name = '星之界电竞'
|
||||
|
||||
if Club.query.filter(club_id=club_id).exists():
|
||||
self.stdout.write(self.style.WARNING(f'俱乐部 {club_id} 已存在,跳过 create_club'))
|
||||
else:
|
||||
self.stdout.write(f'创建俱乐部 {club_id} ...')
|
||||
call_command(
|
||||
'create_club',
|
||||
club_id,
|
||||
name=name,
|
||||
wx_appid=wx_appid,
|
||||
)
|
||||
|
||||
if not options.get('skip_assignments'):
|
||||
self.stdout.write('分配客服数据范围 ...')
|
||||
call_command('seed_admin_assignments', club_id=club_id)
|
||||
|
||||
self.stdout.write(self.style.SUCCESS(
|
||||
f'星之界 {club_id} 就绪。请在后台「俱乐部配置」检查支付密钥,并在 xzj 视图下单独改轮播/会员价。'
|
||||
))
|
||||
@@ -3,6 +3,7 @@ from datetime import date, datetime, timedelta
|
||||
|
||||
from django.db.models import Sum
|
||||
|
||||
from jituan.constants import DATA_SCOPE_ALL
|
||||
from jituan.services.club_context import (
|
||||
filter_club_char_field,
|
||||
filter_queryset_by_club,
|
||||
|
||||
144
jituan/services/club_member_admin.py
Normal file
144
jituan/services/club_member_admin.py
Normal file
@@ -0,0 +1,144 @@
|
||||
"""后台会员管理:全局 Huiyuan 定义 + 各俱乐部 ClubHuiyuanPrice 售价/分成。"""
|
||||
import random
|
||||
import string
|
||||
from decimal import Decimal
|
||||
|
||||
from django.db import transaction
|
||||
|
||||
from jituan.constants import CLUB_ID_DEFAULT, DATA_SCOPE_ALL
|
||||
from jituan.models import ClubHuiyuanPrice
|
||||
from jituan.services.club_context import club_id_for_write, resolve_club_id_from_request, resolve_club_scope
|
||||
from jituan.services.club_user_access import list_response_meta
|
||||
from products.models import Huiyuan
|
||||
|
||||
|
||||
def _format_member(m, price_row=None):
|
||||
item = {
|
||||
'huiyuan_id': m.huiyuan_id,
|
||||
'jieshao': m.jieshao,
|
||||
'jtjieshao': m.jtjieshao,
|
||||
'jiage': str(m.jiage),
|
||||
'guanshifc': str(m.guanshifc),
|
||||
'zuzhangfc': str(m.zuzhangfc),
|
||||
'goumai_cishu': m.goumai_cishu,
|
||||
'CreateTime': m.CreateTime.isoformat() if m.CreateTime else None,
|
||||
'UpdateTime': m.UpdateTime.isoformat() if m.UpdateTime else None,
|
||||
'bankuai_id': m.bankuai_id,
|
||||
'club_enabled': True,
|
||||
}
|
||||
if price_row is not None:
|
||||
item['jiage'] = str(price_row.jiage)
|
||||
item['guanshifc'] = str(price_row.guanshifc)
|
||||
item['zuzhangfc'] = str(price_row.zuzhangfc)
|
||||
item['club_enabled'] = bool(price_row.is_enabled)
|
||||
return item
|
||||
|
||||
|
||||
def build_member_list_payload(request):
|
||||
scope = resolve_club_scope(request)
|
||||
club_id = resolve_club_id_from_request(request)
|
||||
members = Huiyuan.query.all().order_by('-CreateTime')
|
||||
member_list = []
|
||||
|
||||
if scope == DATA_SCOPE_ALL:
|
||||
for m in members:
|
||||
member_list.append(_format_member(m))
|
||||
else:
|
||||
price_map = {
|
||||
r.huiyuan_id: r
|
||||
for r in ClubHuiyuanPrice.query.filter(club_id=club_id)
|
||||
}
|
||||
for m in members:
|
||||
row = price_map.get(m.huiyuan_id)
|
||||
if row is None and club_id != CLUB_ID_DEFAULT:
|
||||
continue
|
||||
if row is not None and not row.is_enabled:
|
||||
continue
|
||||
member_list.append(_format_member(m, row))
|
||||
|
||||
return {
|
||||
'list': member_list,
|
||||
'club_id': club_id,
|
||||
'scope': scope,
|
||||
**list_response_meta(request),
|
||||
}
|
||||
|
||||
|
||||
def update_member_for_club(request, huiyuan_id, jieshao, jiage, guanshifc, zuzhangfc, jtjieshao):
|
||||
if resolve_club_scope(request) == DATA_SCOPE_ALL:
|
||||
return None, '集团汇总视图下不可修改,请切换到具体俱乐部'
|
||||
|
||||
club_id = club_id_for_write(request)
|
||||
|
||||
with transaction.atomic():
|
||||
try:
|
||||
member = Huiyuan.objects.select_for_update().get(huiyuan_id=huiyuan_id)
|
||||
except Huiyuan.DoesNotExist:
|
||||
return None, '会员不存在'
|
||||
|
||||
member.jieshao = jieshao
|
||||
member.jtjieshao = jtjieshao
|
||||
member.save(update_fields=['jieshao', 'jtjieshao', 'UpdateTime'])
|
||||
|
||||
row, created = ClubHuiyuanPrice.query.get_or_create(
|
||||
club_id=club_id,
|
||||
huiyuan_id=huiyuan_id,
|
||||
defaults={
|
||||
'jiage': jiage,
|
||||
'guanshifc': guanshifc,
|
||||
'zuzhangfc': zuzhangfc,
|
||||
'is_enabled': True,
|
||||
'bankuai_id': member.bankuai_id,
|
||||
},
|
||||
)
|
||||
if not created:
|
||||
row.jiage = jiage
|
||||
row.guanshifc = guanshifc
|
||||
row.zuzhangfc = zuzhangfc
|
||||
row.is_enabled = True
|
||||
row.save()
|
||||
|
||||
if club_id == CLUB_ID_DEFAULT:
|
||||
member.jiage = jiage
|
||||
member.guanshifc = guanshifc
|
||||
member.zuzhangfc = zuzhangfc
|
||||
member.save(update_fields=['jiage', 'guanshifc', 'zuzhangfc', 'UpdateTime'])
|
||||
|
||||
return {'huiyuan_id': huiyuan_id}, None
|
||||
|
||||
|
||||
def add_member_for_club(request, jieshao, jiage, guanshifc, zuzhangfc, jtjieshao, bankuai_id=None):
|
||||
if resolve_club_scope(request) == DATA_SCOPE_ALL:
|
||||
return None, '集团汇总视图下不可添加,请切换到具体俱乐部'
|
||||
|
||||
club_id = club_id_for_write(request)
|
||||
|
||||
def generate_huiyuan_id():
|
||||
while True:
|
||||
new_id = ''.join(random.choices(string.ascii_uppercase + string.digits, k=6))
|
||||
if not Huiyuan.query.filter(huiyuan_id=new_id).exists():
|
||||
return new_id
|
||||
|
||||
with transaction.atomic():
|
||||
new_id = generate_huiyuan_id()
|
||||
member = Huiyuan.query.create(
|
||||
huiyuan_id=new_id,
|
||||
jieshao=jieshao,
|
||||
jiage=jiage,
|
||||
guanshifc=guanshifc,
|
||||
zuzhangfc=zuzhangfc,
|
||||
jtjieshao=jtjieshao,
|
||||
goumai_cishu=0,
|
||||
bankuai_id=bankuai_id,
|
||||
)
|
||||
ClubHuiyuanPrice.query.create(
|
||||
club_id=club_id,
|
||||
huiyuan_id=new_id,
|
||||
jiage=jiage,
|
||||
guanshifc=guanshifc,
|
||||
zuzhangfc=zuzhangfc,
|
||||
is_enabled=True,
|
||||
bankuai_id=bankuai_id,
|
||||
)
|
||||
|
||||
return {'huiyuan_id': member.huiyuan_id}, None
|
||||
@@ -1,7 +1,7 @@
|
||||
"""后台按俱乐部校验用户是否在当前数据范围内。"""
|
||||
"""后台按俱乐部校验用户/订单是否在当前数据范围内。"""
|
||||
from rest_framework.response import Response
|
||||
|
||||
from jituan.constants import DATA_SCOPE_ALL
|
||||
from jituan.constants import CLUB_ID_DEFAULT, DATA_SCOPE_ALL
|
||||
from jituan.services.club_context import resolve_club_id_from_request, resolve_club_scope
|
||||
|
||||
|
||||
@@ -25,3 +25,19 @@ def list_response_meta(request):
|
||||
'club_id': resolve_club_id_from_request(request),
|
||||
'scope': resolve_club_scope(request),
|
||||
}
|
||||
|
||||
|
||||
def order_belongs_to_request(order, request):
|
||||
if resolve_club_scope(request) == DATA_SCOPE_ALL:
|
||||
return True
|
||||
club_id = resolve_club_id_from_request(request)
|
||||
order_club = (getattr(order, 'ClubID', None) or '').strip()
|
||||
if club_id == CLUB_ID_DEFAULT:
|
||||
return order_club in (club_id, '', None) or not order_club
|
||||
return order_club == club_id
|
||||
|
||||
|
||||
def forbid_if_order_out_of_scope(request, order):
|
||||
if order_belongs_to_request(order, request):
|
||||
return None
|
||||
return Response({'code': 403, 'msg': '该订单不属于当前俱乐部数据范围'})
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
from django.urls import path
|
||||
|
||||
from backend.view import (
|
||||
AddMemberView,
|
||||
FaKuanChuLiView,
|
||||
FaKuanChuangJianView,
|
||||
FaKuanLieBiaoView,
|
||||
FaKuanTongJiView,
|
||||
GetGuanliListView,
|
||||
GetMemberListView,
|
||||
GetOperationLogListView,
|
||||
GetWithdrawSettingsView,
|
||||
GetZuzhangListView,
|
||||
@@ -13,6 +15,7 @@ from backend.view import (
|
||||
KefuGetShangjiaListView,
|
||||
PopupNoticeListAPIView,
|
||||
PopupNoticeModifyAPIView,
|
||||
UpdateMemberView,
|
||||
UpdateWithdrawSettingsView,
|
||||
)
|
||||
from users.views import KefuPunishmentListView
|
||||
@@ -70,5 +73,8 @@ urlpatterns = [
|
||||
path('houtai/glyclfk', FaKuanChuLiView.as_view(), name='jituan_penalty_action'),
|
||||
path('houtai/htfksc', FaKuanChuangJianView.as_view(), name='jituan_penalty_create'),
|
||||
path('houtai/kefu-cfgl', KefuPunishmentListView.as_view(), name='jituan_kefu_cfgl'),
|
||||
path('houtai/hthqhylb', GetMemberListView.as_view(), name='jituan_member_list'),
|
||||
path('houtai/htxghyxx', UpdateMemberView.as_view(), name='jituan_member_update'),
|
||||
path('houtai/httjhy', AddMemberView.as_view(), name='jituan_member_add'),
|
||||
path('club/list', ClubListView.as_view(), name='jituan_club_list'),
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user