package service import ( "errors" "netidhub-saas-be/app/database/entity" "netidhub-saas-be/app/module/clients/mapper" "netidhub-saas-be/app/module/clients/repository" "netidhub-saas-be/app/module/clients/request" "netidhub-saas-be/app/module/clients/response" usersRepository "netidhub-saas-be/app/module/users/repository" "netidhub-saas-be/utils/paginator" "github.com/google/uuid" "github.com/rs/zerolog" utilSvc "netidhub-saas-be/utils/service" ) // ClientsService type clientsService struct { Repo repository.ClientsRepository UsersRepo usersRepository.UsersRepository Log zerolog.Logger } // ClientsService define interface of IClientsService type ClientsService interface { All(authToken string, req request.ClientsQueryRequest) (clients []*response.ClientsResponse, paging paginator.Pagination, err error) Show(id uuid.UUID) (clients *response.ClientsResponse, err error) Save(req request.ClientsCreateRequest, authToken string) (clients *entity.Clients, err error) Update(id uuid.UUID, req request.ClientsUpdateRequest) (err error) Delete(id uuid.UUID) error // New hierarchy methods CreateSubClient(parentId uuid.UUID, req request.ClientsCreateRequest) (*entity.Clients, error) MoveClient(clientId uuid.UUID, req request.MoveClientRequest) error GetHierarchy(clientId uuid.UUID) (*response.ClientHierarchyResponse, error) GetClientStats(clientId uuid.UUID) (*response.ClientStatsResponse, error) BulkCreateSubClients(req request.BulkCreateSubClientsRequest) (*response.BulkOperationResponse, error) } // NewClientsService init ClientsService func NewClientsService(repo repository.ClientsRepository, log zerolog.Logger, usersRepo usersRepository.UsersRepository) ClientsService { return &clientsService{ Repo: repo, Log: log, UsersRepo: usersRepo, } } // All implement interface of ClientsService func (_i *clientsService) All(authToken string, req request.ClientsQueryRequest) (clientss []*response.ClientsResponse, paging paginator.Pagination, err error) { // Extract clientId from authToken var clientId *uuid.UUID if authToken != "" { user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) if user != nil && user.ClientId != nil { clientId = user.ClientId _i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token") } } results, paging, err := _i.Repo.GetAll(req) if err != nil { return } for _, result := range results { clientss = append(clientss, mapper.ClientsResponseMapper(result)) } return } func (_i *clientsService) Show(id uuid.UUID) (clients *response.ClientsResponse, err error) { result, err := _i.Repo.FindOne(id) if err != nil { return nil, err } return mapper.ClientsResponseMapper(result), nil } func (_i *clientsService) Save(req request.ClientsCreateRequest, authToken string) (clients *entity.Clients, err error) { _i.Log.Info().Interface("data", req).Msg("") // Convert request to entity newReq := &entity.Clients{ Name: req.Name, Description: req.Description, ClientType: req.ClientType, ParentClientId: req.ParentClientId, MaxUsers: req.MaxUsers, MaxStorage: req.MaxStorage, Settings: req.Settings, } _i.Log.Info().Interface("token", authToken).Msg("") createdBy := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) _i.Log.Info().Interface("token", authToken).Msg("") newReq.CreatedById = &createdBy.ID newReq.ID = uuid.New() _i.Log.Info().Interface("new data", newReq).Msg("") return _i.Repo.Create(newReq) } func (_i *clientsService) Update(id uuid.UUID, req request.ClientsUpdateRequest) (err error) { _i.Log.Info().Interface("data", req).Msg("") // Convert request to entity updateReq := &entity.Clients{ Name: *req.Name, Description: req.Description, ClientType: *req.ClientType, ParentClientId: req.ParentClientId, MaxUsers: req.MaxUsers, MaxStorage: req.MaxStorage, Settings: req.Settings, IsActive: req.IsActive, } return _i.Repo.Update(id, updateReq) } func (_i *clientsService) Delete(id uuid.UUID) error { result, err := _i.Repo.FindOne(id) if err != nil { return err } isActive := false result.IsActive = &isActive return _i.Repo.Update(id, result) } // ===================================================================== // NEW HIERARCHY METHODS // ===================================================================== // CreateSubClient creates a client under a parent func (_i *clientsService) CreateSubClient(parentId uuid.UUID, req request.ClientsCreateRequest) (*entity.Clients, error) { // Validate parent exists _, err := _i.Repo.FindOne(parentId) if err != nil { return nil, errors.New("parent client not found") } // Set client type and parent req.ClientType = "sub_client" req.ParentClientId = &parentId // Convert to entity newReq := &entity.Clients{ Name: req.Name, Description: req.Description, ClientType: req.ClientType, ParentClientId: req.ParentClientId, MaxUsers: req.MaxUsers, MaxStorage: req.MaxStorage, Settings: req.Settings, } newReq.ID = uuid.New() return _i.Repo.Create(newReq) } // MoveClient moves a client to different parent func (_i *clientsService) MoveClient(clientId uuid.UUID, req request.MoveClientRequest) error { client, err := _i.Repo.FindOne(clientId) if err != nil { return errors.New("client not found") } // If moving to root (standalone) if req.TargetParentId == nil { client.ClientType = "standalone" client.ParentClientId = nil return _i.Repo.Update(clientId, client) } // Validate target parent exists _, err = _i.Repo.FindOne(*req.TargetParentId) if err != nil { return errors.New("target parent not found") } // Move return _i.Repo.MoveClient(clientId, *req.TargetParentId) } // GetHierarchy gets full client tree func (_i *clientsService) GetHierarchy(clientId uuid.UUID) (*response.ClientHierarchyResponse, error) { client, err := _i.Repo.GetWithHierarchy(clientId) if err != nil { return nil, err } return _i.buildHierarchyResponse(client, 0, []string{}), nil } // buildHierarchyResponse recursively builds hierarchy func (_i *clientsService) buildHierarchyResponse(client *entity.Clients, level int, path []string) *response.ClientHierarchyResponse { currentPath := append(path, client.Name) resp := &response.ClientHierarchyResponse{ ID: client.ID, Name: client.Name, Description: client.Description, ClientType: client.ClientType, Level: level, Path: currentPath, ParentClientId: client.ParentClientId, IsActive: client.IsActive, } // Count users (simplified - would need proper DB access) resp.CurrentUsers = 0 // TODO: implement user count // Build sub-clients recursively if client.SubClients != nil { for _, subClient := range client.SubClients { resp.SubClients = append(resp.SubClients, *_i.buildHierarchyResponse(&subClient, level+1, currentPath)) } } return resp } // GetClientStats gets comprehensive statistics func (_i *clientsService) GetClientStats(clientId uuid.UUID) (*response.ClientStatsResponse, error) { client, err := _i.Repo.FindOne(clientId) if err != nil { return nil, err } stats, err := _i.Repo.GetClientStats(clientId) if err != nil { return nil, err } isParent, _ := _i.Repo.IsParentClient(clientId) return &response.ClientStatsResponse{ ClientId: client.ID, ClientName: client.Name, TotalUsers: stats["total_users"].(int), TotalArticles: stats["total_articles"].(int), SubClientCount: stats["sub_client_count"].(int), IsParent: isParent, }, nil } // BulkCreateSubClients creates multiple sub-clients func (_i *clientsService) BulkCreateSubClients(req request.BulkCreateSubClientsRequest) (*response.BulkOperationResponse, error) { results := []response.BulkOperationResult{} successful := 0 failed := 0 for i, subClientReq := range req.SubClients { createReq := request.ClientsCreateRequest{ Name: subClientReq.Name, Description: subClientReq.Description, ClientType: "sub_client", ParentClientId: &req.ParentClientId, MaxUsers: subClientReq.MaxUsers, MaxStorage: subClientReq.MaxStorage, } client, err := _i.CreateSubClient(req.ParentClientId, createReq) if err != nil { failed++ errMsg := err.Error() results = append(results, response.BulkOperationResult{ Index: i, Name: subClientReq.Name, Success: false, Error: &errMsg, }) } else { successful++ results = append(results, response.BulkOperationResult{ Index: i, ClientId: &client.ID, Name: client.Name, Success: true, }) } } return &response.BulkOperationResponse{ TotalRequested: len(req.SubClients), Successful: successful, Failed: failed, Results: results, }, nil }