// Copyright 2026 The InfiniFlow Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package service import ( "context" "errors" "fmt" "ragflow/internal/utility" "ragflow/internal/common" "ragflow/internal/dao" "ragflow/internal/entity" ) type ChatChannelService struct { chatChannelDAO *dao.ChatChannelDAO chatDAO *dao.ChatDAO userTenantDAO *dao.UserTenantDAO } func NewChatChannelService() *ChatChannelService { return &ChatChannelService{ chatChannelDAO: dao.NewChatChannel(), chatDAO: dao.NewChatDAO(), userTenantDAO: dao.NewUserTenantDAO(), } } func (s *ChatChannelService) Insert(ctx context.Context, channel *entity.ChatChannel) error { if channel == nil { return errors.New("channel is nil") } if channel.ID == "" { channel.ID = utility.GenerateUUID() } if channel.Status == 0 { channel.Status = 1 } return s.chatChannelDAO.Create(ctx, dao.DB, channel) } func (s *ChatChannelService) GetByID(ctx context.Context, id string) (*entity.ChatChannel, error) { if id == "" { return nil, errors.New("id is empty") } return s.chatChannelDAO.GetByIDOnly(ctx, dao.DB, id) } func (s *ChatChannelService) List(ctx context.Context, tenantID string) ([]*entity.ChatChannelListResponse, error) { return s.chatChannelDAO.ListByTenantID(ctx, dao.DB, tenantID) } func (s *ChatChannelService) CreateChatChannel(ctx context.Context, tenantID, name, channelType string, config entity.JSONMap, chatID *string) (*entity.ChatChannel, error) { if chatID != nil && *chatID != "" { dialog, err := s.chatDAO.GetByID(ctx, dao.DB, *chatID) if err != nil { if dao.IsNotFoundErr(err) { return nil, errors.New("can't find this chat assistant") } return nil, err } if dialog.TenantID != tenantID { return nil, errors.New("no authorization") } } row := &entity.ChatChannel{ ID: utility.GenerateUUID(), TenantID: tenantID, Name: name, Channel: channelType, Config: config, ChatID: chatID, Status: 1, } if err := s.Insert(ctx, row); err != nil { return nil, err } created, err := s.GetByID(ctx, row.ID) if err != nil { return nil, fmt.Errorf("failed to load created chat channel: %w", err) } return created, nil } func (s *ChatChannelService) accessible(ctx context.Context, userID, channelID string) (*entity.ChatChannel, bool, error) { channel, err := s.chatChannelDAO.GetByIDOnly(ctx, dao.DB, channelID) if err != nil { if dao.IsNotFoundErr(err) { return nil, false, nil } return nil, false, err } if channel.TenantID == userID { return channel, true, nil } tenantIDs, err := s.userTenantDAO.GetTenantIDsByUserID(userID) if err != nil { return nil, false, err } for _, tenantID := range tenantIDs { if tenantID == channel.TenantID { return channel, true, nil } } return channel, false, nil } func (s *ChatChannelService) GetChatChannel(ctx context.Context, userID, channelID string) (*entity.ChatChannel, common.ErrorCode, error) { _, ok, err := s.accessible(ctx, userID, channelID) if err != nil { return nil, common.CodeServerError, err } if !ok { return nil, common.CodeAuthenticationError, errors.New("no authorization") } channel, err := s.chatChannelDAO.GetByIDOnly(ctx, dao.DB, channelID) if err != nil { if dao.IsNotFoundErr(err) { return nil, common.CodeDataError, errors.New("can't find this chat channel") } return nil, common.CodeServerError, err } return channel, common.CodeSuccess, nil } func (s *ChatChannelService) UpdateChatChannel(ctx context.Context, userID, channelID string, req map[string]interface{}) (*entity.ChatChannel, common.ErrorCode, error) { channel, ok, err := s.accessible(ctx, userID, channelID) if err != nil { return nil, common.CodeServerError, err } if !ok { return nil, common.CodeAuthenticationError, errors.New("no authorization") } if channel == nil { return nil, common.CodeDataError, errors.New("can't find this chat channel") } updates := map[string]interface{}{} if value, exists := req["name"]; exists { name, ok := value.(string) if !ok { return nil, common.CodeDataError, errors.New("name must be string") } updates["name"] = name } if value, exists := req["config"]; exists { if value == nil { updates["config"] = nil } else { config, ok := value.(map[string]interface{}) if !ok { return nil, common.CodeDataError, errors.New("config must be object") } updates["config"] = entity.JSONMap(config) } } if value, exists := req["chat_id"]; exists { if value == nil { updates["chat_id"] = nil } else { chatID, ok := value.(string) if !ok { return nil, common.CodeDataError, errors.New("chat_id must be string or null") } if chatID != "" { dialog, err := s.chatDAO.GetByID(ctx, dao.DB, chatID) if err != nil { if dao.IsNotFoundErr(err) { return nil, common.CodeDataError, errors.New("can't find this chat assistant") } return nil, common.CodeServerError, err } if dialog.TenantID != channel.TenantID { return nil, common.CodeAuthenticationError, errors.New("no authorization") } } updates["chat_id"] = chatID } } if len(updates) > 0 { if err = s.chatChannelDAO.UpdateByID(ctx, dao.DB, channelID, channel.TenantID, updates); err != nil { return nil, common.CodeDataError, err } } updated, err := s.chatChannelDAO.GetByIDOnly(ctx, dao.DB, channelID) if err != nil { if dao.IsNotFoundErr(err) { return nil, common.CodeDataError, errors.New("can't find this chat channel") } return nil, common.CodeServerError, err } return updated, common.CodeSuccess, nil } func (s *ChatChannelService) DeleteChatChannel(ctx context.Context, userID, channelID string) (bool, common.ErrorCode, error) { channel, ok, err := s.accessible(ctx, userID, channelID) if err != nil { return false, common.CodeServerError, err } if !ok { return false, common.CodeAuthenticationError, errors.New("no authorization") } if channel == nil { return false, common.CodeAuthenticationError, errors.New("no authorization") } if err = s.chatChannelDAO.DeleteByID(ctx, dao.DB, channelID, channel.TenantID); err != nil { return false, common.CodeDataError, err } return true, common.CodeSuccess, nil }