"""后台赠送会员(免费接单):与购买会员 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