# llm_account_recharge_popup.dspy — 供应商账号列表「充值登记」弹窗内容 # 入口: json/llm_account_by_vendor.json 工具栏 recharge 按钮(selected_row) → binds # urlwidget → PopupWindow(外壳由 popup_options 生成,此处只返回内容 widget, # 返回 PopupWindow 会套娃——同 llm_call_trace_io.dspy 约定)。 # 入参: 列表页 toolbar selected_row 自动注入整行数据(id=账号主键)。 # 提交: POST llm_account_recharge.dspy → gateway.recharge_account: # balance += 充值金额、total_recharge 累加、写充值流水;余额回升后 # 「钱包充值」待办由状态派生自动消失。 uid = await get_user() if not uid: return {"widgettype": "Message", "options": {"title": "提示", "message": "请先登录", "timeout": 3}} acc_id = str((params_kw or {}).get('id') or '').strip() if not acc_id: return {"widgettype": "Message", "options": {"title": "打开失败", "message": "请先在列表中选中一个账号行"}} user_org = str(await get_userorgid() or '0') dbname = get_module_dbname('pipeline-llm') async with DBPools().sqlorContext(dbname) as sor: recs = await sor.sqlExe( "SELECT a.id, a.name, a.balance, a.total_recharge, a.status, " "COALESCE(NULLIF(a.org_id,''), v.org_id, '0') AS owner_org, v.name AS vendor_name " "FROM llm_account a LEFT JOIN llm_vendor v ON v.id=a.vendor_id " "WHERE a.id=${i}$ LIMIT 1", {"i": acc_id}) await sor.sqlExe("COMMIT", {}) if not recs: return {"widgettype": "Message", "options": {"title": "打开失败", "message": "账号不存在或已删除"}} row = recs[0] owner_org = str(row['owner_org'] or '0') # 机构隔离:平台业主('0')可看全部;否则只看本属主机构的账号(同 llm_wallet_todo_popup) if user_org != '0' and owner_org not in ('', user_org): return {"widgettype": "Message", "options": {"title": "无权访问", "message": "该账号属于其他机构"}} balance = float(row['balance'] or 0) acc_name = str(row['name'] or acc_id) vendor_name = str(row['vendor_name'] or '') head_md = ['## 充值登记', '', '**账号**:' + (('【%s】' % vendor_name) if vendor_name else '') + acc_name, '**当前余额**:%.4f 元 | **累计充值**:%.4f 元 | **状态**:%s' % (balance, float(row['total_recharge'] or 0), str(row['status'] or '')), '', '> 确认后账号余额 = 当前余额 + 充值金额,并写入充值流水;' '余额回升到门禁阈值以上后该账号自动恢复模型调用轮询。'] recharge_url = entire_url('/pipeline-llm/api/llm_account_recharge.dspy') def _read_input_js(wid): # 弹窗内控件 getWidgetById 起点必须 bricks.app(DOM 挂 body 下,app.root 查不到) return ("var cw=bricks.getWidgetById(" + json.dumps(wid) + ",bricks.app);var cv='';" "if(cw){cv=(typeof cw.resultValue==='function')?cw.resultValue():" "((cw.dom_element&&cw.dom_element.value)||'');}" "cv=(cv===null||cv===undefined)?'':String(cv).trim();") # 成功:关弹窗(从内容控件沿 parent 链找 PopupWindow 外壳) + 刷新账号列表 + 提示; # 失败:弹窗保持打开,允许改金额重试。 _close_and_refresh_js = ( "var box=bricks.getWidgetById('llm_acc_recharge_box',bricks.app);" "var pw=box;while(pw&&!(pw instanceof bricks.PopupWindow)&&!(pw instanceof bricks.Popup)){pw=pw.parent;}" "if(pw){if(pw.destroy){pw.destroy();}else if(pw.dismiss){pw.dismiss();}}" "var t=bricks.getWidgetById('llm_account_tbl',bricks.app);" "if(t&&t.render){t.old_params=null;t.render({});}") do_js = (_read_input_js('llm_acc_amt') + "var amt=parseFloat(cv);" + "if(isNaN(amt)||amt<=0){new bricks.Message({title:'请填写金额',message:'充值金额必须是大于 0 的数字'}).open();return;}" + _read_input_js('llm_acc_note') + "var r=await fetch(" + json.dumps(recharge_url) + ",{method:'POST'," "headers:{'Content-Type':'application/json'}," "body:JSON.stringify({account_id:" + json.dumps(acc_id) + ",amount:amt,note:cv})});" "var d=null;try{d=await r.json();}catch(e){}" + "if(d&&d.success){" + _close_and_refresh_js + "new bricks.Message({title:'充值成功',message:d.message||'账号余额已更新'}).open();}" + "else{new bricks.Message({title:'充值失败',message:(d&&(d.message||d.error))||('请求失败 HTTP '+r.status)}).open();}") return { "widgettype": "VBox", "id": "llm_acc_recharge_box", "options": {"width": "100%", "height": "100%", "padding": "16px", "gap": "12px"}, "subwidgets": [ {"widgettype": "MdWidget", "options": {"mdtext": '\n'.join(head_md), "width": "100%"}}, {"widgettype": "HBox", "options": {"width": "100%", "gap": "8px", "alignItems": "center"}, "subwidgets": [ {"widgettype": "Text", "options": {"text": "充值金额(元):", "cfontsize": 0.9, "color": "#334155"}}, {"widgettype": "UiText", "id": "llm_acc_amt", "options": {"name": "llm_acc_amt", "placeholder": "如 100.00", "width": "160px"}}, {"widgettype": "UiText", "id": "llm_acc_note", "options": {"name": "llm_acc_note", "placeholder": "备注(可选,如银行流水号)", "flex": "1 1 auto"}}, {"widgettype": "Button", "options": {"label": "💰 确认充值", "css": "primary small"}, "binds": [{"wid": "self", "event": "click", "actiontype": "script", "target": "self", "script": do_js}]}]}] }