Browse Source

🌐 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.
t0ng7u 5 months ago
parent
commit
15f65bb558

+ 1 - 0
controller/misc.go

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

+ 3 - 0
model/option.go

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

+ 1 - 0
setting/payment.go

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

+ 1 - 0
web/src/components/settings/OperationSetting.js

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

+ 3 - 1
web/src/i18n/locales/en.json

@@ -1779,5 +1779,7 @@
   "将仅保留第一个密钥文件,其余文件将被移除,是否继续?": "Only the first key file will be retained, and the remaining files will be removed. Continue?",
   "自定义模型名称": "Custom model name",
   "启用全部密钥": "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"
 }

+ 11 - 0
web/src/pages/Setting/Operation/SettingsGeneral.js

@@ -26,6 +26,7 @@ export default function GeneralSettings(props) {
     'general_setting.docs_link': '',
     QuotaPerUnit: '',
     RetryTimes: '',
+    USDExchangeRate: '',
     DisplayInCurrencyEnabled: false,
     DisplayTokenStatEnabled: false,
     DefaultCollapseSidebar: false,
@@ -129,6 +130,16 @@ export default function GeneralSettings(props) {
                   onClick={() => setShowQuotaWarning(true)}
                 />
               </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}>
                 <Form.Input
                   field={'RetryTimes'}