Files
Django/jituan/services/gift_huiyuan.py
2026-07-19 21:34:57 +08:00

252 lines
9.1 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.
"""后台赠送会员(免费接单):与购买会员 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 user_has_any_active_gift_huiyuan(yonghu_id):
"""是否持有任意一条未过期的赠送会员(提现/收款资格用)。"""
if not yonghu_id:
return False
now = timezone.now()
for rec in DashouZengsongHuiyuan.query.filter(yonghu_id=yonghu_id):
if _gift_record_active(rec, now=now):
return True
return False
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