2025-03-15 15:51:18 +08:00

235 lines
6.2 KiB
Go

package repository
import (
"enaklo-pos-be/internal/common/mycontext"
"enaklo-pos-be/internal/entity"
"enaklo-pos-be/internal/repository/models"
"github.com/pkg/errors"
"gorm.io/gorm"
"time"
)
type CustomerRepo interface {
Create(ctx mycontext.Context, customer *entity.Customer) (*entity.Customer, error)
FindByID(ctx mycontext.Context, id int64) (*entity.Customer, error)
FindByPhone(ctx mycontext.Context, phone string) (*entity.Customer, error)
FindByEmail(ctx mycontext.Context, email string) (*entity.Customer, error)
AddPoints(ctx mycontext.Context, id int64, points int) error
FindSequence(ctx mycontext.Context, partnerID int64) (int64, error)
GetAllCustomers(ctx mycontext.Context, req entity.MemberSearch) (entity.MemberList, int, error)
}
type customerRepository struct {
db *gorm.DB
}
func NewCustomerRepository(db *gorm.DB) *customerRepository {
return &customerRepository{db: db}
}
func (r *customerRepository) Create(ctx mycontext.Context, customer *entity.Customer) (*entity.Customer, error) {
customerDB := r.toCustomerDBModel(customer)
if err := r.db.Create(&customerDB).Error; err != nil {
return nil, errors.Wrap(err, "failed to insert customer")
}
customer.ID = customerDB.ID
return customer, nil
}
func (r *customerRepository) FindByID(ctx mycontext.Context, id int64) (*entity.Customer, error) {
var customerDB models.CustomerDB
if err := r.db.First(&customerDB, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("customer not found")
}
return nil, errors.Wrap(err, "failed to find customer")
}
customer := r.toDomainCustomerModel(&customerDB)
return customer, nil
}
func (r *customerRepository) FindByPhone(ctx mycontext.Context, phone string) (*entity.Customer, error) {
var customerDB models.CustomerDB
if err := r.db.Where("phone = ?", phone).First(&customerDB).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("customer not found")
}
return nil, errors.Wrap(err, "failed to find customer by phone")
}
customer := r.toDomainCustomerModel(&customerDB)
return customer, nil
}
func (r *customerRepository) FindByEmail(ctx mycontext.Context, email string) (*entity.Customer, error) {
var customerDB models.CustomerDB
if err := r.db.Where("email = ?", email).First(&customerDB).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("customer not found")
}
return nil, errors.Wrap(err, "failed to find customer by email")
}
customer := r.toDomainCustomerModel(&customerDB)
return customer, nil
}
func (r *customerRepository) AddPoints(ctx mycontext.Context, id int64, points int) error {
now := time.Now()
result := r.db.Model(&models.CustomerDB{}).
Where("id = ?", id).
Updates(map[string]interface{}{
"points": gorm.Expr("points + ?", points),
"updated_at": now,
})
if result.Error != nil {
return errors.Wrap(result.Error, "failed to add points to customer")
}
if result.RowsAffected == 0 {
return errors.New("customer not found")
}
return nil
}
func (r *customerRepository) toCustomerDBModel(customer *entity.Customer) models.CustomerDB {
return models.CustomerDB{
ID: customer.ID,
Name: customer.Name,
Email: customer.Email,
Phone: customer.Phone,
Points: customer.Points,
CreatedAt: customer.CreatedAt,
UpdatedAt: customer.UpdatedAt,
CustomerID: customer.CustomerID,
BirthDate: customer.BirthDate,
}
}
func (r *customerRepository) FindSequence(ctx mycontext.Context, partnerID int64) (int64, error) {
tx := r.db.Begin()
if tx.Error != nil {
return 0, errors.Wrap(tx.Error, "failed to begin transaction")
}
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
var sequence models.PartnerMemberSequence
result := tx.Where("partner_id = ?", partnerID).First(&sequence)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
now := time.Now()
newSequence := models.PartnerMemberSequence{
PartnerID: partnerID,
LastSequence: 1,
UpdatedAt: now,
}
if err := tx.Create(&newSequence).Error; err != nil {
tx.Rollback()
return 0, errors.Wrap(err, "failed to create new sequence")
}
if err := tx.Commit().Error; err != nil {
return 0, errors.Wrap(err, "failed to commit transaction")
}
return 1, nil
}
tx.Rollback()
return 0, errors.Wrap(result.Error, "failed to query sequence")
}
newSequenceValue := sequence.LastSequence + 1
updates := map[string]interface{}{
"last_sequence": newSequenceValue,
"updated_at": time.Now(),
}
if err := tx.Model(&sequence).Updates(updates).Error; err != nil {
tx.Rollback()
return 0, errors.Wrap(err, "failed to update sequence")
}
if err := tx.Commit().Error; err != nil {
return 0, errors.Wrap(err, "failed to commit transaction")
}
return newSequenceValue, nil
}
func (r *customerRepository) toDomainCustomerModel(dbModel *models.CustomerDB) *entity.Customer {
return &entity.Customer{
ID: dbModel.ID,
Name: dbModel.Name,
Email: dbModel.Email,
Phone: dbModel.Phone,
Points: dbModel.Points,
CreatedAt: dbModel.CreatedAt,
UpdatedAt: dbModel.UpdatedAt,
CustomerID: dbModel.CustomerID,
BirthDate: dbModel.BirthDate,
}
}
func (r *customerRepository) GetAllCustomers(ctx mycontext.Context, req entity.MemberSearch) (entity.MemberList, int, error) {
if req.Limit <= 0 {
req.Limit = 10
}
if req.Offset < 0 {
req.Offset = 0
}
query := r.db.Model(&models.CustomerDB{})
if req.Search != "" {
searchTerm := "%" + req.Search + "%"
query = query.Where(
"name ILIKE ? OR email ILIKE ? OR phone ILIKE ?",
searchTerm, searchTerm, searchTerm,
)
}
var totalCount int64
if err := query.Count(&totalCount).Error; err != nil {
return nil, 0, errors.Wrap(err, "failed to count customers")
}
var customersDB []models.CustomerDB
result := query.
Order("created_at DESC").
Limit(req.Limit).
Offset(req.Offset).
Find(&customersDB)
if result.Error != nil {
return nil, 0, errors.Wrap(result.Error, "failed to retrieve customers")
}
customers := make(entity.MemberList, len(customersDB))
for i, customerDB := range customersDB {
customers[i] = r.toDomainCustomerModel(&customerDB)
}
return customers, int(totalCount), nil
}