Files
boc/backend/service/customer.go
T

193 lines
5.1 KiB
Go
Raw Normal View History

package service
import (
"context"
"fmt"
"boc/middleware"
"boc/repository"
)
// CustomerService innehåller affärslogik för kunder
type CustomerService struct {
repo *repository.CustomerRepository
}
func NewCustomerService(repo *repository.CustomerRepository) *CustomerService {
return &CustomerService{repo: repo}
}
// CreateCustomer skapar en ny kund med validering
func (s *CustomerService) CreateCustomer(ctx context.Context, req *CreateCustomerRequest) (*repository.Customer, error) {
// Validera input
v := middleware.NewValidator()
v.ValidateString("name", req.Name, 2, 255, true)
v.ValidateEmail("email", req.Email, true)
if req.Phone != "" {
if !middleware.ValidatePhone(req.Phone) {
v.AddError("phone", "invalid phone format")
}
}
if req.OrgNumber != "" {
if !middleware.ValidateOrgNumber(req.OrgNumber) {
v.AddError("org_number", "invalid organization number format (XXXXXX-XXXX)")
}
}
v.ValidateEnum("status", req.Status, []string{"active", "lead", "prospect", "inactive"}, false)
if v.HasErrors() {
return nil, fmt.Errorf("validation failed: %v", v.Errors())
}
// Skapa kund
customer := &repository.Customer{
TenantID: req.TenantID,
Name: middleware.SanitizeString(req.Name),
Email: req.Email,
Phone: req.Phone,
Company: middleware.SanitizeString(req.Company),
OrgNumber: req.OrgNumber,
Status: req.Status,
Source: req.Source,
Tags: req.Tags,
AssignedTo: req.AssignedTo,
}
if customer.Status == "" {
customer.Status = "lead"
}
if err := s.repo.CreateCustomer(ctx, customer); err != nil {
return nil, fmt.Errorf("create customer: %w", err)
}
return customer, nil
}
// GetCustomer hämtar en kund med ID
func (s *CustomerService) GetCustomer(ctx context.Context, id string) (*repository.Customer, error) {
if !middleware.ValidateUUID(id) {
return nil, fmt.Errorf("invalid customer ID")
}
customer, err := s.repo.GetCustomer(ctx, id)
if err != nil {
return nil, err
}
return customer, nil
}
// ListCustomers hämtar kunder med pagination
func (s *CustomerService) ListCustomers(ctx context.Context, tenantID, status string, page, pageSize int) (*ListCustomersResponse, error) {
if page < 1 {
page = 1
}
if pageSize < 1 || pageSize > 100 {
pageSize = 20
}
offset := (page - 1) * pageSize
customers, total, err := s.repo.ListCustomers(ctx, tenantID, status, pageSize, offset)
if err != nil {
return nil, err
}
return &ListCustomersResponse{
Customers: customers,
Total: total,
Page: page,
PageSize: pageSize,
Pages: (total + pageSize - 1) / pageSize,
}, nil
}
// UpdateCustomer uppdaterar en kund
func (s *CustomerService) UpdateCustomer(ctx context.Context, id string, req *UpdateCustomerRequest) (*repository.Customer, error) {
if !middleware.ValidateUUID(id) {
return nil, fmt.Errorf("invalid customer ID")
}
// Hämta befintlig kund
customer, err := s.repo.GetCustomer(ctx, id)
if err != nil {
return nil, err
}
// Uppdatera fält
if req.Name != "" {
customer.Name = middleware.SanitizeString(req.Name)
}
if req.Email != "" {
customer.Email = req.Email
}
if req.Phone != "" {
customer.Phone = req.Phone
}
if req.Company != "" {
customer.Company = middleware.SanitizeString(req.Company)
}
if req.Status != "" {
customer.Status = req.Status
}
if err := s.repo.UpdateCustomer(ctx, customer); err != nil {
return nil, err
}
return customer, nil
}
// DeleteCustomer tar bort en kund (soft delete)
func (s *CustomerService) DeleteCustomer(ctx context.Context, id string) error {
if !middleware.ValidateUUID(id) {
return fmt.Errorf("invalid customer ID")
}
return s.repo.DeleteCustomer(ctx, id)
}
// SearchCustomers söker efter kunder
func (s *CustomerService) SearchCustomers(ctx context.Context, tenantID, query string, limit int) ([]repository.Customer, error) {
if limit < 1 || limit > 100 {
limit = 20
}
return s.repo.SearchCustomers(ctx, tenantID, query, limit)
}
// ── Request/Response DTOs ─────────────────────────────────────────────────
type CreateCustomerRequest struct {
TenantID string `json:"tenant_id"`
Name string `json:"name"`
Email string `json:"email"`
Phone string `json:"phone,omitempty"`
Company string `json:"company,omitempty"`
OrgNumber string `json:"org_number,omitempty"`
Status string `json:"status,omitempty"`
Source string `json:"source,omitempty"`
Tags []string `json:"tags,omitempty"`
AssignedTo *string `json:"assigned_to,omitempty"`
}
type UpdateCustomerRequest struct {
Name string `json:"name,omitempty"`
Email string `json:"email,omitempty"`
Phone string `json:"phone,omitempty"`
Company string `json:"company,omitempty"`
Status string `json:"status,omitempty"`
}
type ListCustomersResponse struct {
Customers []repository.Customer `json:"customers"`
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
Pages int `json:"pages"`
}