250 lines
5.9 KiB
Go
250 lines
5.9 KiB
Go
package storage
|
||
|
||
import (
|
||
"crm-go/models"
|
||
"crypto/rand"
|
||
"database/sql"
|
||
"encoding/hex"
|
||
"fmt"
|
||
"strings"
|
||
"time"
|
||
)
|
||
|
||
type mysqlFollowUpStorage struct {
|
||
db *sql.DB
|
||
}
|
||
|
||
// NewMySQLFollowUpStorage 创建MySQL跟进存储
|
||
func NewMySQLFollowUpStorage() FollowUpStorage {
|
||
return &mysqlFollowUpStorage{
|
||
db: GetDB(),
|
||
}
|
||
}
|
||
|
||
func (fs *mysqlFollowUpStorage) GetAllFollowUps() ([]models.FollowUp, error) {
|
||
query := `
|
||
SELECT id, created_at, customer_name, deal_status, customer_level,
|
||
industry, follow_up_time, notification_sent
|
||
FROM followups
|
||
ORDER BY follow_up_time DESC
|
||
`
|
||
|
||
rows, err := fs.db.Query(query)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer rows.Close()
|
||
|
||
var followUps []models.FollowUp
|
||
for rows.Next() {
|
||
var f models.FollowUp
|
||
var dealStatus, customerLevel, industry sql.NullString
|
||
var notificationSent int
|
||
|
||
err := rows.Scan(
|
||
&f.ID, &f.CreatedAt, &f.CustomerName,
|
||
&dealStatus, &customerLevel, &industry,
|
||
&f.FollowUpTime, ¬ificationSent,
|
||
)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
f.DealStatus = dealStatus.String
|
||
f.CustomerLevel = customerLevel.String
|
||
f.Industry = industry.String
|
||
f.NotificationSent = notificationSent == 1
|
||
|
||
followUps = append(followUps, f)
|
||
}
|
||
|
||
return followUps, rows.Err()
|
||
}
|
||
|
||
func (fs *mysqlFollowUpStorage) GetFollowUpByID(id string) (*models.FollowUp, error) {
|
||
query := `
|
||
SELECT id, created_at, customer_name, deal_status, customer_level,
|
||
industry, follow_up_time, notification_sent
|
||
FROM followups
|
||
WHERE id = ?
|
||
`
|
||
|
||
var f models.FollowUp
|
||
var dealStatus, customerLevel, industry sql.NullString
|
||
var notificationSent int
|
||
|
||
err := fs.db.QueryRow(query, id).Scan(
|
||
&f.ID, &f.CreatedAt, &f.CustomerName,
|
||
&dealStatus, &customerLevel, &industry,
|
||
&f.FollowUpTime, ¬ificationSent,
|
||
)
|
||
|
||
if err == sql.ErrNoRows {
|
||
return nil, nil
|
||
}
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
f.DealStatus = dealStatus.String
|
||
f.CustomerLevel = customerLevel.String
|
||
f.Industry = industry.String
|
||
f.NotificationSent = notificationSent == 1
|
||
|
||
return &f, nil
|
||
}
|
||
|
||
func (fs *mysqlFollowUpStorage) CreateFollowUp(followUp models.FollowUp) error {
|
||
if followUp.ID == "" {
|
||
followUp.ID = generateFollowUpMySQLUUID()
|
||
}
|
||
if followUp.CreatedAt.IsZero() {
|
||
followUp.CreatedAt = time.Now()
|
||
}
|
||
|
||
query := `
|
||
INSERT INTO followups (id, created_at, customer_name, deal_status, customer_level,
|
||
industry, follow_up_time, notification_sent)
|
||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||
`
|
||
|
||
notificationSent := 0
|
||
if followUp.NotificationSent {
|
||
notificationSent = 1
|
||
}
|
||
|
||
_, err := fs.db.Exec(query,
|
||
followUp.ID, followUp.CreatedAt, followUp.CustomerName,
|
||
followUp.DealStatus, followUp.CustomerLevel, followUp.Industry,
|
||
followUp.FollowUpTime, notificationSent,
|
||
)
|
||
|
||
return err
|
||
}
|
||
|
||
func (fs *mysqlFollowUpStorage) UpdateFollowUp(id string, updates models.UpdateFollowUpRequest) error {
|
||
// 首先获取现有记录
|
||
existing, err := fs.GetFollowUpByID(id)
|
||
if err != nil || existing == nil {
|
||
return err
|
||
}
|
||
|
||
// 应用更新
|
||
if updates.CustomerName != nil {
|
||
existing.CustomerName = *updates.CustomerName
|
||
}
|
||
if updates.DealStatus != nil {
|
||
existing.DealStatus = *updates.DealStatus
|
||
}
|
||
if updates.CustomerLevel != nil {
|
||
existing.CustomerLevel = *updates.CustomerLevel
|
||
}
|
||
if updates.Industry != nil {
|
||
existing.Industry = *updates.Industry
|
||
}
|
||
if updates.FollowUpTime != nil {
|
||
t, err := time.Parse(time.RFC3339, *updates.FollowUpTime)
|
||
if err == nil {
|
||
existing.FollowUpTime = t
|
||
}
|
||
}
|
||
if updates.NotificationSent != nil {
|
||
existing.NotificationSent = *updates.NotificationSent
|
||
}
|
||
|
||
query := `
|
||
UPDATE followups
|
||
SET customer_name = ?, deal_status = ?, customer_level = ?,
|
||
industry = ?, follow_up_time = ?, notification_sent = ?
|
||
WHERE id = ?
|
||
`
|
||
|
||
notificationSent := 0
|
||
if existing.NotificationSent {
|
||
notificationSent = 1
|
||
}
|
||
|
||
_, err = fs.db.Exec(query,
|
||
existing.CustomerName, existing.DealStatus, existing.CustomerLevel,
|
||
existing.Industry, existing.FollowUpTime, notificationSent,
|
||
id,
|
||
)
|
||
|
||
return err
|
||
}
|
||
|
||
func (fs *mysqlFollowUpStorage) DeleteFollowUp(id string) error {
|
||
query := `DELETE FROM followups WHERE id = ?`
|
||
_, err := fs.db.Exec(query, id)
|
||
if err != nil {
|
||
if strings.Contains(err.Error(), "command denied") {
|
||
return fmt.Errorf("数据库权限不足:无法执行删除操作,请联系管理员")
|
||
}
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (fs *mysqlFollowUpStorage) SaveFollowUps(followUps []models.FollowUp) error {
|
||
// MySQL版本不需要使用此方法,保留接口兼容
|
||
return nil
|
||
}
|
||
|
||
func (fs *mysqlFollowUpStorage) LoadFollowUps() ([]models.FollowUp, error) {
|
||
return fs.GetAllFollowUps()
|
||
}
|
||
|
||
func (fs *mysqlFollowUpStorage) GetPendingNotifications() ([]models.FollowUp, error) {
|
||
query := `
|
||
SELECT id, created_at, customer_name, deal_status, customer_level,
|
||
industry, follow_up_time, notification_sent
|
||
FROM followups
|
||
WHERE notification_sent = 0 AND follow_up_time < NOW()
|
||
`
|
||
|
||
rows, err := fs.db.Query(query)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer rows.Close()
|
||
|
||
var pending []models.FollowUp
|
||
for rows.Next() {
|
||
var f models.FollowUp
|
||
var dealStatus, customerLevel, industry sql.NullString
|
||
var notificationSent int
|
||
|
||
err := rows.Scan(
|
||
&f.ID, &f.CreatedAt, &f.CustomerName,
|
||
&dealStatus, &customerLevel, &industry,
|
||
&f.FollowUpTime, ¬ificationSent,
|
||
)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
f.DealStatus = dealStatus.String
|
||
f.CustomerLevel = customerLevel.String
|
||
f.Industry = industry.String
|
||
f.NotificationSent = notificationSent == 1
|
||
|
||
pending = append(pending, f)
|
||
}
|
||
|
||
return pending, rows.Err()
|
||
}
|
||
|
||
func (fs *mysqlFollowUpStorage) MarkNotificationSent(id string) error {
|
||
query := `UPDATE followups SET notification_sent = 1 WHERE id = ?`
|
||
_, err := fs.db.Exec(query, id)
|
||
return err
|
||
}
|
||
|
||
func generateFollowUpMySQLUUID() 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)
|
||
}
|