Files
shaguabijia-app-server/scripts/reset_signin.py
T
OuYingJun1024 0722d7b0d5 feat: 福利钱包+签到+省钱战绩+激励广告发奖+微信提现 后端
- 福利钱包: 金币/现金余额、流水、兑换 (api/v1/wallet.py, crud/wallet.py, models/wallet.py, schemas/welfare.py)
- 每日签到: 连续天数 + 档位奖励 (api/v1/signin.py, crud/signin.py, models/signin.py)
- 任务系统: "打开消息提醒"等任务领奖 (api/v1/tasks.py, crud/task.py, models/task.py)
- 省钱战绩: 省钱汇总/战绩/店铺菜品 (api/v1/savings.py, crud/savings.py, models/savings.py)
- 激励广告发奖: 穿山甲服务端回调 + 发奖规则 + 限流 (api/v1/ad.py, core/pangle.py, core/rewards.py, core/ratelimit.py, crud/ad_reward.py, schemas/ad.py, docs/ad_reward_golive_checklist.md)
- 微信提现: 商家转账到零钱 V3 (core/wxpay.py); user 表加微信 openid/nickname/avatar
- DB 迁移: 8 个 alembic (welfare 表/cash_transaction/openid 唯一约束/savings 店铺菜品/savings_record/ad_reward/withdraw_order+openid/user 微信字段)
- 运维脚本: reconcile_withdraws(对账) + reset_signin/reset_welfare + sim_pangle_callback(模拟穿山甲回调)
- 测试: test_welfare / test_withdraw / test_ad_reward
- 配置: .env.example + config.py 新增福利/广告/微信支付项; main.py 挂 5 个新路由

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 17:38:08 +08:00

122 lines
5.0 KiB
Python

"""清空指定用户的签到记录,方便反复测试"未签到→自动弹窗→签到领金币"
默认**只删 signin_record**(签到历史),不动金币/现金——这样能保留你已注入/兑换的余额,
只把"今天是否已签到 / 连续天数"重置掉。删后当天即变成"未签到",再进福利页会重新自动弹签到弹窗。
⚠️ 注意:只删签到记录的话,之前签到已发的金币仍留在余额和 coin_transaction 里(biz_type='signin')。
如果想把签到发的金币也一并退掉(余额减回、删 signin 流水),加 --with-coins。
用法(在 shaguabijia-app-server 目录下,用本项目的 python 跑):
python scripts/reset_signin.py # 只清 user_id=2 的签到记录
python scripts/reset_signin.py 5 # 清 user_id=5
python scripts/reset_signin.py --all # 清所有用户的签到记录
python scripts/reset_signin.py --with-coins # 连签到发的金币一起退回(余额减、删 signin 流水)
python scripts/reset_signin.py --dry-run # 只看现状,不改
DB 默认取 <项目根>/data/app.db,可用 --db 指定。
"""
from __future__ import annotations
import argparse
import sqlite3
import sys
from pathlib import Path
# Windows 控制台默认 GBK,强制 UTF-8 否则中文输出乱码
if hasattr(sys.stdout, "reconfigure"):
sys.stdout.reconfigure(encoding="utf-8")
# 本脚本在 scripts/ 下,项目根是上一级
DEFAULT_DB = Path(__file__).resolve().parent.parent / "data" / "app.db"
def snapshot(cur: sqlite3.Cursor, where: str, params: tuple) -> dict:
"""返回签到记录数 + 签到金币流水数/总额 + 账户金币余额,用于打印 before/after。"""
signin_rows = cur.execute(
f"select count(*) from signin_record where {where}", params
).fetchone()[0]
signin_coin_rows, signin_coin_sum = cur.execute(
f"select count(*), coalesce(sum(amount), 0) from coin_transaction "
f"where biz_type='signin' and {where}",
params,
).fetchone()
accounts = cur.execute(
f"select user_id, coin_balance, total_coin_earned from coin_account where {where}",
params,
).fetchall()
return {
"signin_rows": signin_rows,
"signin_coin_rows": signin_coin_rows,
"signin_coin_sum": signin_coin_sum,
"accounts": accounts,
}
def print_snapshot(label: str, snap: dict) -> None:
print(f"--- {label} ---")
print(f" signin_record: {snap['signin_rows']}")
print(f" coin_transaction(signin): {snap['signin_coin_rows']} 条, 共 {snap['signin_coin_sum']} 金币")
if snap["accounts"]:
for uid, coin, earned in snap["accounts"]:
print(f" coin_account user={uid}: coin={coin} earned={earned}")
else:
print(" coin_account: (无)")
def main() -> None:
parser = argparse.ArgumentParser(description="清空用户签到记录")
parser.add_argument("user_id", nargs="?", type=int, default=2,
help="要清的 user_id(默认 2)")
parser.add_argument("--all", action="store_true", help="清所有用户")
parser.add_argument("--with-coins", action="store_true",
help="同时退回签到发的金币(余额减、删 signin 流水)")
parser.add_argument("--dry-run", action="store_true", help="只看现状,不修改")
parser.add_argument("--db", default=str(DEFAULT_DB), help="SQLite 路径")
args = parser.parse_args()
if args.all:
where, params, scope = "1=1", (), "ALL users"
else:
where, params, scope = "user_id=?", (args.user_id,), f"user_id={args.user_id}"
db_path = Path(args.db)
if not db_path.exists():
raise SystemExit(f"DB 不存在: {db_path}")
conn = sqlite3.connect(str(db_path))
cur = conn.cursor()
print(f"DB: {db_path} scope: {scope} with_coins: {args.with_coins}")
print_snapshot("before", snapshot(cur, where, params))
if args.dry_run:
print("(dry-run,未修改)")
conn.close()
return
if args.with_coins:
# 先按用户把签到金币总额退回(余额、累计收益都减),再删 signin 流水
rows = cur.execute(
f"select user_id, coalesce(sum(amount), 0) from coin_transaction "
f"where biz_type='signin' and {where} group by user_id",
params,
).fetchall()
for uid, total in rows:
cur.execute(
"update coin_account set coin_balance = coin_balance - ?, "
"total_coin_earned = max(0, total_coin_earned - ?) where user_id = ?",
(total, total, uid),
)
cur.execute(f"delete from coin_transaction where biz_type='signin' and {where}", params)
cur.execute(f"delete from signin_record where {where}", params)
conn.commit()
print_snapshot("after", snapshot(cur, where, params))
conn.close()
print("签到记录已清空。提醒:App 内存里的状态不会自动同步,重启 App 或进收益明细页刷新;"
"重进福利页(当天未签到)会重新自动弹签到弹窗。")
if __name__ == "__main__":
main()