🌐 feat: add configurable USD exchange-rate support across backend & frontend

Backend
- setting/payment.go: introduce default `USDExchangeRate` (7.3)
- model/option.go:
  • inject `USDExchangeRate` into `InitOptionMap`
  • persist & sync value in `updateOptionMap`
- controller/misc.go: expose `usd_exchange_rate` via `/api/status`

Frontend
- OperationSetting.js & SettingsGeneral.js:
  • extend state/inputs with `USDExchangeRate`
  • add form field “美元汇率 (non-top-up rate, pricing only)”
- ModelPricing.js already consumes `status.usd_exchange_rate`; no change needed

API
- Administrators can update the rate via `PUT /api/option` (key: `USDExchangeRate`)
- All clients receive the latest rate through `GET /api/status`

This closes the end-to-end flow for displaying model prices in both USD and CNY based on a configurable exchange rate.
This commit is contained in:
t0ng7u
2025-07-17 23:04:45 +08:00
parent b161d6831f
commit 15f65bb558
6 changed files with 20 additions and 1 deletions

View File

@@ -77,6 +77,7 @@ func GetStatus(c *gin.Context) {
"self_use_mode_enabled": operation_setting.SelfUseModeEnabled, "self_use_mode_enabled": operation_setting.SelfUseModeEnabled,
"default_use_auto_group": setting.DefaultUseAutoGroup, "default_use_auto_group": setting.DefaultUseAutoGroup,
"pay_methods": setting.PayMethods, "pay_methods": setting.PayMethods,
"usd_exchange_rate": setting.USDExchangeRate,
// 面板启用开关 // 面板启用开关
"api_info_enabled": cs.ApiInfoEnabled, "api_info_enabled": cs.ApiInfoEnabled,

View File

@@ -74,6 +74,7 @@ func InitOptionMap() {
common.OptionMap["EpayId"] = "" common.OptionMap["EpayId"] = ""
common.OptionMap["EpayKey"] = "" common.OptionMap["EpayKey"] = ""
common.OptionMap["Price"] = strconv.FormatFloat(setting.Price, 'f', -1, 64) common.OptionMap["Price"] = strconv.FormatFloat(setting.Price, 'f', -1, 64)
common.OptionMap["USDExchangeRate"] = strconv.FormatFloat(setting.USDExchangeRate, 'f', -1, 64)
common.OptionMap["MinTopUp"] = strconv.Itoa(setting.MinTopUp) common.OptionMap["MinTopUp"] = strconv.Itoa(setting.MinTopUp)
common.OptionMap["TopupGroupRatio"] = common.TopupGroupRatio2JSONString() common.OptionMap["TopupGroupRatio"] = common.TopupGroupRatio2JSONString()
common.OptionMap["Chats"] = setting.Chats2JsonString() common.OptionMap["Chats"] = setting.Chats2JsonString()
@@ -306,6 +307,8 @@ func updateOptionMap(key string, value string) (err error) {
setting.EpayKey = value setting.EpayKey = value
case "Price": case "Price":
setting.Price, _ = strconv.ParseFloat(value, 64) setting.Price, _ = strconv.ParseFloat(value, 64)
case "USDExchangeRate":
setting.USDExchangeRate, _ = strconv.ParseFloat(value, 64)
case "MinTopUp": case "MinTopUp":
setting.MinTopUp, _ = strconv.Atoi(value) setting.MinTopUp, _ = strconv.Atoi(value)
case "TopupGroupRatio": case "TopupGroupRatio":

View File

@@ -8,6 +8,7 @@ var EpayId = ""
var EpayKey = "" var EpayKey = ""
var Price = 7.3 var Price = 7.3
var MinTopUp = 1 var MinTopUp = 1
var USDExchangeRate = 7.3
var PayMethods = []map[string]string{ var PayMethods = []map[string]string{
{ {

View File

@@ -19,6 +19,7 @@ const OperationSetting = () => {
TopUpLink: '', TopUpLink: '',
'general_setting.docs_link': '', 'general_setting.docs_link': '',
QuotaPerUnit: 0, QuotaPerUnit: 0,
USDExchangeRate: 0,
RetryTimes: 0, RetryTimes: 0,
DisplayInCurrencyEnabled: false, DisplayInCurrencyEnabled: false,
DisplayTokenStatEnabled: false, DisplayTokenStatEnabled: false,

View File

@@ -1779,5 +1779,7 @@
"将仅保留第一个密钥文件,其余文件将被移除,是否继续?": "Only the first key file will be retained, and the remaining files will be removed. Continue?", "将仅保留第一个密钥文件,其余文件将被移除,是否继续?": "Only the first key file will be retained, and the remaining files will be removed. Continue?",
"自定义模型名称": "Custom model name", "自定义模型名称": "Custom model name",
"启用全部密钥": "Enable all keys", "启用全部密钥": "Enable all keys",
"以充值价格显示": "Show with recharge price" "以充值价格显示": "Show with recharge price",
"美元汇率(非充值汇率,仅用于定价页面换算)": "USD exchange rate (not recharge rate, only used for pricing page conversion)",
"美元汇率": "USD exchange rate"
} }

View File

@@ -26,6 +26,7 @@ export default function GeneralSettings(props) {
'general_setting.docs_link': '', 'general_setting.docs_link': '',
QuotaPerUnit: '', QuotaPerUnit: '',
RetryTimes: '', RetryTimes: '',
USDExchangeRate: '',
DisplayInCurrencyEnabled: false, DisplayInCurrencyEnabled: false,
DisplayTokenStatEnabled: false, DisplayTokenStatEnabled: false,
DefaultCollapseSidebar: false, DefaultCollapseSidebar: false,
@@ -129,6 +130,16 @@ export default function GeneralSettings(props) {
onClick={() => setShowQuotaWarning(true)} onClick={() => setShowQuotaWarning(true)}
/> />
</Col> </Col>
<Col xs={24} sm={12} md={8} lg={8} xl={8}>
<Form.Input
field={'USDExchangeRate'}
label={t('美元汇率(非充值汇率,仅用于定价页面换算)')}
initValue={''}
placeholder={t('美元汇率')}
onChange={handleFieldChange('USDExchangeRate')}
showClear
/>
</Col>
<Col xs={24} sm={12} md={8} lg={8} xl={8}> <Col xs={24} sm={12} md={8} lg={8} xl={8}>
<Form.Input <Form.Input
field={'RetryTimes'} field={'RetryTimes'}