243 lines
5.2 KiB
Go
243 lines
5.2 KiB
Go
package storage
|
|
|
|
import (
|
|
"crm-go/models"
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type FollowUpStorage interface {
|
|
GetAllFollowUps() ([]models.FollowUp, error)
|
|
GetFollowUpByID(id string) (*models.FollowUp, error)
|
|
CreateFollowUp(followUp models.FollowUp) error
|
|
UpdateFollowUp(id string, updates models.UpdateFollowUpRequest) error
|
|
DeleteFollowUp(id string) error
|
|
SaveFollowUps(followUps []models.FollowUp) error
|
|
LoadFollowUps() ([]models.FollowUp, error)
|
|
GetPendingNotifications() ([]models.FollowUp, error)
|
|
MarkNotificationSent(id string) error
|
|
}
|
|
|
|
type followUpStorage struct {
|
|
filePath string
|
|
mutex sync.RWMutex
|
|
}
|
|
|
|
func NewFollowUpStorage(filePath string) FollowUpStorage {
|
|
storage := &followUpStorage{
|
|
filePath: filePath,
|
|
}
|
|
return storage
|
|
}
|
|
|
|
func (fs *followUpStorage) GetAllFollowUps() ([]models.FollowUp, error) {
|
|
fs.mutex.RLock()
|
|
defer fs.mutex.RUnlock()
|
|
|
|
followUps, err := fs.LoadFollowUps()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Sort by FollowUpTime in descending order (newest first)
|
|
for i := 0; i < len(followUps)-1; i++ {
|
|
for j := i + 1; j < len(followUps); j++ {
|
|
if followUps[i].FollowUpTime.Before(followUps[j].FollowUpTime) {
|
|
followUps[i], followUps[j] = followUps[j], followUps[i]
|
|
}
|
|
}
|
|
}
|
|
|
|
return followUps, nil
|
|
}
|
|
|
|
func (fs *followUpStorage) GetFollowUpByID(id string) (*models.FollowUp, error) {
|
|
fs.mutex.RLock()
|
|
defer fs.mutex.RUnlock()
|
|
|
|
followUps, err := fs.LoadFollowUps()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, followUp := range followUps {
|
|
if followUp.ID == id {
|
|
return &followUp, nil
|
|
}
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func (fs *followUpStorage) CreateFollowUp(followUp models.FollowUp) error {
|
|
fs.mutex.Lock()
|
|
defer fs.mutex.Unlock()
|
|
|
|
if followUp.ID == "" {
|
|
followUp.ID = generateFollowUpUUID()
|
|
}
|
|
|
|
if followUp.CreatedAt.IsZero() {
|
|
followUp.CreatedAt = time.Now()
|
|
}
|
|
|
|
followUps, err := fs.LoadFollowUps()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
followUps = append(followUps, followUp)
|
|
|
|
return fs.SaveFollowUps(followUps)
|
|
}
|
|
|
|
func generateFollowUpUUID() 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 (fs *followUpStorage) UpdateFollowUp(id string, updates models.UpdateFollowUpRequest) error {
|
|
fs.mutex.Lock()
|
|
defer fs.mutex.Unlock()
|
|
|
|
followUps, err := fs.LoadFollowUps()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for i, followUp := range followUps {
|
|
if followUp.ID == id {
|
|
if updates.CustomerName != nil {
|
|
followUps[i].CustomerName = *updates.CustomerName
|
|
}
|
|
if updates.DealStatus != nil {
|
|
followUps[i].DealStatus = *updates.DealStatus
|
|
}
|
|
if updates.CustomerLevel != nil {
|
|
followUps[i].CustomerLevel = *updates.CustomerLevel
|
|
}
|
|
if updates.Industry != nil {
|
|
followUps[i].Industry = *updates.Industry
|
|
}
|
|
if updates.FollowUpTime != nil {
|
|
// Parse the time string
|
|
t, err := time.Parse(time.RFC3339, *updates.FollowUpTime)
|
|
if err == nil {
|
|
followUps[i].FollowUpTime = t
|
|
}
|
|
}
|
|
if updates.NotificationSent != nil {
|
|
followUps[i].NotificationSent = *updates.NotificationSent
|
|
}
|
|
|
|
return fs.SaveFollowUps(followUps)
|
|
}
|
|
}
|
|
|
|
return nil // FollowUp not found, but not an error
|
|
}
|
|
|
|
func (fs *followUpStorage) DeleteFollowUp(id string) error {
|
|
fs.mutex.Lock()
|
|
defer fs.mutex.Unlock()
|
|
|
|
followUps, err := fs.LoadFollowUps()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for i, followUp := range followUps {
|
|
if followUp.ID == id {
|
|
followUps = append(followUps[:i], followUps[i+1:]...)
|
|
return fs.SaveFollowUps(followUps)
|
|
}
|
|
}
|
|
|
|
return nil // FollowUp not found, but not an error
|
|
}
|
|
|
|
func (fs *followUpStorage) SaveFollowUps(followUps []models.FollowUp) error {
|
|
// Ensure the directory exists
|
|
dir := filepath.Dir(fs.filePath)
|
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
|
return err
|
|
}
|
|
|
|
data, err := json.MarshalIndent(followUps, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return os.WriteFile(fs.filePath, data, 0644)
|
|
}
|
|
|
|
func (fs *followUpStorage) LoadFollowUps() ([]models.FollowUp, error) {
|
|
// Check if file exists
|
|
if _, err := os.Stat(fs.filePath); os.IsNotExist(err) {
|
|
// Return empty slice if file doesn't exist
|
|
return []models.FollowUp{}, nil
|
|
}
|
|
|
|
data, err := os.ReadFile(fs.filePath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var followUps []models.FollowUp
|
|
if err := json.Unmarshal(data, &followUps); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return followUps, nil
|
|
}
|
|
|
|
func (fs *followUpStorage) GetPendingNotifications() ([]models.FollowUp, error) {
|
|
fs.mutex.RLock()
|
|
defer fs.mutex.RUnlock()
|
|
|
|
followUps, err := fs.LoadFollowUps()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
now := time.Now()
|
|
var pending []models.FollowUp
|
|
|
|
for _, followUp := range followUps {
|
|
// Check if follow-up time has passed and notification hasn't been sent
|
|
if !followUp.NotificationSent && followUp.FollowUpTime.Before(now) {
|
|
pending = append(pending, followUp)
|
|
}
|
|
}
|
|
|
|
return pending, nil
|
|
}
|
|
|
|
func (fs *followUpStorage) MarkNotificationSent(id string) error {
|
|
fs.mutex.Lock()
|
|
defer fs.mutex.Unlock()
|
|
|
|
followUps, err := fs.LoadFollowUps()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for i, followUp := range followUps {
|
|
if followUp.ID == id {
|
|
followUps[i].NotificationSent = true
|
|
return fs.SaveFollowUps(followUps)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|