crm/internal/handlers/customer_handler.go
2026-01-07 17:34:34 +08:00

359 lines
8.5 KiB
Go

package handlers
import (
"encoding/csv"
"encoding/json"
"io"
"net/http"
"strconv"
"strings"
"time"
"crm-go/internal/storage"
"crm-go/models"
"github.com/xuri/excelize/v2"
)
type CustomerHandler struct {
storage storage.CustomerStorage
}
func NewCustomerHandler(storage storage.CustomerStorage) *CustomerHandler {
return &CustomerHandler{
storage: storage,
}
}
func (h *CustomerHandler) GetCustomers(w http.ResponseWriter, r *http.Request) {
page := 1
pageSize := 10
if pageStr := r.URL.Query().Get("page"); pageStr != "" {
if p, err := strconv.Atoi(pageStr); err == nil && p > 0 {
page = p
}
}
if pageSizeStr := r.URL.Query().Get("pageSize"); pageSizeStr != "" {
if ps, err := strconv.Atoi(pageSizeStr); err == nil && ps > 0 {
pageSize = ps
}
}
customers, err := h.storage.GetAllCustomers()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
total := len(customers)
totalPages := (total + pageSize - 1) / pageSize
start := (page - 1) * pageSize
end := start + pageSize
if start >= total {
customers = []models.Customer{}
} else if end > total {
customers = customers[start:]
} else {
customers = customers[start:end]
}
response := map[string]interface{}{
"customers": customers,
"total": total,
"page": page,
"pageSize": pageSize,
"totalPages": totalPages,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
func (h *CustomerHandler) GetCustomerByID(w http.ResponseWriter, r *http.Request) {
// Extract customer ID from URL path
urlPath := r.URL.Path
// The path should be /api/customers/{id}
pathParts := strings.Split(urlPath, "/")
if len(pathParts) < 4 {
http.Error(w, "Invalid URL format", http.StatusBadRequest)
return
}
id := pathParts[3] // Get the ID from the path
// Remove query parameters if any
if idx := strings.Index(id, "?"); idx != -1 {
id = id[:idx]
}
customer, err := h.storage.GetCustomerByID(id)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if customer == nil {
http.Error(w, "Customer not found", http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(customer)
}
func (h *CustomerHandler) CreateCustomer(w http.ResponseWriter, r *http.Request) {
var req models.CreateCustomerRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
customer := models.Customer{
CustomerName: req.CustomerName,
IntendedProduct: req.IntendedProduct,
Version: req.Version,
Description: req.Description,
Solution: req.Solution,
Type: req.Type,
Module: req.Module,
StatusProgress: req.StatusProgress,
Reporter: req.Reporter,
CreatedAt: time.Now(),
}
if err := h.storage.CreateCustomer(customer); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(customer)
}
func (h *CustomerHandler) UpdateCustomer(w http.ResponseWriter, r *http.Request) {
// Extract customer ID from URL path
urlPath := r.URL.Path
// The path should be /api/customers/{id}
pathParts := strings.Split(urlPath, "/")
if len(pathParts) < 4 {
http.Error(w, "Invalid URL format", http.StatusBadRequest)
return
}
id := pathParts[3] // Get the ID from the path
// Remove query parameters if any
if idx := strings.Index(id, "?"); idx != -1 {
id = id[:idx]
}
var req models.UpdateCustomerRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
if err := h.storage.UpdateCustomer(id, req); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}
func (h *CustomerHandler) DeleteCustomer(w http.ResponseWriter, r *http.Request) {
// Extract customer ID from URL path
urlPath := r.URL.Path
// The path should be /api/customers/{id}
pathParts := strings.Split(urlPath, "/")
if len(pathParts) < 4 {
http.Error(w, "Invalid URL format", http.StatusBadRequest)
return
}
id := pathParts[3] // Get the ID from the path
// Remove query parameters if any
if idx := strings.Index(id, "?"); idx != -1 {
id = id[:idx]
}
if err := h.storage.DeleteCustomer(id); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}
func (h *CustomerHandler) ImportCustomers(w http.ResponseWriter, r *http.Request) {
err := r.ParseMultipartForm(10 << 20) // 10 MB
if err != nil {
http.Error(w, "Unable to parse form", http.StatusBadRequest)
return
}
file, handler, err := r.FormFile("file")
if err != nil {
http.Error(w, "Unable to get file", http.StatusBadRequest)
return
}
defer file.Close()
// Check file extension
filename := handler.Filename
isExcel := strings.HasSuffix(strings.ToLower(filename), ".xlsx")
importedCount := 0
duplicateCount := 0
if isExcel {
// Parse as Excel file
f, err := excelize.OpenReader(file)
if err != nil {
http.Error(w, "Unable to open Excel file", http.StatusBadRequest)
return
}
defer f.Close()
// Get the first sheet
sheets := f.GetSheetList()
if len(sheets) == 0 {
http.Error(w, "No sheets found in Excel file", http.StatusBadRequest)
return
}
// Read all rows from the first sheet
rows, err := f.GetRows(sheets[0])
if err != nil {
http.Error(w, "Unable to read Excel file", http.StatusBadRequest)
return
}
// Skip header row if exists
for i, row := range rows {
if i == 0 {
continue // Skip header row
}
if len(row) < 2 {
continue // Skip rows with insufficient data
}
customer := models.Customer{
ID: "", // Will be generated by the storage
CreatedAt: time.Now(),
CustomerName: getValue(row, 0),
Version: getValue(row, 1),
Description: getValue(row, 2),
Solution: getValue(row, 3),
Type: getValue(row, 4),
Module: getValue(row, 5),
StatusProgress: getValue(row, 6),
Reporter: getValue(row, 7),
IntendedProduct: getValue(row, 8),
}
// Check for duplicate
exists, err := h.storage.CustomerExists(customer)
if err != nil {
http.Error(w, "Error checking for duplicate customer", http.StatusInternalServerError)
return
}
if exists {
duplicateCount++
continue // Skip duplicate
}
if err := h.storage.CreateCustomer(customer); err != nil {
http.Error(w, "Error saving customer", http.StatusInternalServerError)
return
}
importedCount++
}
} else {
// Parse as CSV
content, err := io.ReadAll(file)
if err != nil {
http.Error(w, "Unable to read file", http.StatusBadRequest)
return
}
reader := csv.NewReader(strings.NewReader(string(content)))
records, err := reader.ReadAll()
if err != nil {
http.Error(w, "Unable to parse CSV file", http.StatusBadRequest)
return
}
// Skip header row if exists
for i, row := range records {
if i == 0 {
continue // Skip header row
}
if len(row) < 2 {
continue // Skip rows with insufficient data
}
customer := models.Customer{
ID: "", // Will be generated by the storage
CreatedAt: time.Now(),
CustomerName: getValue(row, 0),
Version: getValue(row, 1),
Description: getValue(row, 2),
Solution: getValue(row, 3),
Type: getValue(row, 4),
Module: getValue(row, 5),
StatusProgress: getValue(row, 6),
Reporter: getValue(row, 7),
IntendedProduct: getValue(row, 8),
}
// Check for duplicate
exists, err := h.storage.CustomerExists(customer)
if err != nil {
http.Error(w, "Error checking for duplicate customer", http.StatusInternalServerError)
return
}
if exists {
duplicateCount++
continue // Skip duplicate
}
if err := h.storage.CreateCustomer(customer); err != nil {
http.Error(w, "Error saving customer", http.StatusInternalServerError)
return
}
importedCount++
}
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]interface{}{
"message": "Customers imported successfully",
"importedCount": importedCount,
"duplicateCount": duplicateCount,
})
}
func getValue(row []string, index int) string {
if index < len(row) {
return row[index]
}
return ""
}