action = params_kw.get('action', 'submit') # ---- Lookup mode: find customer by username, return info ---- if action == 'lookup': username = params_kw.get('username', '').strip() if not username: return json.dumps({'status': 'error', 'message': '用户名不能为空'}, ensure_ascii=False, default=str) db = DBPools() dbname = get_module_dbname('accounting') async with db.sqlorContext(dbname) as sor: sql = """ select u.username, u.orgid as customerid, o.orgname, a.id as accountid, a.balance from users u left join organization o on u.orgid = o.id COLLATE utf8mb4_unicode_ci left join account a on a.orgid = u.orgid COLLATE utf8mb4_unicode_ci where u.username = ${username}$ limit 1 """ recs = await sor.sqlExe(sql, {'username': username}) if not recs or len(recs) == 0: return json.dumps({'status': 'error', 'message': f'用户 {username} 不存在'}, ensure_ascii=False, default=str) rec = recs[0] return json.dumps({ 'status': 'ok', 'data': { 'username': rec.username, 'customerid': rec.customerid, 'orgname': rec.orgname or '', 'accountid': rec.accountid or '', 'balance': float(rec.balance) if rec.balance else 0.0 } }, ensure_ascii=False, default=str) # ---- Submit mode: process the proxy recharge ---- username = params_kw.get('username', '').strip() amount_raw = params_kw.get('amount', 0) if not username: return { "widgettype": "Text", "options": {"text": "❌ 用户名不能为空", "color": "#EF4444"} } try: amount = float(amount_raw) except (ValueError, TypeError): return { "widgettype": "Text", "options": {"text": "❌ 充值金额格式错误", "color": "#EF4444"} } if amount <= 0: return { "widgettype": "Text", "options": {"text": "❌ 充值金额必须大于0", "color": "#EF4444"} } userid = await get_user() userorgid = await get_userorgid() db = DBPools() # Look up the target customer by username dbname = get_module_dbname('accounting') async with db.sqlorContext(dbname) as sor: sql = """ select u.username, u.orgid as customerid, o.orgname, a.id as accountid from users u left join organization o on u.orgid = o.id COLLATE utf8mb4_unicode_ci left join account a on a.orgid = u.orgid COLLATE utf8mb4_unicode_ci where u.username = ${username}$ limit 1 """ recs = await sor.sqlExe(sql, {'username': username}) if not recs or len(recs) == 0: return { "widgettype": "Text", "options": {"text": f"❌ 找不到用户名: {username}", "color": "#EF4444"} } customer = recs[0] customerid = customer.customerid if customerid == userorgid: return { "widgettype": "Text", "options": {"text": "❌ 不能给自己进行代客充值", "color": "#EF4444"} } # Create payment log in unipay for audit trail unipay_dbname = get_module_dbname('unipay') async with db.sqlorContext(unipay_dbname) as unipay_sor: plog_id = getID() biz_date = await get_business_date(sor) now_str = timestampstr() plog_data = { "id": plog_id, "customerid": customerid, "channelid": "proxy", "payment_name": "充值", "payer_client_ip": "admin_proxy", "amount_total": amount, "pay_feerate": 0.0, "pay_fee": 0.0, "currency": "CNY", "payment_status": "1", "init_timestamp": now_str, "payed_timestamp": now_str, "cancel_timestamp": "2000-01-01 00:00:00.001", "userid": userid } await unipay_sor.C('payment_log', plog_data.copy()) # Perform recharge accounting await recharge_accounting( sor, customerid, 'RECHARGE', plog_id, biz_date, amount, 0.0 ) debug(f'Proxy recharge: user={username}, customerid={customerid}, amount={amount}, operator={userid}') orgname = customer.orgname or '' return { "widgettype": "Text", "options": { "text": f"✅ 代客充值成功 — 已为用户 {username} ({orgname}) 充值 ¥{amount:.2f}", "color": "#22C55E", "fontSize": "14px", "fontWeight": "500" } }