This commit is contained in:
aditya.siregar
2025-04-05 11:28:06 +08:00
parent 118ec58521
commit c642c5c61b
35 changed files with 1194 additions and 212 deletions
+148 -7
View File
@@ -1,6 +1,8 @@
package customer
import (
"context"
errors2 "enaklo-pos-be/internal/common/errors"
"enaklo-pos-be/internal/common/logger"
"enaklo-pos-be/internal/common/mycontext"
"enaklo-pos-be/internal/constants"
@@ -8,7 +10,9 @@ import (
"enaklo-pos-be/internal/utils"
"github.com/pkg/errors"
"go.uber.org/zap"
"log"
"strings"
"time"
)
type Repository interface {
@@ -16,26 +20,35 @@ type Repository interface {
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
AddPoints(ctx mycontext.Context, id int64, points int, reference string) error
FindSequence(ctx mycontext.Context, partnerID int64) (int64, error)
GetAllCustomers(ctx mycontext.Context, req entity.MemberSearch) (entity.MemberList, int, error)
VerifyOTP(ctx mycontext.Context, verificationHash string, otpCode string) (int64, error)
}
type Service interface {
ResolveCustomer(ctx mycontext.Context, req *entity.CustomerResolutionRequest) (int64, error)
AddPoints(ctx mycontext.Context, customerID int64, points int) error
AddPoints(ctx mycontext.Context, customerID int64, points int, reference string) error
GetCustomer(ctx mycontext.Context, id int64) (*entity.Customer, error)
CustomerCheck(ctx mycontext.Context, req *entity.CustomerResolutionRequest) (*entity.CustomerCheckResponse, error)
GetAllCustomers(ctx mycontext.Context, req *entity.MemberSearch) (*entity.MemberList, int, error)
RegistrationMember(ctx mycontext.Context, req *entity.Customer) (*entity.Customer, error)
VerifyOTP(ctx mycontext.Context, verificationID, otpCode string) error
}
type EmailService interface {
SendEmailTransactional(ctx context.Context, param entity.SendEmailNotificationParam) error
}
type customerSvc struct {
repo Repository
repo Repository
notification EmailService
}
func New(repo Repository) Service {
func New(repo Repository, notification EmailService) Service {
return &customerSvc{
repo: repo,
repo: repo,
notification: notification,
}
}
@@ -106,12 +119,68 @@ func (s *customerSvc) ResolveCustomer(ctx mycontext.Context, req *entity.Custome
return customer.ID, nil
}
func (s *customerSvc) AddPoints(ctx mycontext.Context, customerID int64, points int) error {
func (s *customerSvc) RegistrationMember(ctx mycontext.Context, req *entity.Customer) (*entity.Customer, error) {
if req.Email == "" && req.PhoneNumber == "" {
return nil, errors2.ErrorPhoneNumberEmailIsRequired
}
if req.PhoneNumber != "" {
customer, err := s.repo.FindByPhone(ctx, req.PhoneNumber)
if err != nil && !strings.Contains(err.Error(), "not found") {
return nil, errors2.ErrorInternalServer
}
if customer != nil {
return nil, errors2.ErrorPhoneNumberIsAlreadyRegistered
}
}
if req.Email != "" {
customer, err := s.repo.FindByEmail(ctx, req.Email)
if err != nil && !strings.Contains(err.Error(), "not found") {
return nil, errors2.ErrorInternalServer
}
if customer != nil {
return nil, errors2.ErrorEmailIsAlreadyRegistered
}
}
newCustomer := &entity.Customer{
Name: req.Name,
Email: req.Email,
Phone: req.PhoneNumber,
CreatedAt: constants.TimeNow(),
UpdatedAt: constants.TimeNow(),
BirthDate: req.BirthDate,
Password: req.HashedPassword(),
}
customer, err := s.repo.Create(ctx, newCustomer)
if err != nil {
logger.ContextLogger(ctx).Error("failed to create customer", zap.Error(err))
return nil, errors2.ErrorInternalServer
}
errs := s.sendRegistrationOTP(ctx, &entity.MemberRegistration{
Name: customer.Name,
Email: customer.Email,
OTP: customer.OTP,
})
if err != nil {
logger.ContextLogger(ctx).Error("failed to send OTP", zap.Error(errs))
}
return customer, nil
}
func (s *customerSvc) AddPoints(ctx mycontext.Context, customerID int64, points int, reference string) error {
if points <= 0 {
return nil
}
err := s.repo.AddPoints(ctx, customerID, points)
err := s.repo.AddPoints(ctx, customerID, points, reference)
if err != nil {
return errors.Wrap(err, "failed to add points to customer")
}
@@ -202,3 +271,75 @@ func (s *customerSvc) GetAllCustomers(ctx mycontext.Context, req *entity.MemberS
return &customers, totalCount, nil
}
func (s *customerSvc) sendRegistrationOTP(
ctx mycontext.Context,
registration *entity.MemberRegistration,
) error {
emailData := map[string]interface{}{
"UserName": registration.Name,
"OTPCode": registration.OTP,
}
err := s.notification.SendEmailTransactional(ctx, entity.SendEmailNotificationParam{
Sender: "noreply@enaklo.co.id",
Recipient: registration.Email,
Subject: "Enaklo - Registration Verification Code",
TemplateName: "member_registration_otp",
TemplatePath: "templates/member_registration_otp.html",
Data: emailData,
})
if err != nil {
return err
}
return nil
}
func (s *customerSvc) VerifyOTP(ctx mycontext.Context, verificationID, otpCode string) error {
customerID, err := s.repo.VerifyOTP(ctx, verificationID, otpCode)
if err != nil {
return errors.Wrap(err, "verification failed")
}
customer, _ := s.repo.FindByID(ctx, customerID)
go func(customer *entity.Customer) {
newCtx := context.Background()
defer func() {
if r := recover(); r != nil {
log.Printf("Recovered from panic in sendWelcomeEmail: %v", r)
}
}()
s.sendWelcomeEmail(newCtx, customer)
}(customer)
return nil
}
func (s *customerSvc) sendWelcomeEmail(
ctx context.Context,
customer *entity.Customer,
) error {
welcomeData := map[string]interface{}{
"UserName": customer.Name,
"MemberID": customer.CustomerID,
"PointsName": "EnakPoint",
"PointsBalance": customer.Points,
"RedeemLink": "https://enaklo.co.id/redeem",
"CurrentDate": time.Now().Format("01-2006"),
}
return s.notification.SendEmailTransactional(ctx, entity.SendEmailNotificationParam{
Sender: "noreply@enaklo.co.id",
Recipient: customer.Email,
Subject: "Welcome to Enaklo Membership Program",
TemplateName: "welcome_member",
TemplatePath: "templates/welcome_member.html",
Data: welcomeData,
})
}