crm/internal/storage/customer_storage.go

229 lines
5.0 KiB
Go

package storage
import (
"crm-go/models"
"crypto/rand"
"encoding/hex"
"encoding/json"
"os"
"path/filepath"
"sync"
"time"
)
type CustomerStorage interface {
GetAllCustomers() ([]models.Customer, error)
GetCustomerByID(id string) (*models.Customer, error)
CreateCustomer(customer models.Customer) error
UpdateCustomer(id string, updates models.UpdateCustomerRequest) error
DeleteCustomer(id string) error
SaveCustomers(customers []models.Customer) error
LoadCustomers() ([]models.Customer, error)
CustomerExists(customer models.Customer) (bool, error)
}
type customerStorage struct {
filePath string
mutex sync.RWMutex
}
func NewCustomerStorage(filePath string) CustomerStorage {
storage := &customerStorage{
filePath: filePath,
}
// Ensure the directory exists
dir := os.DirFS(filePath[:len(filePath)-len("/customers.json")])
_ = dir // Use the directory to ensure it exists
return storage
}
func (cs *customerStorage) GetAllCustomers() ([]models.Customer, error) {
cs.mutex.RLock()
defer cs.mutex.RUnlock()
customers, err := cs.LoadCustomers()
if err != nil {
return nil, err
}
// Sort by CreatedAt in descending order (newest first)
for i := 0; i < len(customers)-1; i++ {
for j := i + 1; j < len(customers); j++ {
if customers[i].CreatedAt.Before(customers[j].CreatedAt) {
customers[i], customers[j] = customers[j], customers[i]
}
}
}
return customers, nil
}
func (cs *customerStorage) GetCustomerByID(id string) (*models.Customer, error) {
cs.mutex.RLock()
defer cs.mutex.RUnlock()
customers, err := cs.LoadCustomers()
if err != nil {
return nil, err
}
for _, customer := range customers {
if customer.ID == id {
return &customer, nil
}
}
return nil, nil
}
func (cs *customerStorage) CreateCustomer(customer models.Customer) error {
cs.mutex.Lock()
defer cs.mutex.Unlock()
if customer.ID == "" {
customer.ID = generateUUID()
}
if customer.CreatedAt.IsZero() {
customer.CreatedAt = time.Now()
}
customers, err := cs.LoadCustomers()
if err != nil {
return err
}
customers = append(customers, customer)
return cs.SaveCustomers(customers)
}
func generateUUID() string {
bytes := make([]byte, 16)
rand.Read(bytes)
bytes[6] = (bytes[6] & 0x0f) | 0x40 // Version 4
bytes[8] = (bytes[8] & 0x3f) | 0x80 // Variant
return hex.EncodeToString(bytes)
}
func (cs *customerStorage) UpdateCustomer(id string, updates models.UpdateCustomerRequest) error {
cs.mutex.Lock()
defer cs.mutex.Unlock()
customers, err := cs.LoadCustomers()
if err != nil {
return err
}
for i, customer := range customers {
if customer.ID == id {
if updates.CustomerName != nil {
customers[i].CustomerName = *updates.CustomerName
}
if updates.IntendedProduct != nil {
customers[i].IntendedProduct = *updates.IntendedProduct
}
if updates.Version != nil {
customers[i].Version = *updates.Version
}
if updates.Description != nil {
customers[i].Description = *updates.Description
}
if updates.Solution != nil {
customers[i].Solution = *updates.Solution
}
if updates.Type != nil {
customers[i].Type = *updates.Type
}
if updates.Module != nil {
customers[i].Module = *updates.Module
}
if updates.StatusProgress != nil {
customers[i].StatusProgress = *updates.StatusProgress
}
if updates.Reporter != nil {
customers[i].Reporter = *updates.Reporter
}
return cs.SaveCustomers(customers)
}
}
return nil // Customer not found, but not an error
}
func (cs *customerStorage) DeleteCustomer(id string) error {
cs.mutex.Lock()
defer cs.mutex.Unlock()
customers, err := cs.LoadCustomers()
if err != nil {
return err
}
for i, customer := range customers {
if customer.ID == id {
customers = append(customers[:i], customers[i+1:]...)
return cs.SaveCustomers(customers)
}
}
return nil // Customer not found, but not an error
}
func (cs *customerStorage) SaveCustomers(customers []models.Customer) error {
// Ensure the directory exists
dir := filepath.Dir(cs.filePath)
if err := os.MkdirAll(dir, 0755); err != nil {
return err
}
data, err := json.MarshalIndent(customers, "", " ")
if err != nil {
return err
}
return os.WriteFile(cs.filePath, data, 0644)
}
func (cs *customerStorage) LoadCustomers() ([]models.Customer, error) {
// Check if file exists
if _, err := os.Stat(cs.filePath); os.IsNotExist(err) {
// Return empty slice if file doesn't exist
return []models.Customer{}, nil
}
data, err := os.ReadFile(cs.filePath)
if err != nil {
return nil, err
}
var customers []models.Customer
if err := json.Unmarshal(data, &customers); err != nil {
return nil, err
}
return customers, nil
}
func (cs *customerStorage) CustomerExists(customer models.Customer) (bool, error) {
cs.mutex.RLock()
defer cs.mutex.RUnlock()
customers, err := cs.LoadCustomers()
if err != nil {
return false, err
}
for _, existingCustomer := range customers {
if existingCustomer.Description == customer.Description {
return true, nil
}
}
return false, nil
}