feat: 会员体验版、俱乐部分离上架与分红计数加固

扩展 club_huiyuan_price/huiyuangoumai/czjilu/gsfenhong 字段;微信购会员强制写 czjilu+收支入账;formal_cishu 仅统计正式已支付单;关闭余额购会员;俱乐部上架/下架 API。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
XingQue
2026-06-26 01:32:33 +08:00
parent deb1688d0c
commit 2f0838e552
11 changed files with 689 additions and 221 deletions

View File

@@ -1276,28 +1276,20 @@ class KefuUpdateDashouView(APIView):
return Response({'code': 403, 'msg': '无权限给打手加会员'})
if not huiyuan_id:
return Response({'code': 400, 'msg': '缺少会员ID'})
days = int(value) if value else 30
try:
huiyuan = Huiyuan.query.get(huiyuan_id=huiyuan_id)
except Huiyuan.DoesNotExist:
return Response({'code': 404, 'msg': '会员类型不存在'})
from jituan.services.club_user import get_user_club_id
from jituan.services.member_recharge import extend_member_entitlement_admin, get_club_huiyuan_row
player_club = get_user_club_id(dashou_user)
record, created = Huiyuangoumai.objects.select_for_update().get_or_create(
yonghu_id=dashou_id,
huiyuan_id=huiyuan_id,
defaults={
'huiyuan_zhuangtai': 1,
'jieshao': huiyuan.jieshao,
'daoqi_time': timezone.now() + timedelta(days=days),
'club_id': player_club,
}
row = get_club_huiyuan_row(player_club, huiyuan_id, require_enabled=False)
days = int(value) if value else int(row.formal_days or 30) if row else 30
record, err = extend_member_entitlement_admin(
dashou_id, huiyuan_id, days, player_club,
)
if not created:
new_daoqi = record.daoqi_time + timedelta(days=days)
record.daoqi_time = new_daoqi
record.huiyuan_zhuangtai = 1
record.save()
if err:
return Response({'code': 400, 'msg': err})
write_xiugai_log(
yonghuid=dashou_id,
xiugaiid=kefu.user.Phone,
@@ -1305,9 +1297,9 @@ class KefuUpdateDashouView(APIView):
huiyuants=days,
huiyuan_id=huiyuan_id,
qitashuoming=(
f'【后台】给打手添加会员会员ID={huiyuan_id}{huiyuan.jieshao}'
f'【后台】给打手添加正式会员会员ID={huiyuan_id}{huiyuan.jieshao}'
f'增加{days}天,到期时间={record.daoqi_time.strftime("%Y-%m-%d %H:%M:%S")}'
f'打手ID={dashou_id},操作人={kefu.user.Phone}'
f'打手ID={dashou_id},操作人={kefu.user.Phone}(无支付单、无分红)'
),
)
return Response({'code': 0, 'msg': '加会员成功', 'data': {'new_daoqi': record.daoqi_time.strftime('%Y-%m-%d %H:%M:%S')}})
@@ -2827,8 +2819,16 @@ class UpdateMemberView(APIView):
from jituan.services.club_member_admin import update_member_for_club
extra = {
'formal_days': request.data.get('formal_days'),
'trial_enabled': request.data.get('trial_enabled'),
'trial_price': request.data.get('trial_price'),
'trial_days': request.data.get('trial_days'),
'trial_guanshifc': request.data.get('trial_guanshifc'),
'trial_zuzhangfc': request.data.get('trial_zuzhangfc'),
}
data, err = update_member_for_club(
request, huiyuan_id, jieshao, jiage, guanshifc, zuzhangfc, jtjieshao,
request, huiyuan_id, jieshao, jiage, guanshifc, zuzhangfc, jtjieshao, extra=extra,
)
if err:
return Response({'code': 400, 'msg': err})
@@ -2880,11 +2880,17 @@ class AddMemberView(APIView):
if '3300a' not in permissions:
return Response({'code': 403, 'msg': '无权限添加会员'})
from jituan.services.club_member_admin import add_member_for_club
from jituan.services.club_member_admin import add_global_member
from jituan.services.club_context import resolve_club_scope
from jituan.constants import DATA_SCOPE_ALL
data, err = add_member_for_club(
request, jieshao, jiage, guanshifc, zuzhangfc, jtjieshao,
)
if resolve_club_scope(request) != DATA_SCOPE_ALL and '000001' not in permissions:
return Response({
'code': 403,
'msg': '仅集团汇总视图可新建全局会员类型;俱乐部请从会员 catalog 上架',
})
data, err = add_global_member(jieshao, jiage, guanshifc, zuzhangfc, jtjieshao)
if err:
return Response({'code': 400, 'msg': err})
return Response({'code': 0, 'msg': '添加成功', 'data': data})
@@ -2892,6 +2898,78 @@ class AddMemberView(APIView):
class ClubMemberCatalogView(APIView):
"""俱乐部可上架的集团会员 catalog。POST /houtai/hthycatalog"""
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 '3300a' not in permissions:
return Response({'code': 403, 'msg': '无权限'})
from jituan.services.club_member_admin import build_member_catalog_payload
return Response({'code': 0, 'data': build_member_catalog_payload(request)})
class ClubMemberEnableView(APIView):
"""俱乐部上架会员。POST /houtai/hthysj"""
permission_classes = []
parser_classes = [JSONParser]
def post(self, request):
username = request.data.get('username', '').strip()
huiyuan_id = request.data.get('huiyuan_id', '').strip()
if not username or not huiyuan_id:
return Response({'code': 400, 'msg': '参数不完整'})
kefu, permissions = verify_kefu_permission(request, username)
if kefu is None:
return permissions
if '3300a' not in permissions:
return Response({'code': 403, 'msg': '无权限'})
try:
jiage = Decimal(str(request.data.get('jiage')))
guanshifc = Decimal(str(request.data.get('guanshifc')))
zuzhangfc = Decimal(str(request.data.get('zuzhangfc')))
except Exception:
return Response({'code': 400, 'msg': '金额格式错误'})
from jituan.services.club_member_admin import enable_member_for_club
extra = request.data
data, err = enable_member_for_club(
request, huiyuan_id, jiage, guanshifc, zuzhangfc, extra=extra,
)
if err:
return Response({'code': 400, 'msg': err})
return Response({'code': 0, 'msg': '上架成功', 'data': data})
class ClubMemberDisableView(APIView):
"""俱乐部下架会员。POST /houtai/hthyxj"""
permission_classes = []
parser_classes = [JSONParser]
def post(self, request):
username = request.data.get('username', '').strip()
huiyuan_id = request.data.get('huiyuan_id', '').strip()
if not username or not huiyuan_id:
return Response({'code': 400, 'msg': '参数不完整'})
kefu, permissions = verify_kefu_permission(request, username)
if kefu is None:
return permissions
if '3300a' not in permissions:
return Response({'code': 403, 'msg': '无权限'})
from jituan.services.club_member_admin import disable_member_for_club
data, err = disable_member_for_club(request, huiyuan_id)
if err:
return Response({'code': 400, 'msg': err})
return Response({'code': 0, 'msg': '已下架', 'data': data})
class GetGuanliListView(APIView):
"""