Merge branch 'develop' into mahsaaghagolzadeh/issue252-create-Payment

This commit is contained in:
hossein 2026-04-22 15:48:20 +00:00
commit 0556f3c345
28 changed files with 373 additions and 280 deletions

View File

@ -1,8 +1,9 @@
package domain package entity
import "time" import (
"git.gocasts.ir/ebhomengo/niki/types"
type ID uint64 "time"
)
type CampaignStatus string type CampaignStatus string
@ -15,7 +16,7 @@ const (
) )
type Campaign struct { type Campaign struct {
ID ID `json:"id"` ID types.ID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
GoalAmount float64 `json:"goal_amount"` GoalAmount float64 `json:"goal_amount"`
@ -23,7 +24,7 @@ type Campaign struct {
Status CampaignStatus `json:"status"` Status CampaignStatus `json:"status"`
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
DeadlineAt *time.Time `json:"deadline_at,omitempty"` DeadlineAt *time.Time `json:"deadline_at,omitempty"`
AdminID ID `json:"creator_id"` AdminID types.ID `json:"creator_id"`
} }
// Behavior // Behavior

View File

@ -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
}

View File

@ -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
}

View File

@ -1,7 +0,0 @@
package domain
import (
"context"
"time"
)

View File

@ -1,48 +1,50 @@
package campaign package service
import ( import (
"context" "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" "time"
"your_project/domain"
"your_project/pkg/richerror"
"your_project/repository"
) )
type ID = unit64 // CreateCampaign handles creation of a new campaign.
type EntityCampaign = domain.Campaign func (s *CampaignService) CreateCampaign(ctx context.Context, req entity.Campaign) (types.ID, error) {
const op = "service.campaign.create_campaign"
if err := validateCreateCampaignRequest(req); err != nil {
type CampaignServiceImp interface { return 0, richerror.New(op).WithErr(err)
CreateCampaign(ctx context.Context, req CampaignRepository) (types.ID, error)
} }
campaign := entity.Campaign{
type CampaignService struct { Title: req.Title,
repo repository.CampaignRepository Description: req.Description,
GoalAmount: req.GoalAmount,
RaisedAmount: 0,
Status: req.Status,
DeadlineAt: req.DeadlineAt,
AdminID: req.AdminID,
CreatedAt: time.Now(),
} }
func NewCampaignService( id, err := s.repo.Create(ctx, campaign)
campaignRepo repository.CampaignRepository, if err != nil {
) *CampaignService { return 0, richerror.New(op).WithErr(err)
return &CampaignService{
repo: campaignRepo,
}
} }
return id, nil
}
func (s *CampaignService) CreateCampaign(ctx context.Context, req CreateCampaignRequest) (ID, error) { func validateCreateCampaignRequest(req entity.Campaign) error {
const Op = "service.campaign.create_campaign"
if req.Title == "" { if req.Title == "" {
return 0, richerror.New(Op).WithMessage("title is required") return errRequired("title")
} }
if req.GoalAmount <= 0 { 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 { if req.AdminID == 0 {
return 0, richerror.New(Op).WithMessage("admin_id is required") return errRequired("admin_id")
} }
validStatuses := map[string]bool{ validStatuses := map[string]bool{
@ -50,32 +52,20 @@ func (s *CampaignService) CreateCampaign(ctx context.Context, req CreateCampaign
"active": true, "active": true,
"completed": true, "completed": true,
"cancelled": true, "cancelled": true,
"paused": true "paused": true,
} }
if !validStatuses[req.Status] { if !validStatuses[string(req.Status)] {
return 0, richerror.New(Op).WithMessage("invalid status provided") return errInvalid("invalid status provided")
} }
return nil
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(),
} }
createdCampaignID, err := s.repo.CreateAndSave(ctx, newCampaign) // --- Helpers ---
if err != nil { func errRequired(field string) error {
return 0, richerror.New(Op).WithErr(err) return fmt.Errorf("%s is required", field)
} }
return createdCampaignID, nil func errInvalid(msg string) error {
return fmt.Errorf(msg)
} }

View File

@ -0,0 +1,27 @@
package service
import (
"git.gocasts.ir/ebhomengo/niki/types"
"time"
)
type GetCampaignResponse struct {
ID types.ID `json:"user_id"`
}
type AddCampaignRequest struct {
ID uint64 `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
GoalAmount float64 `json:"goal_amount"`
DeadlineAt *time.Time `json:"deadline_at,omitempty"`
AdminID types.ID `json:"admin_id"`
}
type UpdateCampaignRequest struct {
Title *string `json:"title,omitempty"`
Description *string `json:"description,omitempty"`
GoalAmount *float64 `json:"goal_amount,omitempty"`
DeadlineAt *time.Time `json:"deadline_at,omitempty"`
Status *string `json:"status,omitempty"` // draft/active/completed/paused/cancelled
}

View File

@ -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,
}
}

View File

@ -1,4 +1,4 @@
package cart package entity
import ( import (
"git.gocasts.ir/ebhomengo/niki/types" "git.gocasts.ir/ebhomengo/niki/types"

View File

@ -4,8 +4,8 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"git.gocasts.ir/ebhomengo/niki/domain/shoppingbasket/entity"
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error" richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
"git.gocasts.ir/ebhomengo/niki/shoppingbasketapp/service/cart"
"git.gocasts.ir/ebhomengo/niki/types" "git.gocasts.ir/ebhomengo/niki/types"
"github.com/redis/go-redis/v9" "github.com/redis/go-redis/v9"
"strconv" "strconv"
@ -43,7 +43,7 @@ func (r Repo) itemKey(productID types.ID) string {
return fmt.Sprintf("item:%d", productID) return fmt.Sprintf("item:%d", productID)
} }
func (r Repo) AddItem(ctx context.Context, userID types.ID, item cart.Item) error { func (r Repo) AddItem(ctx context.Context, userID types.ID, item entity.Item) error {
const op = "shoppingbasketapp.repository.AddItem" const op = "shoppingbasketapp.repository.AddItem"
cartKey := r.cartKey(userID) cartKey := r.cartKey(userID)
@ -65,7 +65,7 @@ func (r Repo) AddItem(ctx context.Context, userID types.ID, item cart.Item) erro
} else { } else {
existsItem, _ := r.client.HGet(ctx, cartKey, itemKey).Result() existsItem, _ := r.client.HGet(ctx, cartKey, itemKey).Result()
if existsItem != "" { if existsItem != "" {
var i cart.Item var i entity.Item
if err := json.Unmarshal([]byte(existsItem), &i); err != nil { if err := json.Unmarshal([]byte(existsItem), &i); err != nil {
return richerror.New(op).WithKind(richerror.KindUnexpected).WithErr(err) return richerror.New(op).WithKind(richerror.KindUnexpected).WithErr(err)
} }
@ -91,31 +91,31 @@ func parsInt(s string) int64 {
return i return i
} }
func (r Repo) GetCart(ctx context.Context, userID types.ID) (cart.Cart, error) { func (r Repo) GetCart(ctx context.Context, userID types.ID) (entity.Cart, error) {
const op = "shoppingbasketapp.repository.GetCart" const op = "shoppingbasketapp.repository.GetCart"
cartKey := r.cartKey(userID) cartKey := r.cartKey(userID)
exists, err := r.client.Exists(ctx, cartKey).Result() exists, err := r.client.Exists(ctx, cartKey).Result()
if err != nil { if err != nil {
return cart.Cart{}, richerror.New(op).WithKind(richerror.KindUnexpected).WithErr(err) return entity.Cart{}, richerror.New(op).WithKind(richerror.KindUnexpected).WithErr(err)
} }
if exists == 0 { if exists == 0 {
return cart.Cart{}, richerror.New(op).WithKind(richerror.KindNotFound).WithMessage("not found shopping basket") return entity.Cart{}, richerror.New(op).WithKind(richerror.KindNotFound).WithMessage("not found shopping basket")
} }
allCart, err := r.client.HGetAll(ctx, cartKey).Result() allCart, err := r.client.HGetAll(ctx, cartKey).Result()
if err != nil { if err != nil {
return cart.Cart{}, richerror.New(op).WithKind(richerror.KindUnexpected).WithErr(err) return entity.Cart{}, richerror.New(op).WithKind(richerror.KindUnexpected).WithErr(err)
} }
c := cart.Cart{Items: []cart.Item{}} c := entity.Cart{Items: []entity.Item{}}
for field, value := range allCart { for field, value := range allCart {
if strings.HasPrefix(field, "item:") { if strings.HasPrefix(field, "item:") {
var i cart.Item var i entity.Item
if err := json.Unmarshal([]byte(value), &i); err != nil { if err := json.Unmarshal([]byte(value), &i); err != nil {
return cart.Cart{}, richerror.New(op).WithKind(richerror.KindUnexpected).WithErr(err) return entity.Cart{}, richerror.New(op).WithKind(richerror.KindUnexpected).WithErr(err)
} }
c.Items = append(c.Items, i) c.Items = append(c.Items, i)
@ -185,7 +185,7 @@ func (r Repo) UpdateQuantity(ctx context.Context, userID, productID types.ID, qu
return richerror.New(op).WithKind(richerror.KindUnexpected).WithErr(err) return richerror.New(op).WithKind(richerror.KindUnexpected).WithErr(err)
} }
var item cart.Item var item entity.Item
if err := json.Unmarshal([]byte(data), &item); err != nil { if err := json.Unmarshal([]byte(data), &item); err != nil {
return richerror.New(op).WithKind(richerror.KindUnexpected).WithErr(err) return richerror.New(op).WithKind(richerror.KindUnexpected).WithErr(err)
} }
@ -228,7 +228,7 @@ func (r Repo) updateTotalPrice(ctx context.Context, cartKey string) error {
for field, value := range allFields { for field, value := range allFields {
if strings.HasPrefix(field, "item:") { if strings.HasPrefix(field, "item:") {
var item cart.Item var item entity.Item
if err := json.Unmarshal([]byte(value), &item); err != nil { if err := json.Unmarshal([]byte(value), &item); err != nil {
return richerror.New(op).WithKind(richerror.KindUnexpected).WithErr(err) return richerror.New(op).WithKind(richerror.KindUnexpected).WithErr(err)

View File

@ -1,6 +1,9 @@
package cart package service
import "git.gocasts.ir/ebhomengo/niki/types" import (
"git.gocasts.ir/ebhomengo/niki/domain/shoppingbasket/entity"
"git.gocasts.ir/ebhomengo/niki/types"
)
type AddToCartRequest struct { type AddToCartRequest struct {
UserID types.ID `json:"user_id"` UserID types.ID `json:"user_id"`
@ -12,7 +15,7 @@ type AddToCartRequest struct {
type GetCartResponse struct { type GetCartResponse struct {
UserID types.ID `json:"user_id"` UserID types.ID `json:"user_id"`
Items []Item `json:"items"` Items []entity.Item `json:"items"`
TotalPrice types.Price `json:"total_price"` TotalPrice types.Price `json:"total_price"`
CreatedAt int64 `json:"created_at"` CreatedAt int64 `json:"created_at"`
ExpireAt int64 `json:"expire_at"` ExpireAt int64 `json:"expire_at"`

View File

@ -1,7 +1,8 @@
package cart package service
import ( import (
"context" "context"
"git.gocasts.ir/ebhomengo/niki/domain/shoppingbasket/entity"
"git.gocasts.ir/ebhomengo/niki/pkg/logger" "git.gocasts.ir/ebhomengo/niki/pkg/logger"
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error" richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
"git.gocasts.ir/ebhomengo/niki/types" "git.gocasts.ir/ebhomengo/niki/types"
@ -9,8 +10,8 @@ import (
) )
type Repository interface { type Repository interface {
AddItem(ctx context.Context, userID types.ID, item Item) error AddItem(ctx context.Context, userID types.ID, item entity.Item) error
GetCart(ctx context.Context, userID types.ID) (Cart, error) GetCart(ctx context.Context, userID types.ID) (entity.Cart, error)
DeleteItem(ctx context.Context, userID, productID types.ID) error DeleteItem(ctx context.Context, userID, productID types.ID) error
UpdateQuantity(ctx context.Context, userID, productID types.ID, quantity int) error UpdateQuantity(ctx context.Context, userID, productID types.ID, quantity int) error
DeleteCart(ctx context.Context, userID types.ID) error DeleteCart(ctx context.Context, userID types.ID) error
@ -33,7 +34,7 @@ func (s Service) AddToBasket(ctx context.Context, req AddToCartRequest) error {
return err return err
} }
return s.repo.AddItem(ctx, req.UserID, Item{ return s.repo.AddItem(ctx, req.UserID, entity.Item{
ProductID: req.ProductID, ProductID: req.ProductID,
Quantity: req.Quantity, Quantity: req.Quantity,
Price: req.Price, Price: req.Price,

View File

@ -1,4 +1,4 @@
package cart package service
import ( import (
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error" richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"

View File

@ -1,5 +0,0 @@
package donate_server
type Handler struct{}

View File

@ -1,7 +0,0 @@
package donate_server
import "github.com/labstack/echo/v4"
func (h Handler) RegisterRoutes(e *echo.Echo) {
}

View File

@ -1,16 +0,0 @@
package donate_server
import (
httpserver "git.gocasts.ir/ebhomengo/niki/delivery/http_server"
"github.com/labstack/echo/v4"
)
type Server struct {
Server httpserver.Server
Handler Handler
Router *echo.Echo
}
func (s Server) Start() {
s.Handler.RegisterRoutes(s.Router)
}

View File

@ -0,0 +1,43 @@
package http
import (
"git.gocasts.ir/ebhomengo/niki/domain/campaign/entity"
"git.gocasts.ir/ebhomengo/niki/domain/campaign/service"
httpmsg "git.gocasts.ir/ebhomengo/niki/pkg/http_msg"
"github.com/labstack/echo/v4"
"net/http"
"time"
)
type Handler struct {
svc service.CampaignService
}
func NewHandler(svc service.CampaignService) Handler {
return Handler{svc: svc}
}
func (h Handler) createCampaign(c echo.Context) error {
var req entity.Campaign
if err := c.Bind(&req); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{
"error": "invalid request body",
})
}
req.CreatedAt = time.Now()
req.RaisedAmount = 0
createdID, err := h.svc.CreateCampaign(c.Request().Context(), req)
if err != nil {
msg, code := httpmsg.Error(err)
c.Logger().Errorf("Service error creating campaign: %v (Code: %d)", err, code)
return c.JSON(code, msg)
}
return c.JSON(http.StatusCreated, map[string]interface{}{
"message": "campaign created successfully",
"id": createdID,
})
}

View File

@ -0,0 +1,12 @@
package http
import (
"github.com/labstack/echo/v4"
"net/http"
)
func (s Server) healthCheck(c echo.Context) error {
return c.JSON(http.StatusOK, echo.Map{
"message": "everything is good!",
})
}

View File

@ -0,0 +1,39 @@
package http
import (
"context"
"git.gocasts.ir/ebhomengo/niki/pkg/httpserver"
)
type Server struct {
handler Handler
HTTPServer *httpserver.Server
}
func NewServer(handler Handler, hS *httpserver.Server) Server {
return Server{handler: handler, HTTPServer: hS}
}
func (s Server) Serve() error {
s.registerRoutes()
if err := s.HTTPServer.Start(); err != nil {
return err
}
return nil
}
func (s Server) Stop(ctx context.Context) error {
return s.HTTPServer.Stop(ctx)
}
func (s Server) registerRoutes() {
router := s.HTTPServer.GetRouter()
router.GET("campaign/health-check", s.healthCheck)
r := router.Group("campaign")
r.POST("/", s.handler.createCampaign)
}

View File

@ -2,7 +2,7 @@ package mysql
import ( import (
"context" "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" richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
"git.gocasts.ir/ebhomengo/niki/repository/mysql" "git.gocasts.ir/ebhomengo/niki/repository/mysql"
"git.gocasts.ir/ebhomengo/niki/types" "git.gocasts.ir/ebhomengo/niki/types"
@ -16,11 +16,9 @@ func New(db *mysql.DB) *DB {
return &DB{conn: db} return &DB{conn: db}
} }
// Create adds a new participant to a campaign
func (d *DB) CreateCampaignParticipants(ctx context.Context, participant entity.CampaignParticipant) (types.ID, error) {
// CreateCampaign creates a new campaign const Op = "repository.mysql.campaign_participant.create"
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) tx, err := d.conn.Conn().BeginTx(ctx, nil)
if err != nil { if err != nil {
@ -28,54 +26,17 @@ func (d *DB) CreateAndSave(ctx context.Context, campaign entity.Campaign) (types
} }
defer tx.Rollback() 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) query := `INSERT INTO campaign_participants (id,campaign_id, user_id, amount , created_at)
VALUES (?, ?, ? , ? , NOW())` VALUES (?, ?, ? , ? , NOW())`
result, err := d.conn.ExecContext(ctx, query, result, err := tx.ExecContext(ctx, query,
participant.ID, participant.ID,
participant.CampaignID, participant.CampaignID,
participant.UserID, participant.UserID,
participant.Amount, participant.Amount,
participant.CreatedAt participant.CreatedAt,
) )
if err != nil { if err != nil {
return 0, richerror.New(Op).WithErr(err) return 0, richerror.New(Op).WithErr(err)
} }
@ -84,11 +45,10 @@ func (d *DB) CreateCampaignParticipants(ctx context.Context, participant entity.
if err != nil { if err != nil {
return 0, richerror.New(Op).WithErr(err) return 0, richerror.New(Op).WithErr(err)
} }
if err := tx.Commit(); err != nil {
return types.ID(id), nil return 0, richerror.New(Op).WithErr(err)
} }
return types.ID(id), nil
}

View File

@ -1,14 +1,13 @@
package service package entity
import "time"
type ID uint64 type ID uint64
type CampaignParticipant struct { type CampaignParticipant struct {
ID ID `json:"id"` ID ID `json:"id"`
CampaignID ID `json:"campaign_id"` CampaignID ID `json:"campaign_id"`
UserID ID `json:"user_id"` UserID ID `json:"user_id"`
Amount float64 `json:"amount"` Amount float64 `json:"amount"`
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
} }

View File

@ -1 +1,26 @@
package service package service
import (
"git.gocasts.ir/ebhomengo/niki/types"
"time"
)
type GetCampaignResponse struct {
ID types.ID `json:"user_id"`
}
type AddCampaignRequest struct {
Title string `json:"title"`
Description string `json:"description"`
GoalAmount float64 `json:"goal_amount"`
DeadlineAt *time.Time `json:"deadline_at,omitempty"`
AdminID types.ID `json:"admin_id"`
}
type UpdateCampaignRequest struct {
Title *string `json:"title,omitempty"`
Description *string `json:"description,omitempty"`
GoalAmount *float64 `json:"goal_amount,omitempty"`
DeadlineAt *time.Time `json:"deadline_at,omitempty"`
Status *string `json:"status,omitempty"` // draft/active/completed/paused/cancelled
}

View File

@ -1,38 +1 @@
package service package service
import (
"context"
"errors"
"time"
"git.gocasts.ir/ebhomengo/niki/campaign/entity"
"git.gocasts.ir/ebhomengo/niki/repository"
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
"git.gocasts.ir/ebhomengo/niki/types"
)
type CampaignService struct {
repo repository.repo
}
// type CampaignServiceInterface interface {
// CreateCampaign(ctx context.Context, req CampaignRepository) (types.ID, error)
// }
func NewCampaignService(
repo repository.CampaignRepository,
participantRepo repository.CampaignParticipantRepository,
) *CampaignService {
return &CampaignService{
repo: repo,
participantRepo: participantRepo,
}
}

View File

@ -3,10 +3,10 @@ package purchaseapp
import ( import (
"context" "context"
"fmt" "fmt"
purchaseMysql "git.gocasts.ir/ebhomengo/niki/domain/purchase/repository/mysql" purchaseMysql "git.gocasts.ir/ebhomengo/niki/domain/order/repository/mysql"
purchaseService "git.gocasts.ir/ebhomengo/niki/domain/order/service"
purchaseHTTP "git.gocasts.ir/ebhomengo/niki/purchaseapp/delivery/http" purchaseHTTP "git.gocasts.ir/ebhomengo/niki/purchaseapp/delivery/http"
purchaseHandler "git.gocasts.ir/ebhomengo/niki/purchaseapp/delivery/http/order" purchaseHandler "git.gocasts.ir/ebhomengo/niki/purchaseapp/delivery/http/order"
purchaseService "git.gocasts.ir/ebhomengo/niki/purchaseapp/service/order"
"git.gocasts.ir/ebhomengo/niki/repository/mysql" "git.gocasts.ir/ebhomengo/niki/repository/mysql"
) )

View File

@ -4,11 +4,12 @@ import (
"context" "context"
"fmt" "fmt"
"git.gocasts.ir/ebhomengo/niki/adapter/redis" "git.gocasts.ir/ebhomengo/niki/adapter/redis"
"git.gocasts.ir/ebhomengo/niki/domain/shoppingbasket/repository"
"git.gocasts.ir/ebhomengo/niki/domain/shoppingbasket/service"
"git.gocasts.ir/ebhomengo/niki/pkg/httpserver" "git.gocasts.ir/ebhomengo/niki/pkg/httpserver"
"git.gocasts.ir/ebhomengo/niki/pkg/logger" "git.gocasts.ir/ebhomengo/niki/pkg/logger"
"git.gocasts.ir/ebhomengo/niki/shoppingbasketapp/delivery/http" "git.gocasts.ir/ebhomengo/niki/shoppingbasketapp/delivery/http"
"git.gocasts.ir/ebhomengo/niki/shoppingbasketapp/repository" "git.gocasts.ir/ebhomengo/niki/shoppingbasketapp/delivery/http/cart"
"git.gocasts.ir/ebhomengo/niki/shoppingbasketapp/service/cart"
"os" "os"
"os/signal" "os/signal"
"sync" "sync"
@ -17,8 +18,8 @@ import (
type Application struct { type Application struct {
Repo repository.Repo Repo repository.Repo
Service cart.Service Service service.Service
Handler http.Handler Handler cart.Handler
Server http.Server Server http.Server
Config Config Config Config
} }
@ -28,10 +29,10 @@ func Setup(ctx context.Context, cfg Config) (Application, error) {
adapter := redis.New(cfg.Redis) adapter := redis.New(cfg.Redis)
repo := repository.New(adapter.Client(), cfg.Repo) repo := repository.New(adapter.Client(), cfg.Repo)
validator := cart.NewValidate() validator := service.NewValidate()
svc := cart.New(validator, repo) svc := service.New(validator, repo)
handler := http.NewHandler(svc) handler := cart.NewHandler(svc)
httpServer, err := httpserver.New(cfg.HTTPServer) httpServer, err := httpserver.New(cfg.HTTPServer)
if err != nil { if err != nil {

View File

@ -2,9 +2,9 @@ package shoppingbasketapp
import ( import (
"git.gocasts.ir/ebhomengo/niki/adapter/redis" "git.gocasts.ir/ebhomengo/niki/adapter/redis"
"git.gocasts.ir/ebhomengo/niki/domain/shoppingbasket/repository"
"git.gocasts.ir/ebhomengo/niki/pkg/httpserver" "git.gocasts.ir/ebhomengo/niki/pkg/httpserver"
logger "git.gocasts.ir/ebhomengo/niki/pkg/logger" logger "git.gocasts.ir/ebhomengo/niki/pkg/logger"
"git.gocasts.ir/ebhomengo/niki/shoppingbasketapp/repository"
) )
type Config struct { type Config struct {

View File

@ -1,9 +1,9 @@
package http package cart
import ( import (
"git.gocasts.ir/ebhomengo/niki/domain/shoppingbasket/service"
"git.gocasts.ir/ebhomengo/niki/pkg/claim" "git.gocasts.ir/ebhomengo/niki/pkg/claim"
httpmsg "git.gocasts.ir/ebhomengo/niki/pkg/http_msg" httpmsg "git.gocasts.ir/ebhomengo/niki/pkg/http_msg"
"git.gocasts.ir/ebhomengo/niki/shoppingbasketapp/service/cart"
"git.gocasts.ir/ebhomengo/niki/types" "git.gocasts.ir/ebhomengo/niki/types"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"net/http" "net/http"
@ -11,17 +11,17 @@ import (
) )
type Handler struct { type Handler struct {
svc cart.Service svc service.Service
} }
func NewHandler(svc cart.Service) Handler { func NewHandler(svc service.Service) Handler {
return Handler{svc: svc} return Handler{svc: svc}
} }
func (h Handler) addToBasket(c echo.Context) error { func (h Handler) AddToBasket(c echo.Context) error {
claims := claim.GetClaimsFromEchoContext(c) claims := claim.GetClaimsFromEchoContext(c)
var req cart.AddToCartRequest var req service.AddToCartRequest
if err := c.Bind(&req); err != nil { if err := c.Bind(&req); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{ return c.JSON(http.StatusBadRequest, map[string]string{
"error": "invalid request body", "error": "invalid request body",
@ -37,7 +37,7 @@ func (h Handler) addToBasket(c echo.Context) error {
return c.NoContent(http.StatusNoContent) return c.NoContent(http.StatusNoContent)
} }
func (h Handler) getCart(c echo.Context) error { func (h Handler) GetCart(c echo.Context) error {
claims := claim.GetClaimsFromEchoContext(c) claims := claim.GetClaimsFromEchoContext(c)
res, err := h.svc.GetCart(c.Request().Context(), types.ID(claims.UserID)) res, err := h.svc.GetCart(c.Request().Context(), types.ID(claims.UserID))
@ -49,7 +49,7 @@ func (h Handler) getCart(c echo.Context) error {
return c.JSON(http.StatusOK, res) return c.JSON(http.StatusOK, res)
} }
func (h Handler) removeCart(c echo.Context) error { func (h Handler) RemoveCart(c echo.Context) error {
claims := claim.GetClaimsFromEchoContext(c) claims := claim.GetClaimsFromEchoContext(c)
if err := h.svc.ClearCart(c.Request().Context(), types.ID(claims.UserID)); err != nil { if err := h.svc.ClearCart(c.Request().Context(), types.ID(claims.UserID)); err != nil {
@ -60,7 +60,7 @@ func (h Handler) removeCart(c echo.Context) error {
return c.NoContent(http.StatusNoContent) return c.NoContent(http.StatusNoContent)
} }
func (h Handler) removeItem(c echo.Context) error { func (h Handler) RemoveItem(c echo.Context) error {
claims := claim.GetClaimsFromEchoContext(c) claims := claim.GetClaimsFromEchoContext(c)
p := c.Param("productID") p := c.Param("productID")
@ -71,7 +71,7 @@ func (h Handler) removeItem(c echo.Context) error {
}) })
} }
var req cart.RemoveFromCartRequest var req service.RemoveFromCartRequest
req.UserID = types.ID(claims.UserID) req.UserID = types.ID(claims.UserID)
req.ProductID = types.ID(pID) req.ProductID = types.ID(pID)
@ -84,7 +84,7 @@ func (h Handler) removeItem(c echo.Context) error {
return c.NoContent(http.StatusNoContent) return c.NoContent(http.StatusNoContent)
} }
func (h Handler) updateQuantity(c echo.Context) error { func (h Handler) UpdateQuantity(c echo.Context) error {
claims := claim.GetClaimsFromEchoContext(c) claims := claim.GetClaimsFromEchoContext(c)
p := c.Param("productID") p := c.Param("productID")
@ -103,7 +103,7 @@ func (h Handler) updateQuantity(c echo.Context) error {
}) })
} }
var req cart.UpdateQuantityRequest var req service.UpdateQuantityRequest
req.UserID = types.ID(claims.UserID) req.UserID = types.ID(claims.UserID)
req.ProductID = types.ID(pID) req.ProductID = types.ID(pID)
req.Quantity = q req.Quantity = q

View File

@ -3,14 +3,15 @@ package http
import ( import (
"context" "context"
"git.gocasts.ir/ebhomengo/niki/pkg/httpserver" "git.gocasts.ir/ebhomengo/niki/pkg/httpserver"
"git.gocasts.ir/ebhomengo/niki/shoppingbasketapp/delivery/http/cart"
) )
type Server struct { type Server struct {
handler Handler handler cart.Handler
HTTPServer *httpserver.Server HTTPServer *httpserver.Server
} }
func NewServer(handler Handler, hS *httpserver.Server) Server { func NewServer(handler cart.Handler, hS *httpserver.Server) Server {
return Server{handler: handler, HTTPServer: hS} return Server{handler: handler, HTTPServer: hS}
} }
@ -34,10 +35,10 @@ func (s Server) registerRoutes() {
r := router.Group("shoppingbasket/cart") // Authentication is required r := router.Group("shoppingbasket/cart") // Authentication is required
r.GET("/", s.handler.getCart) r.GET("/", s.handler.GetCart)
r.DELETE("/", s.handler.removeCart) r.DELETE("/", s.handler.RemoveCart)
r.POST("/items", s.handler.addToBasket) r.POST("/items", s.handler.AddToBasket)
r.DELETE("/items/:productID", s.handler.removeItem) r.DELETE("/items/:productID", s.handler.RemoveItem)
r.PUT("/items/:productID/:quantity", s.handler.updateQuantity) r.PUT("/items/:productID/:quantity", s.handler.UpdateQuantity)
} }