diff --git a/Dockerfile b/Dockerfile index ffb8c21b..bbee4d2e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:16 as builder +FROM node:18 as builder WORKDIR /build COPY web/package.json . diff --git a/README.md b/README.md index 6ce9712c..0acea905 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ docker pull woodchen/czloapi ``` -- [x] 当用户充值达到5刀时,自动提升为vip分组,具体金额可在`web\src\pages\TopUp\index.js`调整; +- [x] 当用户充值达到5刀时,自动提升为vip分组; - [x] 修改颜色 -- [x] 日志页面新增快速筛选日期、修复渠道ID查询、自动更新消耗额度、删除日志详情(因为没有太大意义); +- [x] 日志页面新增快速筛选日期、修复渠道ID查询、自动更新消耗额度、修改日志详情(因为没有太大意义); - [x] 用户管理界面,支持快速设置用户组,在`web\src\components\UsersTable.js`处修改相关值; - [x] 令牌界面,删除无用的多种复制、聊天等按钮; - [x] 删除系统访问令牌; diff --git a/controller/relay-text.go b/controller/relay-text.go index 54b56093..090d9cfb 100644 --- a/controller/relay-text.go +++ b/controller/relay-text.go @@ -435,14 +435,11 @@ func relayTextHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode { common.LogError(ctx, "error update user quota cache: "+err.Error()) } if quota != 0 { - // 对modelRatio进行计算 - modelRatio = modelRatio * 0.002 - logContent := fmt.Sprintf("单价 $%.4f/1k tokens,补全与官方一致", modelRatio) + logContent := fmt.Sprintf("模型倍率 %.4f", modelRatio) model.RecordConsumeLog(ctx, userId, channelId, promptTokens, completionTokens, textRequest.Model, tokenName, quota, logContent) model.UpdateUserUsedQuotaAndRequestCount(userId, quota) model.UpdateChannelUsedQuota(channelId, quota) } - } }() }(c.Request.Context()) diff --git a/controller/user.go b/controller/user.go index 59e50017..edbfc371 100644 --- a/controller/user.go +++ b/controller/user.go @@ -557,124 +557,6 @@ type ManageRequest struct { } // ManageUser Only admin user can do this -// func ManageUser(c *gin.Context) { -// var req ManageRequest -// err := json.NewDecoder(c.Request.Body).Decode(&req) - -// if err != nil { -// c.JSON(http.StatusOK, gin.H{ -// "success": false, -// "message": "无效的参数", -// }) -// return -// } -// user := model.User{ -// Username: req.Username, -// } -// // Fill attributes -// model.DB.Where(&user).First(&user) -// if user.Id == 0 { -// c.JSON(http.StatusOK, gin.H{ -// "success": false, -// "message": "用户不存在", -// }) -// return -// } -// myRole := c.GetInt("role") -// if myRole <= user.Role && myRole != common.RoleRootUser { -// c.JSON(http.StatusOK, gin.H{ -// "success": false, -// "message": "无权更新同权限等级或更高权限等级的用户信息", -// }) -// return -// } -// switch req.Action { -// case "disable": -// user.Status = common.UserStatusDisabled -// if user.Role == common.RoleRootUser { -// c.JSON(http.StatusOK, gin.H{ -// "success": false, -// "message": "无法禁用超级管理员用户", -// }) -// return -// } -// case "enable": -// user.Status = common.UserStatusEnabled -// case "delete": -// if user.Role == common.RoleRootUser { -// c.JSON(http.StatusOK, gin.H{ -// "success": false, -// "message": "无法删除超级管理员用户", -// }) -// return -// } -// if err := user.Delete(); err != nil { -// c.JSON(http.StatusOK, gin.H{ -// "success": false, -// "message": err.Error(), -// }) -// return -// } -// case "promote": -// if myRole != common.RoleRootUser { -// c.JSON(http.StatusOK, gin.H{ -// "success": false, -// "message": "普通管理员用户无法提升其他用户为管理员", -// }) -// return -// } -// if user.Role >= common.RoleAdminUser { -// c.JSON(http.StatusOK, gin.H{ -// "success": false, -// "message": "该用户已经是管理员", -// }) -// return -// } -// user.Role = common.RoleAdminUser -// case "demote": -// if user.Role == common.RoleRootUser { -// c.JSON(http.StatusOK, gin.H{ -// "success": false, -// "message": "无法降级超级管理员用户", -// }) -// return -// } -// if user.Role == common.RoleCommonUser { -// c.JSON(http.StatusOK, gin.H{ -// "success": false, -// "message": "该用户已经是普通用户", -// }) -// return -// } -// user.Role = common.RoleCommonUser -// } - -// if err := user.Update(false); err != nil { -// c.JSON(http.StatusOK, gin.H{ -// "success": false, -// "message": err.Error(), -// }) -// return -// } - -// user.Group = req.NewGroup - - -// clearUser := model.User{ -// Group: user.Group, -// Role: user.Role, -// Status: user.Status, -// } -// c.JSON(http.StatusOK, gin.H{ -// "success": true, -// "message": "", -// "data": clearUser, -// }) -// return - - - -// } func ManageUser(c *gin.Context) { var req ManageRequest err := json.NewDecoder(c.Request.Body).Decode(&req) @@ -792,7 +674,7 @@ func TopUp(c *gin.Context) { return } id := c.GetInt("id") - quota, err := model.Redeem(req.Key, id) + quota, upgradedToVIP, err := model.Redeem(req.Key, id) // 调用合并了升级逻辑的 Redeem if err != nil { c.JSON(http.StatusOK, gin.H{ "success": false, @@ -804,6 +686,7 @@ func TopUp(c *gin.Context) { "success": true, "message": "", "data": quota, + "upgradedToVIP": upgradedToVIP, // 返回升级信息 }) return } diff --git a/model/redemption.go b/model/redemption.go index f16412b5..c2201b09 100644 --- a/model/redemption.go +++ b/model/redemption.go @@ -41,12 +41,13 @@ func GetRedemptionById(id int) (*Redemption, error) { return &redemption, err } -func Redeem(key string, userId int) (quota int, err error) { +func Redeem(key string, userId int) (quota int, upgradedToVIP bool, err error) { + upgradedToVIP = false // 初始化升级状态为 false(注意:这里不需要使用 var) if key == "" { - return 0, errors.New("未提供兑换码") + return 0, false, errors.New("未提供兑换码") } if userId == 0 { - return 0, errors.New("无效的 user id") + return 0, false, errors.New("无效的 user id") } redemption := &Redemption{} @@ -73,10 +74,28 @@ func Redeem(key string, userId int) (quota int, err error) { return err }) if err != nil { - return 0, errors.New("兑换失败," + err.Error()) + return 0, false, errors.New("兑换失败," + err.Error()) } RecordLog(userId, LogTypeTopup, fmt.Sprintf("通过兑换码充值 %s", common.LogQuota(redemption.Quota))) - return redemption.Quota, nil + + // 获取用户信息 + user := &User{} + err = DB.Where("id = ?", userId).First(user).Error + if err != nil { + return redemption.Quota, upgradedToVIP, errors.New("查询用户信息失败") + } + + // 检查是否需要升级为 VIP + if user.Group != "vip" && user.Quota >= 5*500000 { + // 升级用户到 VIP + err = DB.Model(&User{}).Where("id = ?", userId).Update("group", "vip").Error + if err != nil { + return redemption.Quota, upgradedToVIP, errors.New("升级 VIP 失败") + } + upgradedToVIP = true // 设置升级状态为 true + } + + return redemption.Quota, upgradedToVIP, nil } func (redemption *Redemption) Insert() error { diff --git a/web/src/components/LogsTable.js b/web/src/components/LogsTable.js index a485f86d..2bb44bb5 100644 --- a/web/src/components/LogsTable.js +++ b/web/src/components/LogsTable.js @@ -327,7 +327,7 @@ const LogsTable = () => { onClick={() => { sortLog('created_time'); }} - width={3} + width={2} > 时间 @@ -412,9 +412,9 @@ const LogsTable = () => { onClick={() => { sortLog('content'); }} - width={isAdminUser ? 3 : 5} + width={isAdminUser ? 4 : 6} > - 详情 + 详情(1=$0.002/1K tokens) diff --git a/web/src/pages/TopUp/index.js b/web/src/pages/TopUp/index.js index 47f6c70f..53c6f3cd 100644 --- a/web/src/pages/TopUp/index.js +++ b/web/src/pages/TopUp/index.js @@ -7,31 +7,8 @@ const TopUp = () => { const [redemptionCode, setRedemptionCode] = useState(''); const [topUpLink, setTopUpLink] = useState(''); const [userQuota, setUserQuota] = useState(0); - const [userGroup, setUserGroup] = useState(''); const [isSubmitting, setIsSubmitting] = useState(false); - // 升级用户组 - const updateUserGroupIfNecessary = async (quota) => { - if (userGroup === 'vip') return; // 添加这一行 - - if (quota >= 5*500000) { - try { - const res = await API.post('/api/user/manage', { - username: localStorage.getItem('username'), - newGroup: 'vip' - }); - const { success, message } = res.data; - if (success) { - showSuccess('已成功升级为 VIP 会员!'); - } else { - showError('请右下角联系客服'); - } - } catch (err) { - showError('请右下角联系客服'); - } - } - }; - const topUp = async () => { if (redemptionCode === '') { showInfo('请输入充值码!') @@ -42,12 +19,15 @@ const TopUp = () => { const res = await API.post('/api/user/topup', { key: redemptionCode }); - const { success, message, data } = res.data; + const { success, message, data ,upgradedToVIP } = res.data; if (success) { - showSuccess('充值成功!'); + if (upgradedToVIP) { // 如果用户成功升级为 VIP + showSuccess('充值成功,升级为 VIP 会员'); + } else { + showSuccess('充值成功'); + } setUserQuota((quota) => { const newQuota = quota + data; - updateUserGroupIfNecessary(newQuota); return newQuota; }); setRedemptionCode(''); @@ -55,7 +35,7 @@ const TopUp = () => { showError(message); } } catch (err) { - showError('请右下角联系客服'); + showError('失败,请右下角联系客服'); } finally { setIsSubmitting(false); } @@ -74,7 +54,7 @@ const TopUp = () => { const { success, message, data } = res.data; if (success) { setUserQuota(data.quota); - setUserGroup(data.group); // 添加这一行 + } else { showError(message); }