feat(transfer): process ALL matching emails, not just current tcode

This commit is contained in:
yumoqing 2026-07-17 13:56:58 +08:00
parent baffcaa043
commit c0c04b2bd0

View File

@ -221,14 +221,15 @@ class TransferGateway(Gateway):
await asyncio.sleep(30)
async def check_transfer(self, tcode: str, env):
"""扫描最近2天邮件查找指定转账码并完成入账。
"""扫描最近2天邮件对所有匹配的转账码完成入账。
返回: (title, message)"""
from datetime import datetime, timedelta
from email.utils import parsedate_to_datetime
cutoff = datetime.now() - timedelta(days=2)
ec = EmailClient(self.pop3server, self.email, self.password)
count, _ = ec.stat()
debug(f'check_transfer: tcode={tcode}, total {count} mails, cutoff={cutoff}')
debug('check_transfer: tcode=' + tcode + ' total=' + str(count) + ' mails, cutoff=' + str(cutoff))
db = DBPools()
dbname = env.get_module_dbname('unipay')
@ -240,8 +241,8 @@ class TransferGateway(Gateway):
tc = recs[0]
return ('已入账', '转账码 ' + tcode + ' 已入账 ' + str(tc.amount) + '')
log_lines = []
matched = None
done = []
failed = []
scanned = 0
for i in range(count, 0, -1):
@ -250,12 +251,10 @@ class TransferGateway(Gateway):
scanned += 1
idx = str(i)
# 日期过滤
mail_date_str = getattr(mail, 'date', None)
skip_date = False
if mail_date_str:
try:
from email.utils import parsedate_to_datetime
mail_date = parsedate_to_datetime(mail_date_str)
if mail_date < cutoff:
skip_date = True
@ -284,52 +283,60 @@ class TransferGateway(Gateway):
code = str(data.code)
amount = data.amount
debug('mail[' + idx + '] code=' + code + ' amount=' + str(amount) + ' looking_for=' + tcode)
debug('mail[' + idx + '] code=' + code + ' amount=' + str(amount))
if code == tcode:
matched = data
debug('mail[' + idx + '] *** MATCHED tcode=' + tcode + ' amount=' + str(amount) + ' ***')
break
debug('mail[' + idx + '] EXIT: code mismatch ' + code + ' != ' + tcode)
# 在transfercode表中查找 status=0 的记录并入账
async with db.sqlorContext(dbname) as sor:
recs = await sor.R('transfercode', {'tcode': code, 'status': '0'})
if len(recs) < 1:
debug('mail[' + idx + '] EXIT: code ' + code + ' not in transfercode or already processed')
continue
tc = recs[0]
diff = abs(float(amount) - float(tc.amount))
if diff > 0.01:
await sor.U('transfercode', {'id': tc.id, 'status': '9',
'remark': '金额不匹配: 到账' + str(amount) + ' 期望' + str(tc.amount)})
debug('mail[' + idx + '] amount mismatch: ' + str(amount) + ' != ' + str(tc.amount))
failed.append(code + '(金额不匹配)')
continue
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()
})
done.append(code + '(' + str(amount) + '元)')
debug('mail[' + idx + '] ACCOUNTING DONE: code=' + code + ' amount=' + str(amount))
except Exception as e:
debug('mail[' + str(i) + '] ERROR: ' + str(e))
debug('check_transfer: scanned ' + str(scanned) + ' mails, matched=' + str(matched is not None))
debug('check_transfer: scanned=' + str(scanned) + ' done=' + str(done) + ' failed=' + str(failed))
if matched is None:
# 构造返回消息
msg_parts = []
if done:
msg_parts.append('入账成功 ' + str(len(done)) + ' 笔: ' + ', '.join(done))
if failed:
msg_parts.append('失败 ' + str(len(failed)) + ' 笔: ' + ', '.join(failed))
if tcode in [d.split('(')[0] for d in done]:
return ('充值成功', '\n'.join(msg_parts))
if not done and not failed:
return ('尚未到账', '扫描了 ' + str(scanned) + ' 封邮件,暂未收到转账码 ' + tcode + ' 的到账通知')
# 执行入账
async with db.sqlorContext(dbname) as sor:
recs = await sor.R('transfercode', {'tcode': tcode, 'status': '0'})
if len(recs) < 1:
return ('已处理', '转账码 ' + 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': '金额不匹配: 到账' + str(matched.amount) + ' 期望' + str(tc.amount)})
return ('金额不匹配', '到账金额 ' + str(matched.amount) + ' 与充值金额 ' + str(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()
})
debug('check_transfer: ACCOUNTING DONE for ' + tcode + ' amount=' + str(matched.amount))
return ('充值成功', '转账码 ' + tcode + ' 已确认到账 ' + str(matched.amount) + '')
return ('转账检查结果', '\n'.join(msg_parts) + '\n\n当前转账码 ' + tcode + ' 尚未到账')
async def query(self, out_trade_no: str) -> Dict[str, Any]:
pass