Add Campaign

This commit is contained in:
Aditya Siregar
2025-09-17 23:55:11 +07:00
parent 201e24041b
commit 155016dec8
26 changed files with 3399 additions and 1 deletions
+268
View File
@@ -0,0 +1,268 @@
package repository
import (
"context"
"fmt"
"apskel-pos-be/internal/entities"
"gorm.io/gorm"
)
type CampaignRepository interface {
Create(ctx context.Context, campaign *entities.Campaign) error
GetByID(ctx context.Context, id string) (*entities.Campaign, error)
List(ctx context.Context, req *entities.ListCampaignsRequest) ([]entities.Campaign, int64, error)
Update(ctx context.Context, campaign *entities.Campaign) error
Delete(ctx context.Context, id string) error
GetActiveCampaigns(ctx context.Context) ([]entities.Campaign, error)
GetCampaignsForApp(ctx context.Context) ([]entities.Campaign, error)
}
type campaignRepository struct {
db *gorm.DB
}
func NewCampaignRepository(db *gorm.DB) CampaignRepository {
return &campaignRepository{
db: db,
}
}
func (r *campaignRepository) Create(ctx context.Context, campaign *entities.Campaign) error {
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
// Create campaign first
if err := tx.Create(campaign).Error; err != nil {
return fmt.Errorf("failed to create campaign: %w", err)
}
// Create campaign rules
if len(campaign.Rules) > 0 {
for i := range campaign.Rules {
campaign.Rules[i].CampaignID = campaign.ID
}
if err := tx.Create(&campaign.Rules).Error; err != nil {
return fmt.Errorf("failed to create campaign rules: %w", err)
}
}
return nil
})
}
func (r *campaignRepository) GetByID(ctx context.Context, id string) (*entities.Campaign, error) {
var campaign entities.Campaign
if err := r.db.WithContext(ctx).Preload("Rules").Where("id = ?", id).First(&campaign).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, fmt.Errorf("campaign not found")
}
return nil, fmt.Errorf("failed to get campaign: %w", err)
}
return &campaign, nil
}
func (r *campaignRepository) List(ctx context.Context, req *entities.ListCampaignsRequest) ([]entities.Campaign, int64, error) {
var campaigns []entities.Campaign
var total int64
query := r.db.WithContext(ctx).Model(&entities.Campaign{})
// Apply filters
if req.Search != "" {
query = query.Where("name ILIKE ? OR description ILIKE ?", "%"+req.Search+"%", "%"+req.Search+"%")
}
if req.Type != "" {
query = query.Where("type = ?", req.Type)
}
if req.IsActive != nil {
query = query.Where("is_active = ?", *req.IsActive)
}
if req.ShowOnApp != nil {
query = query.Where("show_on_app = ?", *req.ShowOnApp)
}
if req.StartDate != nil {
query = query.Where("start_date >= ?", *req.StartDate)
}
if req.EndDate != nil {
query = query.Where("end_date <= ?", *req.EndDate)
}
// Count total records
if err := query.Count(&total).Error; err != nil {
return nil, 0, fmt.Errorf("failed to count campaigns: %w", err)
}
// Apply pagination
if req.Page > 0 && req.Limit > 0 {
offset := (req.Page - 1) * req.Limit
query = query.Offset(offset).Limit(req.Limit)
}
// Apply sorting
query = query.Order("position ASC, created_at DESC")
if err := query.Preload("Rules").Find(&campaigns).Error; err != nil {
return nil, 0, fmt.Errorf("failed to list campaigns: %w", err)
}
return campaigns, total, nil
}
func (r *campaignRepository) Update(ctx context.Context, campaign *entities.Campaign) error {
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
// Update campaign
if err := tx.Save(campaign).Error; err != nil {
return fmt.Errorf("failed to update campaign: %w", err)
}
// Delete existing rules
if err := tx.Where("campaign_id = ?", campaign.ID).Delete(&entities.CampaignRule{}).Error; err != nil {
return fmt.Errorf("failed to delete existing campaign rules: %w", err)
}
// Create new rules
if len(campaign.Rules) > 0 {
for i := range campaign.Rules {
campaign.Rules[i].CampaignID = campaign.ID
}
if err := tx.Create(&campaign.Rules).Error; err != nil {
return fmt.Errorf("failed to create campaign rules: %w", err)
}
}
return nil
})
}
func (r *campaignRepository) Delete(ctx context.Context, id string) error {
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
// Delete campaign (rules will be deleted by CASCADE)
if err := tx.Where("id = ?", id).Delete(&entities.Campaign{}).Error; err != nil {
return fmt.Errorf("failed to delete campaign: %w", err)
}
return nil
})
}
func (r *campaignRepository) GetActiveCampaigns(ctx context.Context) ([]entities.Campaign, error) {
var campaigns []entities.Campaign
now := "now()"
if err := r.db.WithContext(ctx).Where("is_active = ? AND start_date <= ? AND end_date >= ?", true, now, now).Preload("Rules").Find(&campaigns).Error; err != nil {
return nil, fmt.Errorf("failed to get active campaigns: %w", err)
}
return campaigns, nil
}
func (r *campaignRepository) GetCampaignsForApp(ctx context.Context) ([]entities.Campaign, error) {
var campaigns []entities.Campaign
now := "now()"
if err := r.db.WithContext(ctx).Where("is_active = ? AND show_on_app = ? AND start_date <= ? AND end_date >= ?", true, true, now, now).Preload("Rules").Order("position ASC").Find(&campaigns).Error; err != nil {
return nil, fmt.Errorf("failed to get campaigns for app: %w", err)
}
return campaigns, nil
}
// Campaign Rule Repository
type CampaignRuleRepository interface {
Create(ctx context.Context, rule *entities.CampaignRule) error
GetByID(ctx context.Context, id string) (*entities.CampaignRule, error)
List(ctx context.Context, req *entities.ListCampaignRulesRequest) ([]entities.CampaignRule, int64, error)
Update(ctx context.Context, rule *entities.CampaignRule) error
Delete(ctx context.Context, id string) error
GetByCampaignID(ctx context.Context, campaignID string) ([]entities.CampaignRule, error)
}
type campaignRuleRepository struct {
db *gorm.DB
}
func NewCampaignRuleRepository(db *gorm.DB) CampaignRuleRepository {
return &campaignRuleRepository{
db: db,
}
}
func (r *campaignRuleRepository) Create(ctx context.Context, rule *entities.CampaignRule) error {
if err := r.db.WithContext(ctx).Create(rule).Error; err != nil {
return fmt.Errorf("failed to create campaign rule: %w", err)
}
return nil
}
func (r *campaignRuleRepository) GetByID(ctx context.Context, id string) (*entities.CampaignRule, error) {
var rule entities.CampaignRule
if err := r.db.WithContext(ctx).Where("id = ?", id).First(&rule).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, fmt.Errorf("campaign rule not found")
}
return nil, fmt.Errorf("failed to get campaign rule: %w", err)
}
return &rule, nil
}
func (r *campaignRuleRepository) List(ctx context.Context, req *entities.ListCampaignRulesRequest) ([]entities.CampaignRule, int64, error) {
var rules []entities.CampaignRule
var total int64
query := r.db.WithContext(ctx).Model(&entities.CampaignRule{})
// Apply filters
if req.CampaignID != "" {
query = query.Where("campaign_id = ?", req.CampaignID)
}
if req.RuleType != "" {
query = query.Where("rule_type = ?", req.RuleType)
}
if req.RewardType != "" {
query = query.Where("reward_type = ?", req.RewardType)
}
// Count total records
if err := query.Count(&total).Error; err != nil {
return nil, 0, fmt.Errorf("failed to count campaign rules: %w", err)
}
// Apply pagination
if req.Page > 0 && req.Limit > 0 {
offset := (req.Page - 1) * req.Limit
query = query.Offset(offset).Limit(req.Limit)
}
// Apply sorting
query = query.Order("created_at DESC")
if err := query.Find(&rules).Error; err != nil {
return nil, 0, fmt.Errorf("failed to list campaign rules: %w", err)
}
return rules, total, nil
}
func (r *campaignRuleRepository) Update(ctx context.Context, rule *entities.CampaignRule) error {
if err := r.db.WithContext(ctx).Save(rule).Error; err != nil {
return fmt.Errorf("failed to update campaign rule: %w", err)
}
return nil
}
func (r *campaignRuleRepository) Delete(ctx context.Context, id string) error {
if err := r.db.WithContext(ctx).Where("id = ?", id).Delete(&entities.CampaignRule{}).Error; err != nil {
return fmt.Errorf("failed to delete campaign rule: %w", err)
}
return nil
}
func (r *campaignRuleRepository) GetByCampaignID(ctx context.Context, campaignID string) ([]entities.CampaignRule, error) {
var rules []entities.CampaignRule
if err := r.db.WithContext(ctx).Where("campaign_id = ?", campaignID).Find(&rules).Error; err != nil {
return nil, fmt.Errorf("failed to get campaign rules by campaign ID: %w", err)
}
return rules, nil
}
+129
View File
@@ -0,0 +1,129 @@
package repository
import (
"context"
"fmt"
"apskel-pos-be/internal/entities"
"gorm.io/gorm"
)
type RewardRepository interface {
Create(ctx context.Context, reward *entities.Reward) error
GetByID(ctx context.Context, id string) (*entities.Reward, error)
List(ctx context.Context, req *entities.ListRewardsRequest) ([]entities.Reward, int64, error)
Update(ctx context.Context, reward *entities.Reward) error
Delete(ctx context.Context, id string) error
UpdateStock(ctx context.Context, id string, newStock int) error
GetByRewardType(ctx context.Context, rewardType entities.RewardType) ([]entities.Reward, error)
}
type rewardRepository struct {
db *gorm.DB
}
func NewRewardRepository(db *gorm.DB) RewardRepository {
return &rewardRepository{
db: db,
}
}
func (r *rewardRepository) Create(ctx context.Context, reward *entities.Reward) error {
if err := r.db.WithContext(ctx).Create(reward).Error; err != nil {
return fmt.Errorf("failed to create reward: %w", err)
}
return nil
}
func (r *rewardRepository) GetByID(ctx context.Context, id string) (*entities.Reward, error) {
var reward entities.Reward
if err := r.db.WithContext(ctx).Where("id = ?", id).First(&reward).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, fmt.Errorf("reward not found")
}
return nil, fmt.Errorf("failed to get reward: %w", err)
}
return &reward, nil
}
func (r *rewardRepository) List(ctx context.Context, req *entities.ListRewardsRequest) ([]entities.Reward, int64, error) {
var rewards []entities.Reward
var total int64
query := r.db.WithContext(ctx).Model(&entities.Reward{})
// Apply filters
if req.Search != "" {
query = query.Where("name ILIKE ?", "%"+req.Search+"%")
}
if req.RewardType != "" {
query = query.Where("reward_type = ?", req.RewardType)
}
if req.MinPoints != nil {
query = query.Where("cost_points >= ?", *req.MinPoints)
}
if req.MaxPoints != nil {
query = query.Where("cost_points <= ?", *req.MaxPoints)
}
if req.HasStock != nil {
if *req.HasStock {
query = query.Where("stock > 0 OR stock IS NULL")
} else {
query = query.Where("stock <= 0")
}
}
// Count total records
if err := query.Count(&total).Error; err != nil {
return nil, 0, fmt.Errorf("failed to count rewards: %w", err)
}
// Apply pagination
if req.Page > 0 && req.Limit > 0 {
offset := (req.Page - 1) * req.Limit
query = query.Offset(offset).Limit(req.Limit)
}
// Apply sorting
query = query.Order("created_at DESC")
if err := query.Find(&rewards).Error; err != nil {
return nil, 0, fmt.Errorf("failed to list rewards: %w", err)
}
return rewards, total, nil
}
func (r *rewardRepository) Update(ctx context.Context, reward *entities.Reward) error {
if err := r.db.WithContext(ctx).Save(reward).Error; err != nil {
return fmt.Errorf("failed to update reward: %w", err)
}
return nil
}
func (r *rewardRepository) Delete(ctx context.Context, id string) error {
if err := r.db.WithContext(ctx).Where("id = ?", id).Delete(&entities.Reward{}).Error; err != nil {
return fmt.Errorf("failed to delete reward: %w", err)
}
return nil
}
func (r *rewardRepository) UpdateStock(ctx context.Context, id string, newStock int) error {
if err := r.db.WithContext(ctx).Model(&entities.Reward{}).Where("id = ?", id).Update("stock", newStock).Error; err != nil {
return fmt.Errorf("failed to update reward stock: %w", err)
}
return nil
}
func (r *rewardRepository) GetByRewardType(ctx context.Context, rewardType entities.RewardType) ([]entities.Reward, error) {
var rewards []entities.Reward
if err := r.db.WithContext(ctx).Where("reward_type = ?", rewardType).Find(&rewards).Error; err != nil {
return nil, fmt.Errorf("failed to get rewards by type: %w", err)
}
return rewards, nil
}