forked from ebhomengo/niki
improve structure of campaign & adding interfaces
This commit is contained in:
parent
588cf26ef4
commit
d57adaebec
|
|
@ -1,4 +1,4 @@
|
|||
package domain
|
||||
package entity
|
||||
|
||||
import "time"
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
package mysql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"git.gocasts.ir/ebhomengo/niki/domain/campaign/entity"
|
||||
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
|
||||
"git.gocasts.ir/ebhomengo/niki/repository/mysql"
|
||||
"git.gocasts.ir/ebhomengo/niki/types"
|
||||
)
|
||||
|
||||
type DB struct {
|
||||
conn *mysql.DB
|
||||
}
|
||||
|
||||
func New(db *mysql.DB) *DB {
|
||||
return &DB{conn: db}
|
||||
}
|
||||
|
||||
// CreateCampaign creates a new campaign
|
||||
func (d *DB) CreateAndSave(ctx context.Context, campaign entity.Campaign) (types.ID, error) {
|
||||
const Op = "repository.mysql.campaign.create"
|
||||
|
||||
tx, err := d.conn.Conn().BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return 0, richerror.New(Op).WithErr(err)
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
query := `INSERT INTO campaigns (title, description, goal_amount, raised_amount,
|
||||
status, deadline_at ,admin_id , created_at )
|
||||
VALUES (?, ?, ?, ?, ?, ?, ? , NOW() )`
|
||||
|
||||
result, err := tx.ExecContext(ctx, query,
|
||||
campaign.Title,
|
||||
campaign.Description,
|
||||
campaign.GoalAmount,
|
||||
campaign.RaisedAmount,
|
||||
campaign.Status,
|
||||
campaign.DeadlineAt,
|
||||
campaign.AdminID,
|
||||
campaign.CreatedAt,
|
||||
)
|
||||
if err != nil {
|
||||
return 0, richerror.New(Op).WithErr(err)
|
||||
}
|
||||
|
||||
campaignID, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
return 0, richerror.New(Op).WithErr(err)
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return 0, richerror.New(Op).WithErr(err)
|
||||
}
|
||||
|
||||
return types.ID(campaignID), nil
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
package mysql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"git.gocasts.ir/ebhomengo/niki/campaign/entity"
|
||||
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
|
||||
"git.gocasts.ir/ebhomengo/niki/repository/mysql"
|
||||
"git.gocasts.ir/ebhomengo/niki/types"
|
||||
)
|
||||
|
||||
|
||||
|
||||
type CampaignRepository interface {
|
||||
CreateAndSave(ctx context.Context, campaign *Campaign) error
|
||||
FindByID(ctx context.Context, id ID) (*Campaign, error)
|
||||
List(ctx context.Context, status CampaignStatus, limit, offset int) ([]*Campaign, error)
|
||||
Delete(ctx context.Context, id ID) error
|
||||
Update(ctx context.Context, campaign *Campaign) error
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
|
@ -1,48 +1,50 @@
|
|||
package campaign
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors" // For standard errors
|
||||
"fmt"
|
||||
"git.gocasts.ir/ebhomengo/niki/domain/campaign/entity"
|
||||
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
|
||||
"git.gocasts.ir/ebhomengo/niki/types"
|
||||
"time"
|
||||
|
||||
"your_project/domain"
|
||||
"your_project/pkg/richerror"
|
||||
"your_project/repository"
|
||||
)
|
||||
|
||||
type ID = unit64
|
||||
type EntityCampaign = domain.Campaign
|
||||
// CreateCampaign handles creation of a new campaign.
|
||||
func (s *CampaignService) CreateCampaign(ctx context.Context, req entity.Campaign) (types.ID, error) {
|
||||
const op = "service.campaign.create_campaign"
|
||||
|
||||
|
||||
type CampaignServiceImp interface {
|
||||
CreateCampaign(ctx context.Context, req CampaignRepository) (types.ID, error)
|
||||
if err := validateCreateCampaignRequest(req); err != nil {
|
||||
return 0, richerror.New(op).WithErr(err)
|
||||
}
|
||||
|
||||
|
||||
type CampaignService struct {
|
||||
repo repository.CampaignRepository
|
||||
campaign := entity.Campaign{
|
||||
Title: req.Title,
|
||||
Description: req.Description,
|
||||
GoalAmount: req.GoalAmount,
|
||||
RaisedAmount: 0,
|
||||
Status: req.Status,
|
||||
DeadlineAt: req.DeadlineAt,
|
||||
AdminID: req.AdminID,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
func NewCampaignService(
|
||||
campaignRepo repository.CampaignRepository,
|
||||
) *CampaignService {
|
||||
return &CampaignService{
|
||||
repo: campaignRepo,
|
||||
}
|
||||
id, err := s.repo.Create(ctx, campaign)
|
||||
if err != nil {
|
||||
return 0, richerror.New(op).WithErr(err)
|
||||
}
|
||||
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (s *CampaignService) CreateCampaign(ctx context.Context, req CreateCampaignRequest) (ID, error) {
|
||||
const Op = "service.campaign.create_campaign"
|
||||
|
||||
func validateCreateCampaignRequest(req entity.Campaign) error {
|
||||
if req.Title == "" {
|
||||
return 0, richerror.New(Op).WithMessage("title is required")
|
||||
return errRequired("title")
|
||||
}
|
||||
if req.GoalAmount <= 0 {
|
||||
return 0, richerror.New(Op).WithMessage("goal_amount must be greater than 0")
|
||||
return errInvalid("goal_amount must be greater than 0")
|
||||
}
|
||||
if req.AdminID == 0 {
|
||||
return 0, richerror.New(Op).WithMessage("admin_id is required")
|
||||
return errRequired("admin_id")
|
||||
}
|
||||
|
||||
validStatuses := map[string]bool{
|
||||
|
|
@ -50,32 +52,20 @@ func (s *CampaignService) CreateCampaign(ctx context.Context, req CreateCampaign
|
|||
"active": true,
|
||||
"completed": true,
|
||||
"cancelled": true,
|
||||
"paused": true
|
||||
"paused": true,
|
||||
}
|
||||
if !validStatuses[req.Status] {
|
||||
return 0, richerror.New(Op).WithMessage("invalid status provided")
|
||||
if !validStatuses[string(req.Status)] {
|
||||
return errInvalid("invalid status provided")
|
||||
}
|
||||
|
||||
|
||||
newCampaign := &EntityCampaign{
|
||||
Title: req.Title,
|
||||
Description: req.Description,
|
||||
GoalAmount: req.GoalAmount,
|
||||
RaisedAmount: 0, // Initially 0
|
||||
Status: EntityCampaign.Status(req.Status),
|
||||
DeadlineAt: req.DeadlineAt,
|
||||
AdminID: req.AdminID,
|
||||
CreatedAt: time.Now(),
|
||||
return nil
|
||||
}
|
||||
|
||||
createdCampaignID, err := s.repo.CreateAndSave(ctx, newCampaign)
|
||||
if err != nil {
|
||||
return 0, richerror.New(Op).WithErr(err)
|
||||
// --- Helpers ---
|
||||
func errRequired(field string) error {
|
||||
return fmt.Errorf("%s is required", field)
|
||||
}
|
||||
|
||||
return createdCampaignID, nil
|
||||
func errInvalid(msg string) error {
|
||||
return fmt.Errorf(msg)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "fmt"
|
||||
"git.gocasts.ir/ebhomengo/niki/domain/campaign/entity"
|
||||
"git.gocasts.ir/ebhomengo/niki/types"
|
||||
)
|
||||
|
||||
type CampaignFilterParam struct {
|
||||
AdminID types.ID
|
||||
Status string
|
||||
//nil true false
|
||||
IsArchived *bool
|
||||
}
|
||||
|
||||
type CampaignStorage interface {
|
||||
Create(ctx context.Context, c entity.Campaign) (types.ID, error) // باید ID برگرداند
|
||||
Update(ctx context.Context, c entity.Campaign) error
|
||||
FindByID(ctx context.Context, id types.ID) (entity.Campaign, error)
|
||||
FindAll(ctx context.Context, filter CampaignFilterParam) ([]entity.Campaign, error)
|
||||
Archive(ctx context.Context, id types.ID) error // instead Delete
|
||||
TotalDonations(ctx context.Context, campaignID types.ID) (int64, error)
|
||||
}
|
||||
|
||||
type CampaignService struct {
|
||||
repo CampaignStorage
|
||||
}
|
||||
|
||||
// NewCampaignService constructs a new CampaignService.
|
||||
func NewCampaignService(storage CampaignStorage) *CampaignService {
|
||||
return &CampaignService{
|
||||
repo: storage,
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ package mysql
|
|||
|
||||
import (
|
||||
"context"
|
||||
"git.gocasts.ir/ebhomengo/niki/campaign/entity"
|
||||
"git.gocasts.ir/ebhomengo/niki/donate_app/service/entity"
|
||||
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
|
||||
"git.gocasts.ir/ebhomengo/niki/repository/mysql"
|
||||
"git.gocasts.ir/ebhomengo/niki/types"
|
||||
|
|
@ -16,11 +16,9 @@ func New(db *mysql.DB) *DB {
|
|||
return &DB{conn: db}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// CreateCampaign creates a new campaign
|
||||
func (d *DB) CreateAndSave(ctx context.Context, campaign entity.Campaign) (types.ID, error) {
|
||||
const Op = "repository.mysql.campaign.create"
|
||||
// Create adds a new participant to a campaign
|
||||
func (d *DB) CreateCampaignParticipants(ctx context.Context, participant entity.CampaignParticipant) (types.ID, error) {
|
||||
const Op = "repository.mysql.campaign_participant.create"
|
||||
|
||||
tx, err := d.conn.Conn().BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
|
|
@ -28,54 +26,17 @@ func (d *DB) CreateAndSave(ctx context.Context, campaign entity.Campaign) (types
|
|||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
query := `INSERT INTO campaigns (title, description, goal_amount, raised_amount,
|
||||
status, deadline_at ,admin_id , created_at )
|
||||
VALUES (?, ?, ?, ?, ?, ?, ? , NOW() )`
|
||||
|
||||
result, err := tx.ExecContext(ctx, query,
|
||||
campaign.Title,
|
||||
campaign.Description,
|
||||
campaign.GoalAmount,
|
||||
campaign.RaisedAmount,
|
||||
campaign.Status,
|
||||
campaign.DeadlineAt,
|
||||
campaign.AdminID,
|
||||
campaign.CreatedAt,
|
||||
|
||||
|
||||
)
|
||||
if err != nil {
|
||||
return 0, richerror.New(Op).WithErr(err)
|
||||
}
|
||||
|
||||
campaignID, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
return 0, richerror.New(Op).WithErr(err)
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return 0, richerror.New(Op).WithErr(err)
|
||||
}
|
||||
|
||||
return types.ID(campaignID), nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Create adds a new participant to a campaign
|
||||
func (d *DB) CreateCampaignParticipants(ctx context.Context, participant entity.CampaignParticipant) (types.ID, error) {
|
||||
const Op = "repository.mysql.campaign_participant.create"
|
||||
|
||||
query := `INSERT INTO campaign_participants (id,campaign_id, user_id, amount , created_at)
|
||||
VALUES (?, ?, ? , ? , NOW())`
|
||||
|
||||
result, err := d.conn.ExecContext(ctx, query,
|
||||
result, err := tx.ExecContext(ctx, query,
|
||||
participant.ID,
|
||||
participant.CampaignID,
|
||||
participant.UserID,
|
||||
participant.Amount,
|
||||
participant.CreatedAt
|
||||
participant.CreatedAt,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return 0, richerror.New(Op).WithErr(err)
|
||||
}
|
||||
|
|
@ -84,11 +45,10 @@ func (d *DB) CreateCampaignParticipants(ctx context.Context, participant entity.
|
|||
if err != nil {
|
||||
return 0, richerror.New(Op).WithErr(err)
|
||||
}
|
||||
|
||||
return types.ID(id), nil
|
||||
if err := tx.Commit(); err != nil {
|
||||
return 0, richerror.New(Op).WithErr(err)
|
||||
}
|
||||
|
||||
return types.ID(id), nil
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
package service
|
||||
package entity
|
||||
|
||||
import "time"
|
||||
|
||||
type ID uint64
|
||||
|
||||
|
||||
type CampaignParticipant struct {
|
||||
ID ID `json:"id"`
|
||||
CampaignID ID `json:"campaign_id"`
|
||||
UserID ID `json:"user_id"`
|
||||
Amount float64 `json:"amount"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue