Files
ai-agent/internal/services/agent_profile_service.go
T
t 2bbf42b741 refactor(auth): delegate access control to be-system
Remove Agent Desk users, roles, login sessions, tokens, and local permission persistence. Expose the backend as an embeddable ai-agent module with host-provided subject lookup and operation authorization callbacks, and complete the frontend/backend repository split.
2026-08-21 00:41:07 +08:00

208 lines
6.7 KiB
Go

package services
import (
"code.tczkiot.com/wlw/ai-agent/internal/models"
"code.tczkiot.com/wlw/ai-agent/internal/pkg/dto"
"code.tczkiot.com/wlw/ai-agent/internal/pkg/dto/request"
"code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
"code.tczkiot.com/wlw/ai-agent/internal/pkg/errorsx"
"code.tczkiot.com/wlw/ai-agent/internal/pkg/utils"
"code.tczkiot.com/wlw/ai-agent/internal/repositories"
"strings"
"time"
"code.tczkiot.com/wlw/ai-agent/internal/pkg/httpx/params"
"github.com/mlogclub/simple/sqls"
)
var AgentProfileService = newAgentProfileService()
func newAgentProfileService() *agentProfileService {
return &agentProfileService{}
}
type agentProfileService struct {
}
func (s *agentProfileService) Get(id int64) *models.AgentProfile {
return repositories.AgentProfileRepository.Get(sqls.DB(), id)
}
func (s *agentProfileService) Take(where ...interface{}) *models.AgentProfile {
return repositories.AgentProfileRepository.Take(sqls.DB(), where...)
}
func (s *agentProfileService) Find(cnd *sqls.Cnd) []models.AgentProfile {
return repositories.AgentProfileRepository.Find(sqls.DB(), cnd)
}
func (s *agentProfileService) FindOne(cnd *sqls.Cnd) *models.AgentProfile {
return repositories.AgentProfileRepository.FindOne(sqls.DB(), cnd)
}
func (s *agentProfileService) FindPageByParams(params *params.QueryParams) (list []models.AgentProfile, paging *sqls.Paging) {
return repositories.AgentProfileRepository.FindPageByParams(sqls.DB(), params)
}
func (s *agentProfileService) FindPageByCnd(cnd *sqls.Cnd) (list []models.AgentProfile, paging *sqls.Paging) {
return repositories.AgentProfileRepository.FindPageByCnd(sqls.DB(), cnd)
}
func (s *agentProfileService) Count(cnd *sqls.Cnd) int64 {
return repositories.AgentProfileRepository.Count(sqls.DB(), cnd)
}
func (s *agentProfileService) GetByUserID(userID int64) *models.AgentProfile {
if userID <= 0 {
return nil
}
return repositories.AgentProfileRepository.FindOne(sqls.DB(), sqls.NewCnd().Eq("user_id", userID))
}
func (s *agentProfileService) GetUserIDsByTeamID(teamID int64) []int64 {
if teamID <= 0 {
return nil
}
list := s.Find(sqls.NewCnd().Eq("team_id", teamID))
if len(list) == 0 {
return nil
}
result := make([]int64, 0, len(list))
for _, item := range list {
if item.UserID > 0 {
result = append(result, item.UserID)
}
}
return result
}
// GetDispatchAgents 获取可用于分配会话的客服
func (s *agentProfileService) GetDispatchAgents(teamIds []int64) []models.AgentProfile {
return AgentProfileService.Find(sqls.NewCnd().
In("team_id", teamIds).
Eq("status", enums.StatusOk).
Eq("auto_assign_enabled", true).
Eq("service_status", enums.ServiceStatusIdle))
}
func (s *agentProfileService) CreateAgentProfile(req request.CreateAgentProfileRequest, operator *dto.AuthPrincipal) (*models.AgentProfile, error) {
if operator == nil {
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
}
item, err := s.buildProfileModel(0, req)
if err != nil {
return nil, err
}
item.AuditFields = utils.BuildAuditFields(operator)
if err := repositories.AgentProfileRepository.Create(sqls.DB(), item); err != nil {
return nil, err
}
s.dispatchPendingConversationsIfEligible(item)
return item, nil
}
func (s *agentProfileService) UpdateAgentProfile(req request.UpdateAgentProfileRequest, operator *dto.AuthPrincipal) error {
if operator == nil {
return errorsx.UnauthorizedI18n("error.auth.expired")
}
current := s.Get(req.ID)
if current == nil {
return errorsx.InvalidParamI18n("error.e0164")
}
item, err := s.buildProfileModel(req.ID, req.CreateAgentProfileRequest)
if err != nil {
return err
}
if err := repositories.AgentProfileRepository.Updates(sqls.DB(), req.ID, map[string]any{
"user_id": item.UserID,
"team_id": item.TeamID,
"agent_code": item.AgentCode,
"display_name": item.DisplayName,
"avatar": item.Avatar,
"service_status": item.ServiceStatus,
"max_concurrent_count": item.MaxConcurrentCount,
"priority_level": item.PriorityLevel,
"auto_assign_enabled": item.AutoAssignEnabled,
"receive_offline_message": item.ReceiveOfflineMessage,
"remark": item.Remark,
"update_user_id": operator.UserID,
"update_user_name": operator.Username,
"updated_at": time.Now(),
}); err != nil {
return err
}
s.dispatchPendingConversationsIfEligible(item)
return nil
}
func (s *agentProfileService) DeleteAgentProfile(id int64) error {
current := s.Get(id)
if current == nil {
return errorsx.InvalidParamI18n("error.e0164")
}
repositories.AgentProfileRepository.Delete(sqls.DB(), id)
return nil
}
func (s *agentProfileService) buildProfileModel(id int64, req request.CreateAgentProfileRequest) (*models.AgentProfile, error) {
if req.UserID <= 0 {
return nil, errorsx.InvalidParamI18n("error.e0325")
}
if UserService.Get(req.UserID) == nil {
return nil, errorsx.InvalidParamI18n("error.e0127")
}
if req.TeamID <= 0 {
return nil, errorsx.InvalidParamI18n("error.e0328")
}
if AgentTeamService.Get(req.TeamID) == nil {
return nil, errorsx.InvalidParamI18n("error.e0205")
}
req.AgentCode = strings.TrimSpace(req.AgentCode)
req.DisplayName = strings.TrimSpace(req.DisplayName)
if req.AgentCode == "" || req.DisplayName == "" {
return nil, errorsx.InvalidParamI18n("error.e0162")
}
if exists := s.Take("user_id = ? AND id <> ?", req.UserID, id); exists != nil {
return nil, errorsx.InvalidParamI18n("error.e0314")
}
if exists := s.Take("agent_code = ? AND id <> ?", req.AgentCode, id); exists != nil {
return nil, errorsx.InvalidParamI18n("error.e0163")
}
if !enums.IsValidServiceStatus(req.ServiceStatus) {
return nil, errorsx.InvalidParamI18n("error.e0165")
}
if req.MaxConcurrentCount < 0 {
return nil, errorsx.InvalidParamI18n("error.e0229")
}
return &models.AgentProfile{
UserID: req.UserID,
TeamID: req.TeamID,
AgentCode: req.AgentCode,
DisplayName: req.DisplayName,
Avatar: strings.TrimSpace(req.Avatar),
ServiceStatus: req.ServiceStatus,
MaxConcurrentCount: req.MaxConcurrentCount,
PriorityLevel: req.PriorityLevel,
AutoAssignEnabled: req.AutoAssignEnabled,
ReceiveOfflineMessage: req.ReceiveOfflineMessage,
Remark: strings.TrimSpace(req.Remark),
}, nil
}
func (s *agentProfileService) dispatchPendingConversationsIfEligible(item *models.AgentProfile) {
if item == nil {
return
}
if item.Status != enums.StatusOk {
return
}
if !item.AutoAssignEnabled || item.MaxConcurrentCount <= 0 {
return
}
if item.ServiceStatus != enums.ServiceStatusIdle {
return
}
_, _ = ConversationDispatchService.DispatchPendingConversations(0)
}