Files
ragflow/internal/service/chat_channel.go
Hz_ 4e0db3053d feat(go-api): complete chat channel API migration with tests (#16139)
close #16132

## Summary

This PR completes the Go-side merge and cleanup for chat channel APIs,
including handler/service wiring, route registration, and test coverage.

Implemented and aligned 5 chat channel APIs:

```
- POST `/api/v1/chat-channels`
- GET `/api/v1/chat-channels`
- GET `/api/v1/chat-channels/:channel_id`
- PATCH `/api/v1/chat-channels/:channel_id`
- DELETE `/api/v1/chat-channels/:channel_id`
```


Co-authored-by: Haruko386 <tryeverypossible@163.com>
2026-06-22 18:16:15 +08:00

234 lines
6.3 KiB
Go

// 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 (
"errors"
"fmt"
"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(channel *entity.ChatChannel) error {
if channel == nil {
return errors.New("channel is nil")
}
if channel.ID == "" {
channel.ID = common.GenerateUUID()
}
if channel.Status == 0 {
channel.Status = 1
}
return s.chatChannelDAO.Create(channel)
}
func (s *ChatChannelService) GetByID(id string) (*entity.ChatChannel, error) {
if id == "" {
return nil, errors.New("id is empty")
}
return s.chatChannelDAO.GetByIDOnly(id)
}
func (s *ChatChannelService) List(tenantID string) ([]*entity.ChatChannelListResponse, error) {
return s.chatChannelDAO.ListByTenantID(tenantID)
}
func (s *ChatChannelService) CreateChatChannel(tenantID, name, channelType string, config entity.JSONMap, chatID *string) (*entity.ChatChannel, error) {
if chatID != nil && *chatID != "" {
dialog, err := s.chatDAO.GetByID(*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: common.GenerateUUID(),
TenantID: tenantID,
Name: name,
Channel: channelType,
Config: config,
ChatID: chatID,
Status: 1,
}
if err := s.Insert(row); err != nil {
return nil, err
}
created, err := s.GetByID(row.ID)
if err != nil {
return nil, fmt.Errorf("failed to load created chat channel: %w", err)
}
return created, nil
}
func (s *ChatChannelService) accessible(userID, channelID string) (*entity.ChatChannel, bool, error) {
channel, err := s.chatChannelDAO.GetByIDOnly(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(userID, channelID string) (*entity.ChatChannel, common.ErrorCode, error) {
_, ok, err := s.accessible(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(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(userID, channelID string, req map[string]interface{}) (*entity.ChatChannel, common.ErrorCode, error) {
channel, ok, err := s.accessible(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(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(channelID, channel.TenantID, updates); err != nil {
return nil, common.CodeDataError, err
}
}
updated, err := s.chatChannelDAO.GetByIDOnly(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(userID, channelID string) (bool, common.ErrorCode, error) {
channel, ok, err := s.accessible(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(channelID, channel.TenantID); err != nil {
return false, common.CodeDataError, err
}
return true, common.CodeSuccess, nil
}