feat: 限制用户单日提问次数
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package plant
|
package plant
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"sundynix-go/global"
|
"sundynix-go/global"
|
||||||
"sundynix-go/model/commom/response"
|
"sundynix-go/model/commom/response"
|
||||||
"sundynix-go/utils/auth"
|
"sundynix-go/utils/auth"
|
||||||
@@ -24,7 +25,19 @@ func (a *AiChatApi) ChatStreamPlant(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSE Headers(微信小程序通过 enableChunked: true 配合实现打字机效果)
|
userId := auth.GetUserId(c)
|
||||||
|
|
||||||
|
// ── 每日用量检查 ──
|
||||||
|
cfg, cfgErr := sysAiConfigService.GetActiveAiConfig()
|
||||||
|
if cfgErr == nil && cfg.DailyQueryLimit > 0 {
|
||||||
|
todayCount, _ := chatHistoryService.GetTodayCount(userId)
|
||||||
|
if todayCount >= int64(cfg.DailyQueryLimit) {
|
||||||
|
response.FailWithMsg(fmt.Sprintf("今日问答次数已达上限(%d次),明天再来吧", cfg.DailyQueryLimit), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SSE Headers
|
||||||
w := c.Writer
|
w := c.Writer
|
||||||
header := w.Header()
|
header := w.Header()
|
||||||
header.Set("Content-Type", "text/event-stream")
|
header.Set("Content-Type", "text/event-stream")
|
||||||
@@ -46,14 +59,12 @@ func (a *AiChatApi) ChatStreamPlant(c *gin.Context) {
|
|||||||
_, _ = w.WriteString("data: [ERROR]" + err.Error() + "\n\n")
|
_, _ = w.WriteString("data: [ERROR]" + err.Error() + "\n\n")
|
||||||
w.Flush()
|
w.Flush()
|
||||||
} else {
|
} else {
|
||||||
// 流结束标志
|
|
||||||
_, _ = w.WriteString("data: [DONE]\n\n")
|
_, _ = w.WriteString("data: [DONE]\n\n")
|
||||||
w.Flush()
|
w.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 异步保存问答历史
|
// 异步保存问答历史
|
||||||
if fullAnswer != "" {
|
if fullAnswer != "" {
|
||||||
userId := auth.GetUserId(c)
|
|
||||||
go func() {
|
go func() {
|
||||||
if saveErr := chatHistoryService.SaveHistory(userId, query, fullAnswer); saveErr != nil {
|
if saveErr := chatHistoryService.SaveHistory(userId, query, fullAnswer); saveErr != nil {
|
||||||
global.Logger.Error("Save chat history failed", zap.Error(saveErr))
|
global.Logger.Error("Save chat history failed", zap.Error(saveErr))
|
||||||
@@ -138,6 +149,34 @@ func (a *AiChatApi) ClearChatHistory(c *gin.Context) {
|
|||||||
response.OkWithMsg("已清空", c)
|
response.OkWithMsg("已清空", c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetChatQuota 获取当前用户今日剩余问答额度
|
||||||
|
// @Tags Plant-AiChat
|
||||||
|
// @Router /plant/chat/quota [get]
|
||||||
|
func (a *AiChatApi) GetChatQuota(c *gin.Context) {
|
||||||
|
userId := auth.GetUserId(c)
|
||||||
|
todayCount, _ := chatHistoryService.GetTodayCount(userId)
|
||||||
|
|
||||||
|
limit := 0 // 0 = unlimited
|
||||||
|
cfg, err := sysAiConfigService.GetActiveAiConfig()
|
||||||
|
if err == nil && cfg.DailyQueryLimit > 0 {
|
||||||
|
limit = cfg.DailyQueryLimit
|
||||||
|
}
|
||||||
|
|
||||||
|
remaining := -1 // -1 means unlimited
|
||||||
|
if limit > 0 {
|
||||||
|
remaining = limit - int(todayCount)
|
||||||
|
if remaining < 0 {
|
||||||
|
remaining = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response.OkWithData(map[string]interface{}{
|
||||||
|
"used": todayCount,
|
||||||
|
"limit": limit,
|
||||||
|
"remaining": remaining,
|
||||||
|
}, c)
|
||||||
|
}
|
||||||
|
|
||||||
func parseInt(s string) int {
|
func parseInt(s string) int {
|
||||||
n := 0
|
n := 0
|
||||||
for _, c := range s {
|
for _, c := range s {
|
||||||
|
|||||||
@@ -31,4 +31,5 @@ var (
|
|||||||
exchangeService = service.GroupApp.PlantServiceGroup.ExchangeService
|
exchangeService = service.GroupApp.PlantServiceGroup.ExchangeService
|
||||||
aiRagService = service.GroupApp.PlantServiceGroup.AiRagService
|
aiRagService = service.GroupApp.PlantServiceGroup.AiRagService
|
||||||
chatHistoryService = service.GroupApp.PlantServiceGroup.AiChatHistoryService
|
chatHistoryService = service.GroupApp.PlantServiceGroup.AiChatHistoryService
|
||||||
|
sysAiConfigService = service.GroupApp.SystemServiceGroup.SysAiConfigService
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -20,4 +20,6 @@ type SysAiConfig struct {
|
|||||||
EmbeddingApiUrl string `gorm:"column:embedding_api_url;type:varchar(255);comment:Embedding模型接口地址" json:"embeddingApiUrl" form:"embeddingApiUrl"`
|
EmbeddingApiUrl string `gorm:"column:embedding_api_url;type:varchar(255);comment:Embedding模型接口地址" json:"embeddingApiUrl" form:"embeddingApiUrl"`
|
||||||
EmbeddingApiKey string `gorm:"column:embedding_api_key;type:varchar(255);comment:Embedding模型ApiKey" json:"embeddingApiKey" form:"embeddingApiKey"`
|
EmbeddingApiKey string `gorm:"column:embedding_api_key;type:varchar(255);comment:Embedding模型ApiKey" json:"embeddingApiKey" form:"embeddingApiKey"`
|
||||||
EmbeddingModelName string `gorm:"column:embedding_model_name;type:varchar(100);comment:Embedding模型名称" json:"embeddingModelName" form:"embeddingModelName"`
|
EmbeddingModelName string `gorm:"column:embedding_model_name;type:varchar(100);comment:Embedding模型名称" json:"embeddingModelName" form:"embeddingModelName"`
|
||||||
|
// 用量限制
|
||||||
|
DailyQueryLimit int `gorm:"column:daily_query_limit;type:int;default:20;comment:每用户每日问答上限(0=不限)" json:"dailyQueryLimit" form:"dailyQueryLimit"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ func (s *AiChatRouter) InitAiChatRouter(Router *gin.RouterGroup) (R gin.IRoutes)
|
|||||||
aiChatRouter.GET("history", aiChatApi.GetChatHistory) // 问答历史列表
|
aiChatRouter.GET("history", aiChatApi.GetChatHistory) // 问答历史列表
|
||||||
aiChatRouter.POST("history/delete", aiChatApi.DeleteChatHistory) // 删除单条
|
aiChatRouter.POST("history/delete", aiChatApi.DeleteChatHistory) // 删除单条
|
||||||
aiChatRouter.POST("history/clear", aiChatApi.ClearChatHistory) // 清空历史
|
aiChatRouter.POST("history/clear", aiChatApi.ClearChatHistory) // 清空历史
|
||||||
|
aiChatRouter.GET("quota", aiChatApi.GetChatQuota) // 今日剩余额度
|
||||||
}
|
}
|
||||||
return aiChatRouter
|
return aiChatRouter
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ func (s *SysAiConfigRouter) InitSysAiConfigRouter(Router *gin.RouterGroup) (R gi
|
|||||||
sysAiConfigApi := sysAiConfigApi
|
sysAiConfigApi := sysAiConfigApi
|
||||||
{
|
{
|
||||||
sysAiConfigRouter.POST("create", sysAiConfigApi.CreateAiConfig) // 创建配置
|
sysAiConfigRouter.POST("create", sysAiConfigApi.CreateAiConfig) // 创建配置
|
||||||
sysAiConfigRouter.PUT("update", sysAiConfigApi.UpdateAiConfig) // 更新配置
|
sysAiConfigRouter.POST("update", sysAiConfigApi.UpdateAiConfig) // 更新配置
|
||||||
sysAiConfigRouter.POST("setActive", sysAiConfigApi.SetActive) // 设置激活状态
|
sysAiConfigRouter.POST("setActive", sysAiConfigApi.SetActive) // 设置激活状态
|
||||||
sysAiConfigRouter.GET("list", sysAiConfigApi.GetList) // 获取列表
|
sysAiConfigRouter.GET("list", sysAiConfigApi.GetList) // 获取列表
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,3 +46,12 @@ func (s *AiChatHistoryService) DeleteHistory(userId, id string) error {
|
|||||||
func (s *AiChatHistoryService) ClearHistory(userId string) error {
|
func (s *AiChatHistoryService) ClearHistory(userId string) error {
|
||||||
return global.DB.Where("user_id = ?", userId).Delete(&plantModel.AiChatHistory{}).Error
|
return global.DB.Where("user_id = ?", userId).Delete(&plantModel.AiChatHistory{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetTodayCount 获取用户今日问答数量
|
||||||
|
func (s *AiChatHistoryService) GetTodayCount(userId string) (int64, error) {
|
||||||
|
var count int64
|
||||||
|
err := global.DB.Model(&plantModel.AiChatHistory{}).
|
||||||
|
Where("user_id = ? AND DATE(created_at) = CURDATE()", userId).
|
||||||
|
Count(&count).Error
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user