wangkaiyue 2 роки тому
батько
коміт
17fdf602d5

+ 7 - 3
api/v1/aiChatApi.go

@@ -17,13 +17,17 @@ type History struct {
 	Content    string `json:"content" dc:"内容"`
 	Type       int    `json:"type" dc:"1:用户提问 2:智能助手回复"`
 	Useful     int    `json:"useful" dc:"1:有用 -1:无用 0:暂无评价"`
+	Actions    int    `json:"actions" dc:"是否有功能按钮 1:有 0:没有"`
 	CreateTime int64  `json:"create_time" dc:"聊天时间"`
 }
 
 type ChatHistoryRes struct {
-	Data      []History `json:"data"`
-	ErrorMsg  string    `json:"error_msg"`
-	ErrorCode int64     `json:"error_code"`
+	Data struct {
+		HasMore bool      `json:"hasMore" dc:"是否有下一页"`
+		List    []History `json:"list" dc:"聊天历史"`
+	} `json:"data"`
+	ErrorMsg  string `json:"error_msg"`
+	ErrorCode int64  `json:"error_code"`
 }
 
 // EvaluateReq 评价

+ 4 - 2
internal/controller/chatHistory.go

@@ -25,19 +25,21 @@ func (c *cChatHistory) Method(ctx context.Context, req *v1.ChatHistoryReq) (res
 	if req.PrevId != "" {
 		req.PrevId = encrypt.SE.Decode4Hex(req.PrevId) //id解密
 	}
-	history, err := model.ChatHistroy.GetMessage(model.SessionCtx.Get(ctx).JSession.AccountId, req.PageNum, req.PageSize, req.PrevId)
+	history, hasNext, err := model.ChatHistroy.GetMessage(model.SessionCtx.Get(ctx).JSession.AccountId, req.PageNum, req.PageSize, req.PrevId)
 	if err != nil {
 		glog.Error(ctx, "%d查询聊天记录异常,error:%s", model.SessionCtx.Get(ctx).JSession.AccountId, err)
 		res.ErrorCode = -1
 		res.ErrorMsg = "数据查询异常"
 		return
 	}
+	res.Data.HasMore = hasNext
 	for _, v := range history {
-		res.Data = append(res.Data, v1.History{
+		res.Data.List = append(res.Data.List, v1.History{
 			Id:         encrypt.SE.Encode2Hex(v.Id),
 			Content:    v.Content,
 			Type:       v.Type,
 			Useful:     v.Useful,
+			Actions:    v.Actions,
 			CreateTime: v.CreateTime.Unix(),
 		})
 	}

+ 0 - 4
internal/controller/chatWs.go

@@ -35,10 +35,6 @@ var ChatWs = func(r *ghttp.Request) {
 			_ = ws.Close()
 			return
 		}
-		if model.ChatLimit.GetBucket(wsChat.Ctx, session.AccountId).TakeAvailable(1) == 0 {
-			_ = ws.WriteJSON(g.Map{"error_code": -1, "error_msg": g.Cfg().MustGet(wsChat.Ctx, "limit.exceedMsg").String(), "data": nil})
-			continue
-		}
 		// 处理ws
 		wsChat.Handle(ws, msg)
 	}

+ 1 - 1
internal/controller/usuallyProblem.go

@@ -20,6 +20,6 @@ func (c *cUsuallyProblem) Method(ctx context.Context, req *v1.UsuallyProblemReq)
 		return nil, fmt.Errorf("无用户身份")
 	}
 	var list []string
-	list, err = model.Question.GetUsuallyProblem(ctx, model.GetScenario(req.Href), g.Config().MustGet(ctx, "chat.guessQuestion", 5).Int())
+	list, err = model.Question.GetUsuallyProblem(ctx, model.GetScenario(req.Href), g.Config().MustGet(ctx, "chat.usuallyProblem", 5).Int())
 	return &v1.QuestionRes{Data: list}, nil
 }

+ 23 - 0
internal/model/blackList.go

@@ -0,0 +1,23 @@
+package model
+
+import (
+	"context"
+	"fmt"
+	"github.com/gogf/gf/v2/frame/g"
+)
+
+var (
+	UserBlackList = &cUserBlackList{}
+)
+
+type cUserBlackList struct {
+}
+
+// CheckBlackList 校验黑名单
+func (l *cUserBlackList) CheckBlackList(ctx context.Context, accountId int64) bool {
+	v, e := g.Redis().Get(ctx, fmt.Sprintf("aiChat_black_%d", accountId))
+	if e != nil {
+		return false
+	}
+	return !v.IsEmpty()
+}

+ 29 - 6
internal/model/chatApi.go

@@ -13,6 +13,16 @@ type GPTReq struct {
 	Identity string `json:"identity"`
 }
 
+// SimpleRes 语义服务,响应常用问题
+type SimpleRes struct {
+	Status int `json:"status"`
+	Result struct {
+		Prompt string `json:"prompt"`
+		Answer string `json:"answer"`
+	} `json:"result"`
+}
+
+// GPTRes 业务及扩展聊天响应
 type GPTRes struct {
 	Status   int        `json:"status"`
 	Response string     `json:"response"`
@@ -27,20 +37,33 @@ var (
 type cChatGpt struct {
 }
 
-var (
-	tmp = []string{
-		"smart_潜在客户_smart",
+func (c *cChatGpt) SimpleDo(ctx context.Context, qReq *QuestionReq) (res *SimpleRes, err error) {
+	gReq := GPTReq{
+		BaseQuestion: qReq.BaseQuestion,
+		Identity:     g.Config().MustGet(ctx, "chat.api.identity", "剑鱼chat").String(),
 	}
-)
+	//g.Dump(gReq)
+	var gRes *gclient.Response
+	gRes, err = g.Client().Header(consts.RequestJsonHeader).Post(ctx, g.Config().MustGet(ctx, "chat.api.addr_simple", "").String(), gReq)
+	if err != nil {
+		return nil, err
+	}
+	res = &SimpleRes{}
+	err = gconv.Struct(gRes.ReadAll(), res)
+	if err != nil {
+		return nil, err
+	}
+	return
+}
 
-func (c *cChatGpt) Do(ctx context.Context, qReq *QuestionReq) (res *GPTRes, err error) {
+func (c *cChatGpt) GPTDo(ctx context.Context, qReq *QuestionReq) (res *GPTRes, err error) {
 	gReq := GPTReq{
 		BaseQuestion: qReq.BaseQuestion,
 		Identity:     g.Config().MustGet(ctx, "chat.api.identity", "剑鱼chat").String(),
 	}
 	//g.Dump(gReq)
 	var gRes *gclient.Response
-	gRes, err = g.Client().Header(consts.RequestJsonHeader).Post(ctx, g.Config().MustGet(ctx, "chat.api.addr", "").String(), gReq)
+	gRes, err = g.Client().Header(consts.RequestJsonHeader).Post(ctx, g.Config().MustGet(ctx, "chat.api.addr_answer", "").String(), gReq)
 	if err != nil {
 		return nil, err
 	}

+ 8 - 3
internal/model/chatHistory.go

@@ -25,6 +25,7 @@ type ResHistory struct {
 	Content    string    `json:"content" dc:"内容"`
 	Type       int       `json:"type" dc:"1:用户提问 2:智能助手回复"`
 	Useful     int       `json:"useful" dc:"1:有用 -1:无用 0:暂无评价"`
+	Actions    int       `json:"actions" dc:"1:有功能菜单 0:无功能菜单"`
 	CreateTime time.Time `json:"create_time" dc:"聊天时间"`
 }
 
@@ -32,7 +33,9 @@ type ChatRecord struct {
 	Content    string `json:"content" dc:"内容"`
 	Type       int    `json:"type" dc:"1:用户提问 2:智能助手回复"`
 	Useful     int    `json:"useful" dc:"1:有用 -1:无用 0:暂无评价"`
+	Actions    int    `json:"actions" dc:"1:有功能菜单 0:无功能菜单"`
 	Item       int    `json:"item" dc:"1:常见问题 2:业务意图 3:chatGpt"`
+	AnswerId   int64  `json:"answer_id" dc:"回答问题的id"`
 	Refer      string `json:"refer" dc:"会话来源地址"`
 	PersonId   int64  `json:"person_id" dc:"自然人id"`
 	CreateTime string `json:"create_time" dc:"时间"`
@@ -79,12 +82,12 @@ func (m *cChatHistroy) Save(ctx context.Context, msgs ...*ChatRecord) (id int64)
 }
 
 // GetMessage 查询聊天信息
-func (m *cChatHistroy) GetMessage(userId int64, pageNum, pageSize int, prevId string) (h []ResHistory, err error) {
+func (m *cChatHistroy) GetMessage(userId int64, pageNum, pageSize int, prevId string) (h []ResHistory, hasNext bool, err error) {
 	var hTmp []ResHistory
 	if prevId != "" {
-		err = g.Model("ai_message_history").Where("person_id = ? and id < ? ", userId, prevId).OrderDesc("id").Limit(pageNum*pageSize, (pageNum+1)*pageSize).Scan(&hTmp)
+		err = g.Model("ai_message_history").Where("person_id = ? and id < ? ", userId, prevId).OrderDesc("id").Limit(pageNum*pageSize, pageSize).Scan(&hTmp)
 	} else {
-		err = g.Model("ai_message_history").Where("person_id = ?", userId).OrderDesc("id").Limit(pageNum*pageSize, (pageNum+1)*pageSize).Scan(&hTmp)
+		err = g.Model("ai_message_history").Where("person_id = ?", userId).OrderDesc("id").Limit(pageNum*pageSize, pageSize).Scan(&hTmp)
 	}
 	if err != nil {
 		return
@@ -94,6 +97,8 @@ func (m *cChatHistroy) GetMessage(userId int64, pageNum, pageSize int, prevId st
 	for i := len(hTmp) - 1; i >= 0; i-- {
 		h = append(h, hTmp[i])
 	}
+	count, _ := g.Model("ai_message_history").Where("person_id = ? ", userId).Count()
+	hasNext = count > (pageNum+1)*pageSize
 	return
 }
 

+ 10 - 1
internal/model/question.go

@@ -77,7 +77,16 @@ func (q *cQuestion) getIsbusinessData(ctx context.Context, code string) (bRes *B
 
 // DetailQuestion 问题处理
 func (q *cQuestion) DetailQuestion(ctx context.Context, qRes *QuestionReq) (reply string, from int, err error) {
-	cRes, err := ChatGpt.Do(ctx, qRes)
+	// 语义服务
+	sRes, err := ChatGpt.SimpleDo(ctx, qRes)
+	if err != nil {
+		return "", 0, err
+	}
+	if sRes.Result.Answer != "" {
+		return sRes.Result.Answer, Answer_ChatGPT, nil
+	}
+
+	cRes, err := ChatGpt.GPTDo(ctx, qRes)
 	if err != nil {
 		return "", 0, err
 	}

+ 45 - 26
internal/model/ws.go

@@ -5,12 +5,14 @@ import (
 	"aiChat/utility/fsw"
 	. "app.yhyue.com/moapp/jybase/common"
 	"app.yhyue.com/moapp/jybase/date"
+	"app.yhyue.com/moapp/jybase/encrypt"
 	"context"
 	"fmt"
 	"github.com/gogf/gf/v2/encoding/gjson"
 	"github.com/gogf/gf/v2/frame/g"
 	"github.com/gogf/gf/v2/net/ghttp"
 	"github.com/gogf/gf/v2/os/glog"
+	"github.com/gogf/gf/v2/util/gconv"
 	"time"
 )
 
@@ -35,48 +37,65 @@ func (m *WsChat) Handle(ws *ghttp.WebSocket, msg []byte) {
 		return
 	}
 
-	reply, errMsg := func() (string, error) {
-		// 问题敏感词过滤
-		if fsw.Match(req.Prompt) {
-			return "", fmt.Errorf(g.Cfg().MustGet(m.Ctx, "limit.fswMsg", "您的问题可能包含敏感词汇,请修改后再提问!").String())
+	reply, replyId, errMsg := func() (string, int64, error) {
+		questionId := ChatHistroy.Save(m.Ctx, &ChatRecord{
+			Content:    req.Prompt,
+			Type:       1,
+			Refer:      req.Href,
+			PersonId:   jSession.AccountId,
+			CreateTime: time.Now().Format(date.Date_Full_Layout),
+		})
+		// 校验是否在黑名单,黑名单不返回内容
+		if UserBlackList.CheckBlackList(m.Ctx, jSession.AccountId) {
+			return "", 0, nil
 		}
-		reply, from, err := Question.DetailQuestion(m.Ctx, req)
-		// 回答敏感词过滤
-		reply = fsw.Repl(reply)
-		if err != nil {
-			g.Log().Error(m.Ctx, "问答异常", err)
-			return "", fmt.Errorf("问答异常")
+		var err error
+		reply, from := "", 0
+		errReply := func() string {
+			// 校验问答频率
+			if ChatLimit.GetBucket(m.Ctx, jSession.AccountId).TakeAvailable(1) == 0 {
+				return g.Cfg().MustGet(m.Ctx, "limit.exceedMsg").String()
+			}
+			// 问题敏感词过滤
+			if fsw.Match(req.Prompt) {
+				return g.Cfg().MustGet(m.Ctx, "limit.fswMsg", "您的问题可能包含敏感词汇,请修改后再提问!").String()
+			}
+			return ""
+		}()
+		if errReply != "" {
+			reply, from = errReply, -1
+		} else {
+			reply, from, err = Question.DetailQuestion(m.Ctx, req)
+			if err != nil {
+				g.Log().Error(m.Ctx, "问答异常", err)
+				return "", 0, fmt.Errorf("问答异常")
+			}
+			// 回答敏感词过滤
+			reply = fsw.Repl(reply)
 		}
 
 		// 记录问答
-		id := ChatHistroy.Save(m.Ctx, &ChatRecord{
+		replyId := ChatHistroy.Save(m.Ctx, &ChatRecord{
 			Content:    reply,
 			Type:       2,
-			Refer:      req.Href,
+			Actions:    gconv.Int(If(errReply == "", 1, 0)),
+			AnswerId:   questionId,
 			PersonId:   jSession.AccountId,
 			Item:       from,
 			CreateTime: time.Now().Format(date.Date_Full_Layout),
-		}, &ChatRecord{
-			Content:    req.Prompt,
-			Type:       1,
-			Refer:      req.Href,
-			PersonId:   jSession.AccountId,
-			CreateTime: time.Now().Format(date.Date_Full_Layout),
 		})
-		if id <= 0 {
-			g.Log().Error(m.Ctx, "数据存储异常", err)
-			return "", fmt.Errorf("数据存储异常")
+		if replyId <= 0 {
+			g.Log().Error(m.Ctx, "问答存储存储异常")
 		}
-
-		if err = utility.ResourcePowerDeduct(m.Ctx, jSession.AccountId, jSession.EntAccountId, fmt.Sprintf("%d", id)); err != nil {
+		if err = utility.ResourcePowerDeduct(m.Ctx, jSession.AccountId, jSession.EntAccountId, fmt.Sprintf("%d", replyId)); err != nil {
 			g.Log().Error(m.Ctx, "扣减异常", err)
-			return "", err
 		}
-		return reply, nil
+		return reply, replyId, nil
 	}()
+
 	if errMsg != nil {
 		_ = ws.WriteJSON(g.Map{"error_code": -1, "error_msg": errMsg.Error(), "data": nil})
 	} else {
-		_ = ws.WriteJSON(g.Map{"error_code": 0, "error_msg": "", "data": reply})
+		_ = ws.WriteJSON(g.Map{"error_code": 0, "error_msg": "", "data": g.Map{"id": encrypt.SE.Encode2Hex(fmt.Sprintf("%d", replyId)), "reply": reply}})
 	}
 }

+ 2 - 2
manifest/config/config.yaml

@@ -15,10 +15,10 @@ server:
 chat:
   appId: "10000"
   usuallyProblem: 5 # 常见问题返回数量
-  guessQuestion: 5 # 猜你想问返回数量
   resourceCode: "ai_helper" # 资源扣减
   api: #ChatGpt 配置
-    addr: "http://192.168.3.109:12001"
+    addr_simple: "http://192.168.3.109:8880/search" # 语义服务
+    addr_answer: "http://192.168.3.109:12001" # ai回答接口
     identity: "剑鱼chat"
   businessRpc: "192.168.3.149:5051" #业务逻辑rpc地址