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 }