feat: 打手赠送会员(免费接单)独立表与抢单资格
新增 dashou_zengsong_huiyuan,与购买会员分离;抢单/抢单池认赠送有效会员;后台送/改期/移除接口,送时积分非10则改为10并写操作日志。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -14,7 +14,8 @@ from .view import (GetRolePermissionView,
|
||||
FineApplyView, PunishDashouView,
|
||||
FaKuanTongJiView, FaKuanLieBiaoView, FaKuanChuLiView, FaKuanChuangJianView, FaKuanPingTaiShenHeView,
|
||||
HqbkxxView,BkxgView,CaiwuView,CwhybkhqView, HybkjtsjView, SzxxView, CwddhqlxView, CwhqjtddsjView,
|
||||
CwqtczhqView, KhpzhqView, ChzsgcView,KhgglView, ShgxgsjView, KhjlglView, KhjlczView, ZxkfghdsView)
|
||||
CwqtczhqView, KhpzhqView, ChzsgcView,KhgglView, ShgxgsjView, KhjlglView, KhjlczView, ZxkfghdsView,
|
||||
DashouGiftHuiyuanSaveView, DashouGiftHuiyuanRemoveView)
|
||||
from config.views import MiniappScriptHoutaiListView, MiniappScriptHoutaiModifyView
|
||||
from rank.reward_houtai_views import RankRewardHoutaiListView, RankRewardHoutaiSaveView, RankRewardHoutaiClaimsView
|
||||
|
||||
@@ -30,6 +31,8 @@ urlpatterns = [
|
||||
path('kefuhqdslb', KefuGetDashouListView.as_view(), name='客服获取打手数据'),
|
||||
path('kefuhqdsxq', KefuGetDashouDetailView.as_view(), name='客服获取打手具体信息'),
|
||||
path('kefuxgds', KefuUpdateDashouView.as_view(), name='客服修改打手数据'),
|
||||
path('dszsonghy', DashouGiftHuiyuanSaveView.as_view(), name='打手赠送会员'),
|
||||
path('dsycsonghy', DashouGiftHuiyuanRemoveView.as_view(), name='移除打手赠送会员'),
|
||||
path('hqsjgllb', KefuGetShangjiaListView.as_view(), name='后台获取商家列表'),
|
||||
path('hthqsjxq', KefuGetShangjiaDetailView.as_view(), name='后台获取商家详情'),
|
||||
path('xgsjxx', KefuUpdateShangjiaView.as_view(), name='后台修改商家数据'),
|
||||
|
||||
@@ -36,6 +36,7 @@ from .views.kefu import (
|
||||
KefuGetDashouListView, KefuGetDashouDetailView, KefuUpdateDashouView,
|
||||
KefuGetShangjiaListView, KefuGetShangjiaDetailView, KefuUpdateShangjiaView,
|
||||
KefuChatPermissionsView,
|
||||
DashouGiftHuiyuanSaveView, DashouGiftHuiyuanRemoveView,
|
||||
)
|
||||
from .views.products import (
|
||||
GetProductBaseDataView, GetProductListView, SaveProductOrderView,
|
||||
@@ -96,6 +97,7 @@ __all__ = [
|
||||
'GetAdminRolesView', 'GetAdminUserListView', 'ModifyAdminUserView', 'AddAdminUserView',
|
||||
# kefu
|
||||
'KefuGetDashouListView', 'KefuGetDashouDetailView', 'KefuUpdateDashouView',
|
||||
'DashouGiftHuiyuanSaveView', 'DashouGiftHuiyuanRemoveView',
|
||||
'KefuGetShangjiaListView', 'KefuGetShangjiaDetailView', 'KefuUpdateShangjiaView',
|
||||
'KefuChatPermissionsView',
|
||||
# products
|
||||
|
||||
@@ -304,6 +304,7 @@ class KefuGetDashouDetailView(APIView):
|
||||
'data': {
|
||||
'user_info': user_info,
|
||||
'member_list': member_list,
|
||||
'gift_member_list': [],
|
||||
'all_members': all_members,
|
||||
'permissions': permissions,
|
||||
}
|
||||
@@ -369,11 +370,15 @@ class KefuGetDashouDetailView(APIView):
|
||||
# 7. 查询所有会员类型(用于前端添加会员)
|
||||
all_members = list(Huiyuan.query.all().values('huiyuan_id', 'jieshao'))
|
||||
|
||||
from jituan.services.gift_huiyuan import list_gift_huiyuan_for_admin
|
||||
gift_member_list = list_gift_huiyuan_for_admin(uid)
|
||||
|
||||
return Response({
|
||||
'code': 0,
|
||||
'data': {
|
||||
'user_info': user_info,
|
||||
'member_list': member_list,
|
||||
'gift_member_list': gift_member_list,
|
||||
'all_members': all_members,
|
||||
'permissions': permissions, # 返回当前客服的权限列表
|
||||
}
|
||||
@@ -1150,6 +1155,223 @@ class KefuChatPermissionsView(APIView):
|
||||
})
|
||||
|
||||
|
||||
class DashouGiftHuiyuanSaveView(APIView):
|
||||
"""
|
||||
赠送会员 / 改期(免费接单资格,写入 dashou_zengsong_huiyuan)
|
||||
POST /houtai/dszsonghy
|
||||
Body: {
|
||||
phone, dashou_id, huiyuan_id,
|
||||
daoqi_time: 'YYYY-MM-DD' 或 'YYYY-MM-DD HH:MM:SS',
|
||||
id?: 已有记录ID(改期时建议传)
|
||||
}
|
||||
"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
parser_classes = [JSONParser]
|
||||
|
||||
def post(self, request):
|
||||
from jituan.services.gift_huiyuan import (
|
||||
has_gift_huiyuan_permission,
|
||||
parse_gift_daoqi_time,
|
||||
upsert_gift_huiyuan,
|
||||
update_gift_huiyuan_daoqi,
|
||||
)
|
||||
from products.models import Huiyuan as HuiyuanModel
|
||||
|
||||
phone = (request.data.get('phone') or request.data.get('username') or '').strip()
|
||||
dashou_id = (request.data.get('dashou_id') or request.data.get('uid') or '').strip()
|
||||
huiyuan_id = (request.data.get('huiyuan_id') or '').strip()
|
||||
record_id = request.data.get('id')
|
||||
raw_daoqi = request.data.get('daoqi_time')
|
||||
|
||||
kefu, permissions = verify_kefu_permission(request, phone or None)
|
||||
if kefu is None:
|
||||
return permissions
|
||||
if not has_gift_huiyuan_permission(permissions):
|
||||
return Response({'code': 403, 'msg': '无赠送会员权限(需打手基础管理或加积分权限)'})
|
||||
|
||||
if not dashou_id:
|
||||
return Response({'code': 400, 'msg': '缺少打手ID'})
|
||||
daoqi_time, err = parse_gift_daoqi_time(raw_daoqi)
|
||||
if err:
|
||||
return Response({'code': 400, 'msg': err})
|
||||
|
||||
try:
|
||||
dashou_user = User.query.get(UserUID=dashou_id)
|
||||
except User.DoesNotExist:
|
||||
return Response({'code': 404, 'msg': '打手不存在'})
|
||||
denied = forbid_if_user_out_of_scope(request, dashou_user)
|
||||
if denied:
|
||||
return denied
|
||||
try:
|
||||
dashou_user.DashouProfile
|
||||
except ObjectDoesNotExist:
|
||||
return Response({'code': 404, 'msg': '该用户不是打手'})
|
||||
|
||||
song_ren = getattr(kefu.user, 'Phone', None) or phone
|
||||
operator = song_ren
|
||||
|
||||
# 有记录ID:只改期
|
||||
if record_id not in (None, ''):
|
||||
try:
|
||||
rid = int(record_id)
|
||||
except (TypeError, ValueError):
|
||||
return Response({'code': 400, 'msg': '无效的记录ID'})
|
||||
rec, err = update_gift_huiyuan_daoqi(
|
||||
record_id=rid,
|
||||
yonghu_id=dashou_id,
|
||||
daoqi_time=daoqi_time,
|
||||
song_ren=song_ren,
|
||||
)
|
||||
if err:
|
||||
return Response({'code': 400, 'msg': err})
|
||||
try:
|
||||
hy_name = HuiyuanModel.query.get(huiyuan_id=rec.huiyuan_id).jieshao
|
||||
except HuiyuanModel.DoesNotExist:
|
||||
hy_name = rec.huiyuan_id
|
||||
write_xiugai_log(
|
||||
yonghuid=dashou_id,
|
||||
xiugaiid=operator,
|
||||
leixing=XIUGAI_LEIXING_DASHOU,
|
||||
huiyuan_id=rec.huiyuan_id,
|
||||
qitashuoming=(
|
||||
f'【后台】修改赠送会员到期时间:会员ID={rec.huiyuan_id}({hy_name}),'
|
||||
f'新到期={rec.daoqi_time.strftime("%Y-%m-%d %H:%M:%S")},'
|
||||
f'打手ID={dashou_id},操作人={operator}'
|
||||
),
|
||||
)
|
||||
return Response({
|
||||
'code': 0,
|
||||
'msg': '改期成功',
|
||||
'data': {
|
||||
'id': rec.id,
|
||||
'huiyuan_id': rec.huiyuan_id,
|
||||
'daoqi_time': rec.daoqi_time.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
},
|
||||
})
|
||||
|
||||
if not huiyuan_id:
|
||||
return Response({'code': 400, 'msg': '缺少会员ID'})
|
||||
|
||||
rec, created, jifen_info, err = upsert_gift_huiyuan(
|
||||
yonghu_id=dashou_id,
|
||||
huiyuan_id=huiyuan_id,
|
||||
daoqi_time=daoqi_time,
|
||||
song_ren=song_ren,
|
||||
)
|
||||
if err:
|
||||
return Response({'code': 400, 'msg': err})
|
||||
|
||||
try:
|
||||
hy_name = HuiyuanModel.query.get(huiyuan_id=huiyuan_id).jieshao
|
||||
except HuiyuanModel.DoesNotExist:
|
||||
hy_name = huiyuan_id
|
||||
|
||||
if jifen_info:
|
||||
write_xiugai_log(
|
||||
yonghuid=dashou_id,
|
||||
xiugaiid=operator,
|
||||
leixing=XIUGAI_LEIXING_DASHOU,
|
||||
jifen=jifen_info['after'],
|
||||
yjifen=jifen_info['before'],
|
||||
qitashuoming=(
|
||||
f'【后台】送会员时整改积分:{jifen_info["before"]} → {jifen_info["after"]},'
|
||||
f'原积分={jifen_info["before"]},整改后={jifen_info["after"]},'
|
||||
f'打手ID={dashou_id},操作人={operator}(送会员免费接单)'
|
||||
),
|
||||
)
|
||||
|
||||
action_label = '赠送' if created else '更新到期时间'
|
||||
write_xiugai_log(
|
||||
yonghuid=dashou_id,
|
||||
xiugaiid=operator,
|
||||
leixing=XIUGAI_LEIXING_DASHOU,
|
||||
huiyuan_id=huiyuan_id,
|
||||
qitashuoming=(
|
||||
f'【后台】{action_label}赠送会员(免费接单):会员ID={huiyuan_id}({hy_name}),'
|
||||
f'到期={rec.daoqi_time.strftime("%Y-%m-%d %H:%M:%S")},'
|
||||
f'打手ID={dashou_id},操作人={operator}(写入赠送会员表,非购买会员)'
|
||||
),
|
||||
)
|
||||
return Response({
|
||||
'code': 0,
|
||||
'msg': '赠送成功' if created else '到期时间已更新',
|
||||
'data': {
|
||||
'id': rec.id,
|
||||
'huiyuan_id': rec.huiyuan_id,
|
||||
'daoqi_time': rec.daoqi_time.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'created': created,
|
||||
'jifen_adjusted': bool(jifen_info),
|
||||
'jifen': jifen_info,
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
class DashouGiftHuiyuanRemoveView(APIView):
|
||||
"""
|
||||
移除赠送会员
|
||||
POST /houtai/dsycsonghy
|
||||
Body: { phone, dashou_id, id? , huiyuan_id? }
|
||||
"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
parser_classes = [JSONParser]
|
||||
|
||||
def post(self, request):
|
||||
from jituan.services.gift_huiyuan import (
|
||||
has_gift_huiyuan_permission,
|
||||
remove_gift_huiyuan,
|
||||
)
|
||||
|
||||
phone = (request.data.get('phone') or request.data.get('username') or '').strip()
|
||||
dashou_id = (request.data.get('dashou_id') or request.data.get('uid') or '').strip()
|
||||
record_id = request.data.get('id')
|
||||
huiyuan_id = (request.data.get('huiyuan_id') or '').strip()
|
||||
|
||||
kefu, permissions = verify_kefu_permission(request, phone or None)
|
||||
if kefu is None:
|
||||
return permissions
|
||||
if not has_gift_huiyuan_permission(permissions):
|
||||
return Response({'code': 403, 'msg': '无移除赠送会员权限'})
|
||||
|
||||
if not dashou_id:
|
||||
return Response({'code': 400, 'msg': '缺少打手ID'})
|
||||
|
||||
try:
|
||||
dashou_user = User.query.get(UserUID=dashou_id)
|
||||
except User.DoesNotExist:
|
||||
return Response({'code': 404, 'msg': '打手不存在'})
|
||||
denied = forbid_if_user_out_of_scope(request, dashou_user)
|
||||
if denied:
|
||||
return denied
|
||||
|
||||
rid = None
|
||||
if record_id not in (None, ''):
|
||||
try:
|
||||
rid = int(record_id)
|
||||
except (TypeError, ValueError):
|
||||
return Response({'code': 400, 'msg': '无效的记录ID'})
|
||||
|
||||
ok, err = remove_gift_huiyuan(
|
||||
record_id=rid,
|
||||
yonghu_id=dashou_id if not rid else None,
|
||||
huiyuan_id=huiyuan_id if not rid else None,
|
||||
)
|
||||
if err:
|
||||
return Response({'code': 400, 'msg': err})
|
||||
|
||||
operator = getattr(kefu.user, 'Phone', None) or phone
|
||||
write_xiugai_log(
|
||||
yonghuid=dashou_id,
|
||||
xiugaiid=operator,
|
||||
leixing=XIUGAI_LEIXING_DASHOU,
|
||||
huiyuan_id=huiyuan_id or '',
|
||||
qitashuoming=(
|
||||
f'【后台】移除赠送会员(免费接单):记录ID={rid or "-"},'
|
||||
f'会员ID={huiyuan_id or "-"},打手ID={dashou_id},操作人={operator}'
|
||||
),
|
||||
)
|
||||
return Response({'code': 0, 'msg': '移除成功'})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
240
jituan/services/gift_huiyuan.py
Normal file
240
jituan/services/gift_huiyuan.py
Normal file
@@ -0,0 +1,240 @@
|
||||
"""后台赠送会员(免费接单):与购买会员 Huiyuangoumai 分离。"""
|
||||
from datetime import datetime, time
|
||||
|
||||
from django.db import transaction
|
||||
from django.utils import timezone
|
||||
from django.utils.dateparse import parse_datetime, parse_date
|
||||
|
||||
from products.models import Huiyuan
|
||||
from users.models import DashouZengsongHuiyuan, UserDashou
|
||||
|
||||
GIFT_HUIYUAN_PERMS = frozenset({
|
||||
'000001', '001aa', '001bb', '001cc', '001dd', '001ee',
|
||||
})
|
||||
TARGET_JIFEN_ON_GIFT = 10
|
||||
|
||||
|
||||
def has_gift_huiyuan_permission(permissions):
|
||||
return any(p in (permissions or []) for p in GIFT_HUIYUAN_PERMS)
|
||||
|
||||
|
||||
def _gift_record_active(record, now=None):
|
||||
if not record or not record.daoqi_time:
|
||||
return False
|
||||
from jituan.services.huiyuan_bundle import _normalize_datetime_for_compare
|
||||
now = _normalize_datetime_for_compare(now or timezone.now())
|
||||
daoqi = _normalize_datetime_for_compare(record.daoqi_time)
|
||||
return daoqi > now
|
||||
|
||||
|
||||
def parse_gift_daoqi_time(raw):
|
||||
"""
|
||||
解析到期时间。
|
||||
支持:'YYYY-MM-DD HH:MM:SS' / ISO / 'YYYY-MM-DD'(当天 23:59:59)。
|
||||
"""
|
||||
if raw is None or str(raw).strip() == '':
|
||||
return None, '请填写到期时间'
|
||||
text = str(raw).strip()
|
||||
dt = parse_datetime(text.replace('/', '-'))
|
||||
if dt is None:
|
||||
d = parse_date(text.replace('/', '-'))
|
||||
if d is None:
|
||||
try:
|
||||
dt = datetime.strptime(text, '%Y-%m-%d %H:%M:%S')
|
||||
except ValueError:
|
||||
try:
|
||||
d = datetime.strptime(text, '%Y-%m-%d').date()
|
||||
except ValueError:
|
||||
return None, '到期时间格式错误,请用 YYYY-MM-DD 或 YYYY-MM-DD HH:MM:SS'
|
||||
dt = datetime.combine(d, time(23, 59, 59))
|
||||
else:
|
||||
dt = datetime.combine(d, time(23, 59, 59))
|
||||
if timezone.is_naive(dt) and timezone.is_aware(timezone.now()):
|
||||
dt = timezone.make_aware(dt, timezone.get_current_timezone())
|
||||
elif (not timezone.is_naive(dt)) and (not timezone.is_aware(timezone.now())):
|
||||
dt = timezone.make_naive(dt, timezone.get_current_timezone())
|
||||
if dt <= timezone.now():
|
||||
return None, '到期时间必须晚于当前时间'
|
||||
return dt, None
|
||||
|
||||
|
||||
def list_gift_huiyuan_for_admin(yonghu_id):
|
||||
"""后台详情:全部赠送记录(含已过期),附会员名。"""
|
||||
records = list(
|
||||
DashouZengsongHuiyuan.query.filter(yonghu_id=yonghu_id).order_by('-daoqi_time')
|
||||
)
|
||||
if not records:
|
||||
return []
|
||||
name_map = {
|
||||
h.huiyuan_id: h.jieshao
|
||||
for h in Huiyuan.query.filter(
|
||||
huiyuan_id__in=[r.huiyuan_id for r in records]
|
||||
).only('huiyuan_id', 'jieshao')
|
||||
}
|
||||
now = timezone.now()
|
||||
out = []
|
||||
for r in records:
|
||||
active = _gift_record_active(r, now=now)
|
||||
out.append({
|
||||
'id': r.id,
|
||||
'huiyuan_id': r.huiyuan_id,
|
||||
'huiyuan_name': name_map.get(r.huiyuan_id, f'会员{r.huiyuan_id}'),
|
||||
'daoqi_time': r.daoqi_time.strftime('%Y-%m-%d %H:%M:%S') if r.daoqi_time else '',
|
||||
'song_ren': r.song_ren or '',
|
||||
'song_time': r.CreateTime.strftime('%Y-%m-%d %H:%M:%S') if r.CreateTime else '',
|
||||
'is_active': active,
|
||||
})
|
||||
return out
|
||||
|
||||
|
||||
def user_has_gift_huiyuan_access(yonghu_id, required_huiyuan_id, club_id=None):
|
||||
"""抢单资格:赠送会员是否覆盖所需会员类型(支持总会员展开)。"""
|
||||
from jituan.services.huiyuan_bundle import (
|
||||
huiyuan_ids_match,
|
||||
is_bundle_huiyuan,
|
||||
normalize_huiyuan_id,
|
||||
resolve_bundle_included_ids,
|
||||
_included_huiyuan_id_set,
|
||||
)
|
||||
|
||||
req = normalize_huiyuan_id(required_huiyuan_id)
|
||||
if not req:
|
||||
return True
|
||||
req_set = {req, req.lstrip('0') or '0'}
|
||||
now = timezone.now()
|
||||
for rec in DashouZengsongHuiyuan.query.filter(yonghu_id=yonghu_id):
|
||||
if not _gift_record_active(rec, now=now):
|
||||
continue
|
||||
if huiyuan_ids_match(rec.huiyuan_id, req):
|
||||
return True
|
||||
if is_bundle_huiyuan(rec.huiyuan_id):
|
||||
included = resolve_bundle_included_ids(
|
||||
rec.huiyuan_id,
|
||||
club_id=club_id,
|
||||
yonghu_id=yonghu_id,
|
||||
)
|
||||
if req_set & _included_huiyuan_id_set(included):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def build_gift_clumber_items(yonghu_id, club_id=None):
|
||||
"""抢单池 clumber:未过期赠送会员条目。"""
|
||||
from jituan.services.huiyuan_bundle import (
|
||||
_append_bundle_sub_clumber,
|
||||
_append_clumber_id_variants,
|
||||
format_legacy_daoqi,
|
||||
is_bundle_huiyuan,
|
||||
resolve_bundle_included_ids,
|
||||
_resolve_user_club_id,
|
||||
)
|
||||
|
||||
items = []
|
||||
seen = set()
|
||||
effective_club = _resolve_user_club_id(yonghu_id, club_id)
|
||||
now = timezone.now()
|
||||
for rec in DashouZengsongHuiyuan.query.filter(yonghu_id=yonghu_id):
|
||||
if not _gift_record_active(rec, now=now):
|
||||
continue
|
||||
try:
|
||||
hy = Huiyuan.query.get(huiyuan_id=rec.huiyuan_id)
|
||||
ming = hy.jieshao or f'会员{rec.huiyuan_id}'
|
||||
except Huiyuan.DoesNotExist:
|
||||
ming = f'会员{rec.huiyuan_id}'
|
||||
base = {
|
||||
'huiyuanming': ming,
|
||||
'daoqi': format_legacy_daoqi(rec.daoqi_time),
|
||||
'huiyuan_zhuangtai': 1,
|
||||
'shifou_daoqi': False,
|
||||
'is_gift': True,
|
||||
}
|
||||
if is_bundle_huiyuan(rec.huiyuan_id):
|
||||
base['is_bundle'] = True
|
||||
base['bundle_huiyuan_id'] = rec.huiyuan_id
|
||||
base['included_huiyuan_ids'] = resolve_bundle_included_ids(
|
||||
rec.huiyuan_id,
|
||||
club_id=effective_club,
|
||||
yonghu_id=yonghu_id,
|
||||
)
|
||||
_append_clumber_id_variants(items, seen, base, rec.huiyuan_id)
|
||||
_append_bundle_sub_clumber(items, seen, base)
|
||||
return items
|
||||
|
||||
|
||||
def upsert_gift_huiyuan(*, yonghu_id, huiyuan_id, daoqi_time, song_ren):
|
||||
"""
|
||||
赠送或改期。同一打手同一会员类型仅一条记录。
|
||||
返回 (record, created, jifen_changed_info_or_None, error_msg)
|
||||
jifen_changed_info = {'before': x, 'after': 10} 若调整了积分
|
||||
"""
|
||||
huiyuan_id = str(huiyuan_id or '').strip()
|
||||
if not huiyuan_id:
|
||||
return None, False, None, '缺少会员ID'
|
||||
if not Huiyuan.query.filter(huiyuan_id=huiyuan_id).exists():
|
||||
return None, False, None, '会员类型不存在'
|
||||
|
||||
jifen_info = None
|
||||
with transaction.atomic():
|
||||
try:
|
||||
dashou = UserDashou.objects.select_for_update().select_related('user').get(
|
||||
user__UserUID=yonghu_id
|
||||
)
|
||||
except UserDashou.DoesNotExist:
|
||||
return None, False, None, '打手不存在'
|
||||
|
||||
before_jifen = int(dashou.jifen or 0)
|
||||
if before_jifen != TARGET_JIFEN_ON_GIFT:
|
||||
dashou.jifen = TARGET_JIFEN_ON_GIFT
|
||||
dashou.save(update_fields=['jifen'])
|
||||
jifen_info = {'before': before_jifen, 'after': TARGET_JIFEN_ON_GIFT}
|
||||
|
||||
locked_qs = DashouZengsongHuiyuan.objects.select_for_update().filter(
|
||||
yonghu_id=yonghu_id, huiyuan_id=huiyuan_id
|
||||
)
|
||||
existing = locked_qs.first()
|
||||
if existing:
|
||||
existing.daoqi_time = daoqi_time
|
||||
existing.song_ren = song_ren
|
||||
existing.save(update_fields=['daoqi_time', 'song_ren', 'UpdateTime'])
|
||||
return existing, False, jifen_info, None
|
||||
|
||||
record = DashouZengsongHuiyuan.query.create(
|
||||
yonghu_id=yonghu_id,
|
||||
huiyuan_id=huiyuan_id,
|
||||
daoqi_time=daoqi_time,
|
||||
song_ren=song_ren,
|
||||
)
|
||||
return record, True, jifen_info, None
|
||||
|
||||
|
||||
def update_gift_huiyuan_daoqi(*, record_id, yonghu_id, daoqi_time, song_ren):
|
||||
"""按记录 ID 改期。"""
|
||||
try:
|
||||
rec = DashouZengsongHuiyuan.query.get(id=record_id)
|
||||
except DashouZengsongHuiyuan.DoesNotExist:
|
||||
return None, '赠送会员记录不存在'
|
||||
if str(rec.yonghu_id) != str(yonghu_id):
|
||||
return None, '记录与打手不匹配'
|
||||
rec.daoqi_time = daoqi_time
|
||||
if song_ren:
|
||||
rec.song_ren = song_ren
|
||||
rec.save(update_fields=['daoqi_time', 'song_ren', 'UpdateTime'])
|
||||
return rec, None
|
||||
|
||||
|
||||
def remove_gift_huiyuan(*, record_id=None, yonghu_id=None, huiyuan_id=None):
|
||||
"""按记录 ID,或 (yonghu_id, huiyuan_id) 移除。"""
|
||||
qs = DashouZengsongHuiyuan.query
|
||||
if record_id:
|
||||
deleted, _ = qs.filter(id=record_id).delete()
|
||||
if not deleted:
|
||||
return False, '赠送会员记录不存在'
|
||||
return True, None
|
||||
yonghu_id = str(yonghu_id or '').strip()
|
||||
huiyuan_id = str(huiyuan_id or '').strip()
|
||||
if not yonghu_id or not huiyuan_id:
|
||||
return False, '缺少记录ID或会员ID'
|
||||
deleted, _ = qs.filter(yonghu_id=yonghu_id, huiyuan_id=huiyuan_id).delete()
|
||||
if not deleted:
|
||||
return False, '赠送会员记录不存在'
|
||||
return True, None
|
||||
@@ -231,6 +231,10 @@ def user_has_huiyuan_access(yonghu_id, required_huiyuan_id, club_id=None):
|
||||
|
||||
if _czjilu_grants_huiyuan_access(yonghu_id, req, club_id):
|
||||
return True
|
||||
|
||||
from jituan.services.gift_huiyuan import user_has_gift_huiyuan_access
|
||||
if user_has_gift_huiyuan_access(yonghu_id, req, club_id=club_id):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
@@ -498,26 +502,33 @@ def _build_clumber_from_paid_czjilu(yonghu_id, club_id=None):
|
||||
|
||||
|
||||
def _finalize_clumber_for_legacy_client(items):
|
||||
"""老端只认 huiyuanid;去掉 is_trial 等内部字段。"""
|
||||
"""老端只认 huiyuanid;去掉 is_trial / is_gift 等内部字段。"""
|
||||
cleaned = []
|
||||
for it in items or []:
|
||||
row = dict(it)
|
||||
row.pop('is_trial', None)
|
||||
row.pop('is_gift', None)
|
||||
cleaned.append(row)
|
||||
return cleaned
|
||||
|
||||
|
||||
def ensure_grab_pool_clumber(yonghu_id, club_id=None):
|
||||
"""
|
||||
dddhq / dashouxinxi:只读 huiyuangoumai + 有效期内充值单 生成 clumber(体验=正式)。
|
||||
dddhq / dashouxinxi:只读 huiyuangoumai + 有效期内充值单 + 赠送会员 生成 clumber。
|
||||
总会员展开子会员虚拟条目供老端 clumber 比对;不写库、不补开通。
|
||||
"""
|
||||
from jituan.services.gift_huiyuan import build_gift_clumber_items
|
||||
|
||||
effective_club = _resolve_user_club_id(yonghu_id, club_id)
|
||||
items = _build_user_clumber_core(yonghu_id, club_id=effective_club)
|
||||
items = _merge_clumber_extra_items(
|
||||
items,
|
||||
_build_clumber_from_paid_czjilu(yonghu_id, club_id=effective_club),
|
||||
)
|
||||
items = _merge_clumber_extra_items(
|
||||
items,
|
||||
build_gift_clumber_items(yonghu_id, club_id=effective_club),
|
||||
)
|
||||
items = _finalize_clumber_for_legacy_client(items)
|
||||
return bool(items), items
|
||||
|
||||
|
||||
33
users/migrations/0005_dashou_zengsong_huiyuan.py
Normal file
33
users/migrations/0005_dashou_zengsong_huiyuan.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0004_user_shangjia_youzhi'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='DashouZengsongHuiyuan',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='记录ID')),
|
||||
('yonghu_id', models.CharField(db_index=True, max_length=7, verbose_name='打手用户ID')),
|
||||
('huiyuan_id', models.CharField(db_index=True, max_length=6, verbose_name='会员ID')),
|
||||
('daoqi_time', models.DateTimeField(db_index=True, verbose_name='到期时间')),
|
||||
('song_ren', models.CharField(max_length=30, verbose_name='赠送人账号')),
|
||||
('CreateTime', models.DateTimeField(auto_now_add=True, verbose_name='赠送时间')),
|
||||
('UpdateTime', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '打手赠送会员',
|
||||
'verbose_name_plural': '打手赠送会员',
|
||||
'db_table': 'dashou_zengsong_huiyuan',
|
||||
'indexes': [
|
||||
models.Index(fields=['yonghu_id', 'huiyuan_id'], name='dashou_zeng_yonghu__7a1c01_idx'),
|
||||
models.Index(fields=['yonghu_id', 'daoqi_time'], name='dashou_zeng_yonghu__8b2d02_idx'),
|
||||
],
|
||||
'unique_together': {('yonghu_id', 'huiyuan_id')},
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -553,7 +553,31 @@ class Xiugaijilu(QModel):
|
||||
return f"修改记录-用户{self.yonghuid}"
|
||||
|
||||
|
||||
class DashouZengsongHuiyuan(QModel):
|
||||
"""
|
||||
后台赠送会员(免费接单资格),与 Huiyuangoumai 购买会员完全分离。
|
||||
仅用于抢单/抢单池资格;不参与提现正式会员校验。
|
||||
"""
|
||||
id = models.AutoField(primary_key=True, verbose_name='记录ID')
|
||||
yonghu_id = models.CharField(max_length=7, db_index=True, verbose_name='打手用户ID')
|
||||
huiyuan_id = models.CharField(max_length=6, db_index=True, verbose_name='会员ID')
|
||||
daoqi_time = models.DateTimeField(db_index=True, verbose_name='到期时间')
|
||||
song_ren = models.CharField(max_length=30, verbose_name='赠送人账号')
|
||||
CreateTime = models.DateTimeField(auto_now_add=True, verbose_name='赠送时间')
|
||||
UpdateTime = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'dashou_zengsong_huiyuan'
|
||||
verbose_name = '打手赠送会员'
|
||||
verbose_name_plural = verbose_name
|
||||
unique_together = [['yonghu_id', 'huiyuan_id']]
|
||||
indexes = [
|
||||
models.Index(fields=['yonghu_id', 'huiyuan_id']),
|
||||
models.Index(fields=['yonghu_id', 'daoqi_time']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"赠送会员-用户{self.yonghu_id}-会员{self.huiyuan_id}"
|
||||
|
||||
|
||||
# yonghu/models.py - 新增排行榜模型
|
||||
|
||||
Reference in New Issue
Block a user