# -*- coding: utf-8 -*- """下载次数增减与格式化。 (自 auth_system.py 拆出,行为与原实现一致。) """ import time from .db import ( _DB_LOCK, _x, get_conn, ) from .users import ( get_user_by_id, get_user_by_name, ) # ============ 使用次数(按次计费:一首歌 = 1 次) ============ def refund_credits(uid: int, count: int) -> int: """退回下载次数(批量打包失败部分自动退费)。返回剩余次数(-1 表示不限)""" user = get_user_by_id(uid) if user and (user['duration_seconds'] or 0) == -1: return -1 # 不限次数用户无需退费 count = max(0, int(count)) if count <= 0: return (user['duration_seconds'] or 0) if user else 0 _x('UPDATE users SET duration_seconds = COALESCE(duration_seconds, 0) + ? WHERE id = ?', (count, uid)) user = get_user_by_id(uid) return (user['duration_seconds'] or 0) if user else 0 def charge_credits(uid: int, count: int = 1) -> int: """按次扣减使用次数(管理员与不限次数(-1)用户不扣减,返回 -1)。返回剩余次数 扣减在 _DB_LOCK 内"读取-判断-写入"一次完成,防止并发双击导致超扣/双花""" user = get_user_by_id(uid) if not user: return 0 if user['role'] == 'admin' or (user['duration_seconds'] or 0) == -1: _x('UPDATE users SET last_active = ? WHERE id = ?', (int(time.time()), uid)) return -1 count = max(1, int(count)) with _DB_LOCK: conn = get_conn() try: row = conn.execute('SELECT duration_seconds FROM users WHERE id = ?', (uid,)).fetchone() cur = int(row['duration_seconds'] or 0) if row else 0 if cur == -1: conn.execute('UPDATE users SET last_active = ? WHERE id = ?', (int(time.time()), uid)) conn.commit() return -1 newval = max(0, cur - count) conn.execute('UPDATE users SET duration_seconds = ?, last_active = ? WHERE id = ?', (newval, int(time.time()), uid)) conn.commit() return newval finally: conn.close() def reserve_credits(uid: int, count: int = 1): """原子预扣下载次数(下载类接口在路由执行前调用,防止并发双击双花)。 足额:扣减并返回剩余次数;不限次数(-1)/管理员:不扣减返回 -1; 不足:返回 None(调用方应拒绝请求)""" count = max(1, int(count)) with _DB_LOCK: conn = get_conn() try: row = conn.execute('SELECT role, duration_seconds FROM users WHERE id = ?', (uid,)).fetchone() if not row: return None if row['role'] == 'admin': conn.execute('UPDATE users SET last_active = ? WHERE id = ?', (int(time.time()), uid)) conn.commit() return -1 cur = int(row['duration_seconds'] or 0) if cur == -1: conn.execute('UPDATE users SET last_active = ? WHERE id = ?', (int(time.time()), uid)) conn.commit() return -1 if cur < count: return None conn.execute('UPDATE users SET duration_seconds = ?, last_active = ? WHERE id = ?', (cur - count, int(time.time()), uid)) conn.commit() return cur - count finally: conn.close() def add_credits(username: str, count: int) -> int: """增加次数(卡密充值/试用领取),返回新剩余次数;不限次数(-1)用户保持不限 累加用 SQL 原生自增(原子),避免并发充值互相覆盖""" user = get_user_by_name(username) if not user: return 0 if user['role'] == 'admin' or (user['duration_seconds'] or 0) == -1: return -1 _x('UPDATE users SET duration_seconds = COALESCE(duration_seconds, 0) + ? WHERE id = ?', (max(0, int(count)), user['id'])) user = get_user_by_name(username) return (user['duration_seconds'] or 0) if user else 0 def format_credits(count: int) -> str: if count < 0: return '不限次数' if count <= 0: return '已用完' return f'{count} 次'