Explorar o código

feat: record used quota & request count (close #102, #165)

JustSong %!s(int64=2) %!d(string=hai) anos
pai
achega
760183a970
Modificáronse 4 ficheiros con 43 adicións e 12 borrados
  1. 1 0
      controller/relay.go
  2. 14 0
      model/user.go
  3. 7 3
      web/src/components/UsersTable.js
  4. 21 9
      web/src/helpers/render.js

+ 1 - 0
controller/relay.go

@@ -261,6 +261,7 @@ func relayHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode {
 			}
 			}
 			userId := c.GetInt("id")
 			userId := c.GetInt("id")
 			model.RecordLog(userId, model.LogTypeConsume, fmt.Sprintf("使用模型 %s 消耗 %d 点额度(模型倍率 %.2f,分组倍率 %.2f)", textRequest.Model, quota, modelRatio, groupRatio))
 			model.RecordLog(userId, model.LogTypeConsume, fmt.Sprintf("使用模型 %s 消耗 %d 点额度(模型倍率 %.2f,分组倍率 %.2f)", textRequest.Model, quota, modelRatio, groupRatio))
+			model.UpdateUserUsedQuotaAndRequestCount(userId, quota)
 		}
 		}
 	}()
 	}()
 
 

+ 14 - 0
model/user.go

@@ -23,6 +23,8 @@ type User struct {
 	VerificationCode string `json:"verification_code" gorm:"-:all"`                                    // this field is only for Email verification, don't save it to database!
 	VerificationCode string `json:"verification_code" gorm:"-:all"`                                    // this field is only for Email verification, don't save it to database!
 	AccessToken      string `json:"access_token" gorm:"type:char(32);column:access_token;uniqueIndex"` // this token is for system management
 	AccessToken      string `json:"access_token" gorm:"type:char(32);column:access_token;uniqueIndex"` // this token is for system management
 	Quota            int    `json:"quota" gorm:"type:int;default:0"`
 	Quota            int    `json:"quota" gorm:"type:int;default:0"`
+	UsedQuota        int    `json:"used_quota" gorm:"type:int;default:0;column:used_quota"` // used quota
+	RequestCount     int    `json:"request_count" gorm:"type:int;default:0;"`               // request number
 	Group            string `json:"group" gorm:"type:varchar(32);default:'default'"`
 	Group            string `json:"group" gorm:"type:varchar(32);default:'default'"`
 }
 }
 
 
@@ -262,3 +264,15 @@ func GetRootUserEmail() (email string) {
 	DB.Model(&User{}).Where("role = ?", common.RoleRootUser).Select("email").Find(&email)
 	DB.Model(&User{}).Where("role = ?", common.RoleRootUser).Select("email").Find(&email)
 	return email
 	return email
 }
 }
+
+func UpdateUserUsedQuotaAndRequestCount(id int, quota int) {
+	err := DB.Model(&User{}).Where("id = ?", id).Updates(
+		map[string]interface{}{
+			"used_quota":    gorm.Expr("used_quota + ?", quota),
+			"request_count": gorm.Expr("request_count + ?", 1),
+		},
+	).Error
+	if err != nil {
+		common.SysError("Failed to update user used quota and request count: " + err.Error())
+	}
+}

+ 7 - 3
web/src/components/UsersTable.js

@@ -4,7 +4,7 @@ import { Link } from 'react-router-dom';
 import { API, showError, showSuccess } from '../helpers';
 import { API, showError, showSuccess } from '../helpers';
 
 
 import { ITEMS_PER_PAGE } from '../constants';
 import { ITEMS_PER_PAGE } from '../constants';
-import { renderGroup, renderText } from '../helpers/render';
+import { renderGroup, renderNumber, renderText } from '../helpers/render';
 
 
 function renderRole(role) {
 function renderRole(role) {
   switch (role) {
   switch (role) {
@@ -197,7 +197,7 @@ const UsersTable = () => {
                 sortUser('quota');
                 sortUser('quota');
               }}
               }}
             >
             >
-              剩余额度
+              统计信息
             </Table.HeaderCell>
             </Table.HeaderCell>
             <Table.HeaderCell
             <Table.HeaderCell
               style={{ cursor: 'pointer' }}
               style={{ cursor: 'pointer' }}
@@ -241,7 +241,11 @@ const UsersTable = () => {
                   </Table.Cell>
                   </Table.Cell>
                   <Table.Cell>{renderGroup(user.group)}</Table.Cell>
                   <Table.Cell>{renderGroup(user.group)}</Table.Cell>
                   <Table.Cell>{user.email ? renderText(user.email, 30) : '无'}</Table.Cell>
                   <Table.Cell>{user.email ? renderText(user.email, 30) : '无'}</Table.Cell>
-                  <Table.Cell>{user.quota}</Table.Cell>
+                  <Table.Cell>
+                    <Popup content='剩余额度' trigger={<Label>{renderNumber(user.quota)}</Label>} />
+                    <Popup content='已用额度' trigger={<Label>{renderNumber(user.used_quota)}</Label>} />
+                    <Popup content='请求次数' trigger={<Label>{renderNumber(user.request_count)}</Label>} />
+                  </Table.Cell>
                   <Table.Cell>{renderRole(user.role)}</Table.Cell>
                   <Table.Cell>{renderRole(user.role)}</Table.Cell>
                   <Table.Cell>{renderStatus(user.status)}</Table.Cell>
                   <Table.Cell>{renderStatus(user.status)}</Table.Cell>
                   <Table.Cell>
                   <Table.Cell>

+ 21 - 9
web/src/helpers/render.js

@@ -8,19 +8,31 @@ export function renderText(text, limit) {
 }
 }
 
 
 export function renderGroup(group) {
 export function renderGroup(group) {
-  if (group === "") {
-    return <Label>default</Label>
+  if (group === '') {
+    return <Label>default</Label>;
   }
   }
-  let groups = group.split(",");
+  let groups = group.split(',');
   groups.sort();
   groups.sort();
   return <>
   return <>
     {groups.map((group) => {
     {groups.map((group) => {
-      if (group === "vip" || group === "pro") {
-        return <Label color='yellow'>{group}</Label>
-      } else if (group === "svip" || group === "premium") {
-        return <Label color='red'>{group}</Label>
+      if (group === 'vip' || group === 'pro') {
+        return <Label color='yellow'>{group}</Label>;
+      } else if (group === 'svip' || group === 'premium') {
+        return <Label color='red'>{group}</Label>;
       }
       }
-      return <Label>{group}</Label>
+      return <Label>{group}</Label>;
     })}
     })}
-  </>
+  </>;
+}
+
+export function renderNumber(num) {
+  if (num >= 1000000000) {
+    return (num / 1000000000).toFixed(1) + 'B';
+  } else if (num >= 1000000) {
+    return (num / 1000000).toFixed(1) + 'M';
+  } else if (num >= 10000) {
+    return (num / 1000).toFixed(1) + 'k';
+  } else {
+    return num;
+  }
 }
 }