You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

249 lines
5.3 KiB
Go

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package campaignuser
import (
"joybuywork/internal/app"
"joybuywork/internal/consts/ckey"
"joybuywork/internal/repository/cache"
"joybuywork/internal/repository/dbcheck"
"joybuywork/internal/repository/finder"
"joybuywork/internal/repository/model"
"joybuywork/internal/repository/mtype"
"joybuywork/internal/repository/withdefault"
"joybuywork/internal/service/provider"
"joybuywork/internal/utils/lockdo"
"joybuywork/internal/utils/logic"
"joybuywork/internal/utils/timez"
pb "joybuywork/proto/go_proto/w_campaign_user"
"joybuywork/proto/go_proto/workerr"
"fmt"
chatpb "gits.branchcn.com/backend/deepai/proto/go_proto/ai_chat"
)
const (
chatOk = iota // 正常
chatTokenLimit // token 不足
chatDayLimit // 每日上限
)
type Chat struct {
logic.Logic
Req *pb.ChatReq
Res *pb.ChatRes
tz *provider.CampaignTZ
chatOk bool
lastMsg *model.FissionChatMsg
}
func (s *Chat) Run() error {
return logic.Run(s)
}
func (s *Chat) Exec() (err error) {
cpn, user, err := s.user()
if err != nil {
return
}
chat, err := s.chat(user)
if err != nil {
return
}
err = s.msg(user, cpn, chat)
if err != nil {
return
}
return
}
func (s *Chat) user() (cpn *model.FissionCampaign, user *model.FissionUser, err error) {
cpn, err = finder.Campaign(s.Ctx, s.Req.GetCampaignId())
if err != nil {
err = s.BE().CacheError(err)
return
}
if cpn == nil || cpn.IsDel == mtype.Yes {
err = workerr.NotFound("not found cpn")
return
}
user, err = finder.FissionUser(s.Ctx, cpn.ID, s.Req.GetUserId())
if err != nil {
err = s.BE().CacheError(err)
return
}
s.tz = provider.NewCampaignTZ(s.Ctx, cpn, user.LangMark)
return
}
func (s *Chat) chat(user *model.FissionUser) (chat *model.FissionChat, err error) {
chat, err = cache.NewFissionChat(s.Ctx).FindByUniq(user.CampaignID, user.ID)
if err != nil {
err = s.BE().CacheError(err)
return
}
return
}
func (s *Chat) msg(user *model.FissionUser, cpn *model.FissionCampaign, chat *model.FissionChat) (err error) {
status, output, err := s._status(user, cpn, chat)
if err != nil {
return
}
var inputMsg = withdefault.FissionChatMsg(&model.FissionChatMsg{
CampaignID: cpn.ID,
UserID: user.ID,
ChatID: chat.ID,
Source: mtype.FissionChatMsgSourceInput,
MsgType: mtype.FissionChatMsgTypeText,
}, mtype.ChatMsgVal{
Text: s.Req.GetInput(),
})
var content string
var msgType int8
var source string
switch status {
case chatOk:
content = output
source = mtype.FissionChatMsgSourceOutput
msgType = mtype.FissionChatMsgTypeText
case chatTokenLimit:
content = "chat:token_limit"
source = mtype.FissionChatMsgSourceOutput
msgType = mtype.FissionChatMsgTypeFix
case chatDayLimit:
content = "chat:day_limit"
source = mtype.FissionChatMsgSourceOutput
msgType = mtype.FissionChatMsgTypeFix
}
var outputMsg = withdefault.FissionChatMsg(&model.FissionChatMsg{
CampaignID: cpn.ID,
UserID: user.ID,
ChatID: chat.ID,
Source: source,
MsgType: msgType,
}, mtype.ChatMsgVal{
Text: content,
})
m := s.GetDao().FissionChatMsg
if err = m.WithContext(s.Ctx).Create(inputMsg, outputMsg); err != nil {
err = s.BE().MysqlError(err)
return
}
s.chatOk = true
s.Res.Status = status
s.Res.Source = source
s.Res.MsgType = int32(msgType)
s.Res.Content = content
s.Res.MsgId = outputMsg.ID
return
}
func (s *Chat) _status(user *model.FissionUser, cpn *model.FissionCampaign, chat *model.FissionChat) (status int32, output string, err error) {
limit, err := s._beLimit(user, cpn)
if err != nil {
return
}
if limit {
status = chatDayLimit
return
}
m := s.GetDao().FissionChatMsg
last, err := m.WithContext(s.Ctx).Where(
m.ChatID.Eq(chat.ID),
m.Source.Eq(mtype.FissionChatMsgSourceOutput),
m.MsgType.Eq(mtype.FissionChatMsgTypeText),
).Last()
if err = dbcheck.DbError(err); err != nil {
err = s.BE().MysqlError(err)
return
}
var assistant string
if last != nil {
val := last.GetVal()
assistant = val.Text
}
msg, err := app.NewDeepAiChat().Npc(s.Ctx, &chatpb.NpcReq{
ProjectId: cpn.ProjectID,
SourceType: 1,
SourceId: cpn.ID,
Assistant: assistant,
Character: fmt.Sprintf("%s,用%s回复,只回答与谈心相关的问题,可以提供一点建议,回复会带点情绪反馈,回复语句尽量在255个字符以内", chat.Character, mtype.LangChineseTitle[user.LangMark]),
Input: s.Req.GetInput(),
UserId: user.SubUserID,
})
if err != nil {
return
}
if msg.Status == 1 {
status = chatTokenLimit
return
}
output = msg.Output
return
}
func (s *Chat) _beLimit(user *model.FissionUser, cpn *model.FissionCampaign) (yes bool, err error) {
conf, err := cache.NewCampaignChat(s.Ctx).FindByCampaignId(cpn.ID)
if err != nil {
err = s.BE().CacheError(err)
return
}
if conf.DayLimit == 0 {
return
}
day, start, end := timez.GMTStartAndEndTs(s.tz.GetTz())
dayKey := ckey.FissionChatDayTotal(user.ID, day)
dayNum, err := lockdo.Int64(s.Ctx, dayKey, func() (num int64, err error) {
m := s.GetDao().FissionChatMsg
return m.WithContext(s.Ctx).Where(m.UserID.Eq(user.ID), m.CreatedAt.Between(start, end)).Count()
})
if err != nil {
err = s.BE().CacheError(err)
return
}
if dayNum >= int64(conf.DayLimit) {
return true, nil
}
s.AddAfterRun(func() (err error) {
if !s.chatOk {
return
}
return app.Redis.Cache.Incr(s.Ctx, dayKey).Err()
})
return
}