Files
Django/jituan/services/club_member_admin.py

322 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""后台会员管理:全局 Huiyuan 定义 + 各俱乐部 ClubHuiyuanPrice 售价/分成/体验配置。"""
import logging
import random
import string
from decimal import Decimal
from django.db import transaction, IntegrityError
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
logger = logging.getLogger(__name__)
CLUB_WRITE_SCOPE_MSG = '当前为集团汇总视图,请先在顶栏切换到具体俱乐部后再操作'
def club_write_blocked(request):
return resolve_club_scope(request) == DATA_SCOPE_ALL
def _price_row_to_dict(row):
if not row:
return {}
return {
'formal_days': int(row.formal_days or 30),
'trial_enabled': bool(row.trial_enabled),
'trial_price': str(row.trial_price or 0),
'trial_days': int(row.trial_days or 0),
'trial_guanshifc': str(row.trial_guanshifc or 0),
'trial_zuzhangfc': str(row.trial_zuzhangfc or 0),
}
def _format_member(m, price_row=None):
global_jiage = str(m.jiage)
global_guanshifc = str(m.guanshifc)
global_zuzhangfc = str(m.zuzhangfc)
item = {
'huiyuan_id': m.huiyuan_id,
'jieshao': m.jieshao,
'jtjieshao': m.jtjieshao,
'jiage': global_jiage,
'guanshifc': global_guanshifc,
'zuzhangfc': global_zuzhangfc,
'global_jiage': global_jiage,
'global_guanshifc': global_guanshifc,
'global_zuzhangfc': global_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,
'price_source': 'global',
'formal_days': 30,
'trial_enabled': False,
'trial_price': '0.00',
'trial_days': 0,
'trial_guanshifc': '0.00',
'trial_zuzhangfc': '0.00',
}
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)
item['price_source'] = 'club'
item.update(_price_row_to_dict(price_row))
return item
def count_club_member_sales(club_id, huiyuan_id, formal_only=True):
"""本俱乐部已支付会员单数(用于后台展示,非全局 goumai_cishu"""
from products.models import Czjilu
qs = Czjilu.query.filter(
club_id=club_id,
huiyuan_id=huiyuan_id,
leixing=1,
zhuangtai=3,
)
if formal_only:
qs = qs.filter(is_trial=False)
return qs.count()
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 = []
price_map = {}
if scope != DATA_SCOPE_ALL:
price_map = {
r.huiyuan_id: r
for r in ClubHuiyuanPrice.query.filter(club_id=club_id)
}
if scope == DATA_SCOPE_ALL:
for m in members:
member_list.append(_format_member(m))
else:
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
item = _format_member(m, row)
item['goumai_cishu'] = count_club_member_sales(club_id, m.huiyuan_id)
member_list.append(item)
return {
'list': member_list,
'club_id': club_id,
'scope': scope,
'price_note': (
f'当前俱乐部 {club_id} 专属售价club_huiyuan_price'
if scope != DATA_SCOPE_ALL else
'集团汇总:展示全局默认价;切换具体俱乐部可配置售价与体验会员。'
),
**list_response_meta(request),
}
def build_member_catalog_payload(request):
"""集团会员类型 + 本俱乐部未上架的可选列表。"""
club_id = resolve_club_id_from_request(request)
enabled_ids = set(
ClubHuiyuanPrice.query.filter(club_id=club_id, is_enabled=True).values_list('huiyuan_id', flat=True)
)
all_members = Huiyuan.query.all().order_by('-CreateTime')
catalog = []
for m in all_members:
if m.huiyuan_id in enabled_ids:
continue
catalog.append({
'huiyuan_id': m.huiyuan_id,
'jieshao': m.jieshao,
'jtjieshao': m.jtjieshao,
})
return {'club_id': club_id, 'catalog': catalog}
def _parse_trial_fields(data, jiage, guanshifc, zuzhangfc):
formal_days = int(data.get('formal_days') or 30)
trial_enabled = bool(data.get('trial_enabled'))
trial_price = Decimal(str(data.get('trial_price') or 0))
trial_days = int(data.get('trial_days') or 0)
trial_guanshifc = Decimal(str(data.get('trial_guanshifc') or 0))
trial_zuzhangfc = Decimal(str(data.get('trial_zuzhangfc') or 0))
if formal_days < 1 or formal_days > 3650:
raise ValueError('正式会员天数无效')
if trial_enabled:
if trial_price <= 0 or trial_days <= 0:
raise ValueError('开启体验会员须配置体验价格与天数')
if trial_guanshifc + trial_zuzhangfc > trial_price:
raise ValueError('体验管事+组长分成不能大于体验价格')
if guanshifc + zuzhangfc > jiage:
raise ValueError('管事分成+组长分成不能大于会员价格')
return {
'formal_days': formal_days,
'trial_enabled': trial_enabled,
'trial_price': trial_price,
'trial_days': trial_days,
'trial_guanshifc': trial_guanshifc,
'trial_zuzhangfc': trial_zuzhangfc,
}
def update_member_for_club(request, huiyuan_id, jieshao, jiage, guanshifc, zuzhangfc, jtjieshao, extra=None):
if club_write_blocked(request):
return None, CLUB_WRITE_SCOPE_MSG
club_id = club_id_for_write(request)
extra = extra or {}
try:
trial_fields = _parse_trial_fields(extra, jiage, guanshifc, zuzhangfc)
except ValueError as e:
return None, str(e)
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,
**trial_fields,
},
)
if not created:
row.jiage = jiage
row.guanshifc = guanshifc
row.zuzhangfc = zuzhangfc
row.is_enabled = True
for k, v in trial_fields.items():
setattr(row, k, v)
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 enable_member_for_club(request, huiyuan_id, jiage, guanshifc, zuzhangfc, extra=None):
"""俱乐部上架集团会员类型。"""
if club_write_blocked(request):
return None, CLUB_WRITE_SCOPE_MSG
club_id = club_id_for_write(request)
extra = extra or {}
try:
member = Huiyuan.query.get(huiyuan_id=huiyuan_id)
except Huiyuan.DoesNotExist:
return None, '集团会员类型不存在'
try:
jiage = Decimal(str(jiage))
guanshifc = Decimal(str(guanshifc))
zuzhangfc = Decimal(str(zuzhangfc))
if jiage <= 0:
raise ValueError
trial_fields = _parse_trial_fields(extra, jiage, guanshifc, zuzhangfc)
except ValueError as e:
return None, str(e) if str(e) else '金额或体验配置错误'
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,
**trial_fields,
},
)
if not created:
row.jiage = jiage
row.guanshifc = guanshifc
row.zuzhangfc = zuzhangfc
row.is_enabled = True
for k, v in trial_fields.items():
setattr(row, k, v)
row.save()
return {'huiyuan_id': huiyuan_id}, None
def disable_member_for_club(request, huiyuan_id):
"""俱乐部软下架。"""
if club_write_blocked(request):
return None, CLUB_WRITE_SCOPE_MSG
club_id = club_id_for_write(request)
updated = ClubHuiyuanPrice.objects.filter(
club_id=club_id, huiyuan_id=huiyuan_id, is_enabled=True,
).update(is_enabled=False)
if not updated:
return None, '该会员在本俱乐部未上架或已下架'
return {'huiyuan_id': huiyuan_id}, None
def add_global_member(jieshao, jiage, guanshifc, zuzhangfc, jtjieshao, bankuai_id=None):
"""集团创建全局会员类型(仅集团后台)。"""
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
try:
with transaction.atomic():
new_id = generate_huiyuan_id()
create_kwargs = dict(
huiyuan_id=new_id,
jieshao=jieshao,
jiage=jiage,
guanshifc=guanshifc,
zuzhangfc=zuzhangfc,
jtjieshao=jtjieshao or '',
goumai_cishu=0,
)
if bankuai_id is not None:
create_kwargs['bankuai_id'] = bankuai_id
member = Huiyuan.query.create(**create_kwargs)
except IntegrityError as e:
logger.exception('创建全局会员失败')
return None, f'创建失败:{e}'
except Exception as e:
logger.exception('创建全局会员失败')
return None, f'创建失败:{e}'
return {'huiyuan_id': member.huiyuan_id}, None
# 兼容旧名:俱乐部不应再调用
def add_member_for_club(request, jieshao, jiage, guanshifc, zuzhangfc, jtjieshao, bankuai_id=None):
if club_write_blocked(request):
return None, CLUB_WRITE_SCOPE_MSG
return None, '俱乐部不可新建会员类型,请从集团会员 catalog 上架'