|
|
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
|
|
|
}
|