feat(admin): 添加模型算力点配置和扣费功能

- 在管理后台设置页面添加模型算力点配置表格
- 实现后端AI接口调用时按模型扣除用户算力点
- 添加算力点余额检查和不足时的错误处理
- 在credit_logs中记录AI调用消费流水
- 更新前端远程调用后刷新用户信息显示
- 文档中补充模型算力点配置说明
This commit is contained in:
HouYunFei
2026-05-25 14:01:04 +08:00
parent 392bd49d8c
commit 2181b3b885
14 changed files with 262 additions and 18 deletions
+27 -3
View File
@@ -54,7 +54,7 @@ func proxyAIGetRequest(w http.ResponseWriter, r *http.Request, path string) {
return
}
request.Header.Set("Authorization", "Bearer "+channel.APIKey)
copyAIResponse(w, request)
copyAIResponse(w, request, nil)
}
func proxyAIRequest(w http.ResponseWriter, r *http.Request, path string) {
@@ -64,6 +64,21 @@ func proxyAIRequest(w http.ResponseWriter, r *http.Request, path string) {
Fail(w, "AI 接口请求失败")
return
}
user, ok := service.UserFromContext(r.Context())
if !ok {
Fail(w, "未登录或权限不足")
return
}
credits, err := service.ModelCost(modelName)
if err != nil {
log.Printf("AI proxy read model cost failed: model=%s err=%v", modelName, err)
Fail(w, "AI 接口请求失败")
return
}
if err := service.EnsureUserCredits(user.ID, credits); err != nil {
FailError(w, err)
return
}
channel, err := service.SelectModelChannel(modelName)
if err != nil {
log.Printf("AI proxy select channel failed: model=%s err=%v", modelName, err)
@@ -80,10 +95,12 @@ func proxyAIRequest(w http.ResponseWriter, r *http.Request, path string) {
if contentType != "" {
request.Header.Set("Content-Type", contentType)
}
copyAIResponse(w, request)
copyAIResponse(w, request, func() error {
return service.ConsumeUserCredits(user.ID, modelName, credits, path)
})
}
func copyAIResponse(w http.ResponseWriter, request *http.Request) {
func copyAIResponse(w http.ResponseWriter, request *http.Request, beforeWrite func() error) {
response, err := http.DefaultClient.Do(request)
if err != nil {
log.Printf("AI proxy request failed: url=%s err=%v", request.URL.String(), err)
@@ -98,6 +115,13 @@ func copyAIResponse(w http.ResponseWriter, request *http.Request) {
Fail(w, "AI 接口请求失败")
return
}
if beforeWrite != nil {
if err := beforeWrite(); err != nil {
log.Printf("AI proxy before write failed: url=%s err=%v", request.URL.String(), err)
FailError(w, err)
return
}
}
for key, values := range response.Header {
if strings.EqualFold(key, "Content-Length") {