From 9484266b9eeeb05a52ab2eb536297ec10a65a3ee Mon Sep 17 00:00:00 2001 From: yumoqing Date: Fri, 17 Jul 2026 11:38:21 +0800 Subject: [PATCH] fix(transfer): use add_startup for mail poll, add transfer_check.dspy + button for manual check --- scripts/load_path.py | 1 + unipay/init.py | 2 +- wwwroot/transfer_check.dspy | 67 +++++++++++++++++++++++++++++++++++++ wwwroot/transfer_info.ui | 18 ++++++++++ 4 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 wwwroot/transfer_check.dspy diff --git a/scripts/load_path.py b/scripts/load_path.py index afb4c37..cd6c824 100644 --- a/scripts/load_path.py +++ b/scripts/load_path.py @@ -56,6 +56,7 @@ PATHS_LOGINED = [ f"/unipay/refund.ui", f"/unipay/transfercode", f"/unipay/transfer_info.ui", + f"/unipay/transfer_check.dspy", f"/unipay/user_recharge.dspy",] # ============================================================ diff --git a/unipay/init.py b/unipay/init.py index 43fbb95..4a3a001 100644 --- a/unipay/init.py +++ b/unipay/init.py @@ -248,7 +248,7 @@ def load_unipay(): if PROVIDERS[name] is not None: debug(f"[unipay] {name} 初始化成功") if name == 'transfer': - asyncio.ensure_future(PROVIDERS[name].run()) + add_startup(PROVIDERS[name].run) debug(f"[unipay] transfer 邮件轮询已启动") else: debug(f"[unipay] {name} 初始化返回 None(渠道 disabled)") diff --git a/wwwroot/transfer_check.dspy b/wwwroot/transfer_check.dspy new file mode 100644 index 0000000..7143d5a --- /dev/null +++ b/wwwroot/transfer_check.dspy @@ -0,0 +1,67 @@ +"""手动检查转账邮件并完成入账""" +tcode = str(params_kw.tcode) +debug(f'transfer_check.dspy: tcode={tcode}') + +try: + provider = PROVIDERS.get('transfer') + if provider is None: + return {"widgettype": "Message", "options": {"timeout": 3, "title": "错误", "message": "转账渠道未初始化"}} + + ec = EmailClient(provider.pop3server, provider.email, provider.password) + count, _ = ec.stat() + debug(f'transfer_check: got {count} mails') + + matched = None + for i in range(count, 0, -1): + try: + mail = ec.get_mail(i) + if mail.mailfrom != '95555@message.cmbchina.com': + continue + if not mail.body.startswith('动账业务通知'): + continue + data = provider.get_transfer_data(mail) + if data is None: + continue + if str(data.code) == tcode: + matched = data + break + except Exception as e: + debug(f'transfer_check: skip mail {i}: {e}') + + if matched is None: + return {"widgettype": "Message", "options": {"timeout": 3, "title": "未匹配", "message": f"暂未找到转账码 {tcode} 的到账邮件,请确认已转账并稍后重试"}} + + env = request._run_ns + db = DBPools() + dbname = get_module_dbname('unipay') + async with db.sqlorContext(dbname) as sor: + recs = await sor.R('transfercode', {'tcode': tcode, 'status': '0'}) + if len(recs) < 1: + return {"widgettype": "Message", "options": {"timeout": 3, "title": "已处理", "message": f"转账码 {tcode} 已处理或不存在"}} + + tc = recs[0] + diff = abs(float(matched.amount) - float(tc.amount)) + if diff > 0.01: + await sor.U('transfercode', {'id': tc.id, 'status': '9', 'remark': f'金额不匹配: 到账{matched.amount} 期望{tc.amount}'}) + return {"widgettype": "Message", "options": {"timeout": 5, "title": "金额不匹配", "message": f"到账金额 {matched.amount} 与充值金额 {tc.amount} 不一致"}} + + biz_date = await env.get_business_date(sor) + await sor.U('transfercode', {'id': tc.id, 'status': '1'}) + + recs2 = await sor.R('payment_log', {'id': tc.id, 'payment_status': '0'}) + if len(recs2) > 0: + plog = recs2[0] + await env.recharge_accounting(sor, + plog.customerid, 'RECHARGE', plog.id, + biz_date, plog.amount_total, plog.pay_feerate) + await sor.U('payment_log', { + 'id': plog.id, + 'payment_status': '1', + 'payed_timestamp': timestampstr() + }) + + return {"widgettype": "Message", "options": {"timeout": 3, "title": "充值成功", "message": f"转账码 {tcode} 已确认到账 {matched.amount} 元"}} + +except Exception as e: + exception(f'transfer_check.dspy error: {e}') + return {"widgettype": "Message", "options": {"timeout": 5, "title": "错误", "message": f"处理失败: {e}"} diff --git a/wwwroot/transfer_info.ui b/wwwroot/transfer_info.ui index 64b8ef9..9597675 100644 --- a/wwwroot/transfer_info.ui +++ b/wwwroot/transfer_info.ui @@ -32,6 +32,24 @@ {"widgettype": "Text", "options": {"text": "日期:{{params_kw.curdate}}"}} ] }, + { + "widgettype": "Button", + "options": { + "label": "我已完成转账", + "css": "primary" + }, + "binds": [{ + "wid": "self", + "event": "click", + "actiontype": "urlfetch", + "options": { + "url": "{{entire_url('transfer_check.dspy')}}", + "params": { + "tcode": "{{params_kw.tcode}}" + } + } + }] + }, { "widgettype": "Text", "options": {