From f4bd43a60f1ceb0d486b68bc9b4e516eac973aa5 Mon Sep 17 00:00:00 2001 From: Sahar Mokarrami Date: Mon, 13 Apr 2026 10:32:58 +0330 Subject: [PATCH] feat(order): add order domain structure --- cmd/purchaseapp/main.go | 2 +- domain/order/entity/order.go | 49 +++++++++++++++ domain/order/entity/order_item.go | 16 +++++ domain/order/entity/shipping.go | 10 ++++ .../2026010411120_create_orders_table.sql | 8 +-- ...0260104_11121_create_order_items_table.sql | 0 .../20261104_11122_create_shippings_table.sql | 16 +++++ domain/order/repository/mysql/db.go | 11 ++++ .../order}/repository/mysql/order.go | 51 +++++++++++----- .../order/service/order.go | 19 +++--- domain/order/service/shipping.go | 16 +++++ purchaseapp/app.go | 2 +- purchaseapp/delivery/http/order/handler.go | 8 +-- .../{service => delivery/http}/order/param.go | 4 +- purchaseapp/delivery/http/order/route.go | 1 - purchaseapp/delivery/http/server.go | 10 ++-- purchaseapp/entity/order.go | 59 ------------------- purchaseapp/repository/mysql/db.go | 1 - purchaseapp/service/order/validator.go | 1 - 19 files changed, 182 insertions(+), 102 deletions(-) create mode 100644 domain/order/entity/order.go create mode 100644 domain/order/entity/order_item.go create mode 100644 domain/order/entity/shipping.go rename {purchaseapp => domain/order}/repository/migrations/2026010411120_create_orders_table.sql (84%) rename {purchaseapp => domain/order}/repository/migrations/20260104_11121_create_order_items_table.sql (100%) create mode 100644 domain/order/repository/migrations/20261104_11122_create_shippings_table.sql create mode 100644 domain/order/repository/mysql/db.go rename {purchaseapp => domain/order}/repository/mysql/order.go (62%) rename purchaseapp/service/order/service.go => domain/order/service/order.go (52%) create mode 100644 domain/order/service/shipping.go rename purchaseapp/{service => delivery/http}/order/param.go (82%) delete mode 100644 purchaseapp/entity/order.go delete mode 100644 purchaseapp/repository/mysql/db.go delete mode 100644 purchaseapp/service/order/validator.go diff --git a/cmd/purchaseapp/main.go b/cmd/purchaseapp/main.go index fdd06b0b..ad0283df 100644 --- a/cmd/purchaseapp/main.go +++ b/cmd/purchaseapp/main.go @@ -3,8 +3,8 @@ package main import ( "flag" "fmt" + purchaseMysql "git.gocasts.ir/ebhomengo/niki/domain/purchase/repository/mysql" "git.gocasts.ir/ebhomengo/niki/purchaseapp/delivery/http" - purchaseMysql "git.gocasts.ir/ebhomengo/niki/purchaseapp/repository/mysql" "git.gocasts.ir/ebhomengo/niki/purchaseapp/service/order" "git.gocasts.ir/ebhomengo/niki/repository/migrator" "git.gocasts.ir/ebhomengo/niki/repository/mysql" diff --git a/domain/order/entity/order.go b/domain/order/entity/order.go new file mode 100644 index 00000000..89f5499e --- /dev/null +++ b/domain/order/entity/order.go @@ -0,0 +1,49 @@ +package entity + +import ( + "git.gocasts.ir/ebhomengo/niki/types" + "time" +) + +type Order struct { + ID types.ID + UserID types.ID + TotalAmount types.Price + TotalDiscount types.Price + ShippingID types.ID + PaymentMethod PaymentMethod + ProcessStatus ProcessStatus + PaymentStatus PaymentStatus + AddressID types.ID + CreatedAt time.Time + UpdatedAt time.Time +} + +type PaymentMethod string + +const ( + Online PaymentMethod = "online" + Wallet = "wallet" + Cart = "cart" +) + +type ProcessStatus string + +const ( + WaitingToPay ProcessStatus = "waiting-to-pay" + Processing = "processing" + Accepted = "accepted" + Preparing = "preparing" + Prepared = "prepared" + GivenToPost = "given-to-post" + Delivered = "delivered" + Cancelled = "cancelled" + SystemCancellation = "system-cancellation" +) + +type PaymentStatus string + +const ( + Paid PaymentStatus = "paid" + UnPaid = "unpaid" +) diff --git a/domain/order/entity/order_item.go b/domain/order/entity/order_item.go new file mode 100644 index 00000000..0a9db823 --- /dev/null +++ b/domain/order/entity/order_item.go @@ -0,0 +1,16 @@ +package entity + +import ( + "git.gocasts.ir/ebhomengo/niki/types" + "time" +) + +type OrderItem struct { + ID types.ID + ProductID types.ID + Price types.Price + Quantity types.Count + PriceWithDiscount types.Price + OrderID types.ID + CreatedAt time.Time +} diff --git a/domain/order/entity/shipping.go b/domain/order/entity/shipping.go new file mode 100644 index 00000000..53d518ca --- /dev/null +++ b/domain/order/entity/shipping.go @@ -0,0 +1,10 @@ +package entity + +import "git.gocasts.ir/ebhomengo/niki/types" + +type Shipping struct { + ID types.ID + Name string + Price types.Price + IsActive bool +} diff --git a/purchaseapp/repository/migrations/2026010411120_create_orders_table.sql b/domain/order/repository/migrations/2026010411120_create_orders_table.sql similarity index 84% rename from purchaseapp/repository/migrations/2026010411120_create_orders_table.sql rename to domain/order/repository/migrations/2026010411120_create_orders_table.sql index 2fe04c92..55ff9490 100644 --- a/purchaseapp/repository/migrations/2026010411120_create_orders_table.sql +++ b/domain/order/repository/migrations/2026010411120_create_orders_table.sql @@ -3,11 +3,11 @@ -- https://www.grouparoo.com/blog/varchar-191#why-varchar-and-not-text CREATE TABLE `orders` ( `id` INT PRIMARY KEY AUTO_INCREMENT, - `user_id` INT NOT NULL, - `address` TEXT, + `user_id` INT, + `address_id` INT, `shipping_id` INT NOT NULL, `payment_method` ENUM('online', 'wallet', 'cart') DEFAULT 'online', - `payment_status` ENUM('unpaid', 'paid', 'cancelled') DEFAULT 'unpaid', + `payment_status` ENUM('unpaid', 'paid') DEFAULT 'unpaid', `process_status` ENUM('waiting-to-pay', 'processing', 'accepted', 'preparing', 'prepared', 'given-to-post', 'delivered', 'cancelled') DEFAULT 'waiting-to-pay', `total_amount` INT NOT NULL, `total_discount` INT NULL, @@ -15,7 +15,7 @@ CREATE TABLE `orders` ( `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -- FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) --- FOREIGN KEY (`shipping_id`) REFERENCES `shippings`(`id`) + FOREIGN KEY (`shipping_id`) REFERENCES `shippings`(`id`) ); diff --git a/purchaseapp/repository/migrations/20260104_11121_create_order_items_table.sql b/domain/order/repository/migrations/20260104_11121_create_order_items_table.sql similarity index 100% rename from purchaseapp/repository/migrations/20260104_11121_create_order_items_table.sql rename to domain/order/repository/migrations/20260104_11121_create_order_items_table.sql diff --git a/domain/order/repository/migrations/20261104_11122_create_shippings_table.sql b/domain/order/repository/migrations/20261104_11122_create_shippings_table.sql new file mode 100644 index 00000000..4a1375c0 --- /dev/null +++ b/domain/order/repository/migrations/20261104_11122_create_shippings_table.sql @@ -0,0 +1,16 @@ +-- +migrate Up +-- please read this article to understand why we use VARCHAR(191) +-- https://www.grouparoo.com/blog/varchar-191#why-varchar-and-not-text +CREATE TABLE `orders` ( + `id` INT PRIMARY KEY AUTO_INCREMENT, + `name` VARCHAR (191), + `price` INT NOT NULL , + `is_active` INT NOT NULL DEFAULT 1, + + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP + +); + +-- +migrate Down +DROP TABLE `orders`; diff --git a/domain/order/repository/mysql/db.go b/domain/order/repository/mysql/db.go new file mode 100644 index 00000000..0082acb0 --- /dev/null +++ b/domain/order/repository/mysql/db.go @@ -0,0 +1,11 @@ +package mysql + +import "git.gocasts.ir/ebhomengo/niki/repository/mysql" + +type DB struct { + conn *mysql.DB +} + +func New(db *mysql.DB) *DB { + return &DB{conn: db} +} diff --git a/purchaseapp/repository/mysql/order.go b/domain/order/repository/mysql/order.go similarity index 62% rename from purchaseapp/repository/mysql/order.go rename to domain/order/repository/mysql/order.go index 279aa351..748b6be0 100644 --- a/purchaseapp/repository/mysql/order.go +++ b/domain/order/repository/mysql/order.go @@ -1,23 +1,14 @@ package mysql import ( + entity "git.gocasts.ir/ebhomengo/niki/domain/order/entity" richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error" - "git.gocasts.ir/ebhomengo/niki/purchaseapp/entity" - "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} -} - func (d *DB) CreateOrder(order entity.Order, orderItems []entity.OrderItem) (types.ID, error) { - const Op = "repository.mysql.order.createorder" + const Op = "domain.repository.mysql.order.create-order" tx, err := d.conn.Conn().Begin() if err != nil { @@ -26,10 +17,10 @@ func (d *DB) CreateOrder(order entity.Order, orderItems []entity.OrderItem) (typ defer tx.Rollback() - query := "insert into orders(user_id, address, shipping_id," + + query := "insert into orders(user_id, address_id, shipping_id," + " payment_method, payment_status, process_status," + " total_amount, total_discount) values (?, ?, ?, ?, ?, ?, ?, ?);" - res, oErr := tx.Exec(query, order.UserID, order.Address, order.ShippingID, + res, oErr := tx.Exec(query, order.UserID, order.AddressID, order.ShippingID, order.PaymentMethod, order.PaymentStatus, order.ProcessStatus, order.TotalAmount, order.TotalDiscount) @@ -57,7 +48,7 @@ func (d *DB) CreateOrder(order entity.Order, orderItems []entity.OrderItem) (typ } func (d *DB) UpdateOrderProcessStatus(orderID types.ID, status string) (bool, error) { - const Op = "repository.mysql.order.update-order-process-status" + const Op = "domain.repository.mysql.order.update-order-process-status" _, err := d.conn.Conn().Exec("update orders set process_status=? where id=?;", status, orderID) if err != nil { @@ -67,3 +58,35 @@ func (d *DB) UpdateOrderProcessStatus(orderID types.ID, status string) (bool, er return true, nil } + +func (d *DB) GetShipping() ([]entity.Shipping, error) { + const Op = "domain.repository.mysql.order.get-shipping" + rows, err := d.conn.Conn().Query("select * from shippings where is_active=1") + + if err != nil { + return []entity.Shipping{}, richerror.New(Op).WithErr(err) + } + + defer rows.Close() + + var shippings []entity.Shipping + + for rows.Next() { + var s entity.Shipping + + err := rows.Scan( + &s.ID, + &s.Name, + &s.Price, + &s.IsActive, + ) + if err != nil { + return nil, richerror.New(Op).WithErr(err) + } + + shippings = append(shippings, s) + } + + return shippings, nil + +} diff --git a/purchaseapp/service/order/service.go b/domain/order/service/order.go similarity index 52% rename from purchaseapp/service/order/service.go rename to domain/order/service/order.go index 21690140..40255d49 100644 --- a/purchaseapp/service/order/service.go +++ b/domain/order/service/order.go @@ -1,9 +1,9 @@ -package order +package service import ( + entity "git.gocasts.ir/ebhomengo/niki/domain/order/entity" richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error" - "git.gocasts.ir/ebhomengo/niki/purchaseapp/entity" - "git.gocasts.ir/ebhomengo/niki/types" + types "git.gocasts.ir/ebhomengo/niki/types" ) type Service struct { @@ -13,26 +13,27 @@ type Service struct { type Repo interface { CreateOrder(order entity.Order, orderItems []entity.OrderItem) (types.ID, error) UpdateOrderProcessStatus(orderID types.ID, status string) (bool, error) + GetShipping() ([]entity.Shipping, error) } func New(orderRepo Repo) Service { return Service{repo: orderRepo} } -func (s Service) CreateOrder(order entity.Order, orderItems []entity.OrderItem) (CreateOrderResponse, error) { - const Op = "purchaseapp.service.CreateOrder" +func (s *Service) CreateOrder(order entity.Order, orderItems []entity.OrderItem) (types.ID, error) { + const Op = "domain.order.service.order.CreateOrder" orderID, err := s.repo.CreateOrder(order, orderItems) if err != nil { - return CreateOrderResponse{}, richerror.New(Op).WithErr(err) + return 0, richerror.New(Op).WithErr(err) } - return CreateOrderResponse{OrderID: orderID}, nil + return orderID, nil } -func (s Service) UpdateOrderProcessStatus(orderID types.ID, status string) (bool, error) { +func (s *Service) UpdateOrderProcessStatus(orderID types.ID, status string) (bool, error) { - const Op = "purchaseapp.service.UpdateOrderProcessStatus" + const Op = "domain.order.service.order.UpdateOrderProcessStatus" _, err := s.repo.UpdateOrderProcessStatus(orderID, status) if err != nil { return false, richerror.New(Op).WithErr(err) diff --git a/domain/order/service/shipping.go b/domain/order/service/shipping.go new file mode 100644 index 00000000..3d1f6dae --- /dev/null +++ b/domain/order/service/shipping.go @@ -0,0 +1,16 @@ +package service + +import ( + "git.gocasts.ir/ebhomengo/niki/domain/order/entity" + richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error" +) + +func (s *Service) GetShipping() ([]entity.Shipping, error) { + const Op = "domain.order.service.shipping.get-shipping" + shippings, err := s.repo.GetShipping() + if err != nil { + return []entity.Shipping{}, richerror.New(Op) + } + + return shippings, nil +} diff --git a/purchaseapp/app.go b/purchaseapp/app.go index 7f62b636..a846535b 100644 --- a/purchaseapp/app.go +++ b/purchaseapp/app.go @@ -3,9 +3,9 @@ package purchaseapp import ( "context" "fmt" + purchaseMysql "git.gocasts.ir/ebhomengo/niki/domain/purchase/repository/mysql" purchaseHTTP "git.gocasts.ir/ebhomengo/niki/purchaseapp/delivery/http" purchaseHandler "git.gocasts.ir/ebhomengo/niki/purchaseapp/delivery/http/order" - purchaseMysql "git.gocasts.ir/ebhomengo/niki/purchaseapp/repository/mysql" purchaseService "git.gocasts.ir/ebhomengo/niki/purchaseapp/service/order" "git.gocasts.ir/ebhomengo/niki/repository/mysql" ) diff --git a/purchaseapp/delivery/http/order/handler.go b/purchaseapp/delivery/http/order/handler.go index 9ae96087..e9ba97f1 100644 --- a/purchaseapp/delivery/http/order/handler.go +++ b/purchaseapp/delivery/http/order/handler.go @@ -1,9 +1,9 @@ package order import ( + entity "git.gocasts.ir/ebhomengo/niki/domain/order/entity" + order "git.gocasts.ir/ebhomengo/niki/domain/order/service" richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error" - "git.gocasts.ir/ebhomengo/niki/purchaseapp/entity" - "git.gocasts.ir/ebhomengo/niki/purchaseapp/service/order" "github.com/labstack/echo/v4" "net/http" "time" @@ -18,7 +18,7 @@ func New(orderSvc order.Service) *Handler { } func (h *Handler) CreateOrderHandler(c echo.Context) error { - var req order.CreateOrderRequest + var req CreateOrderRequest if err := c.Bind(&req); err != nil { msg, code := getErrorDataFromRichError(err) return echo.NewHTTPError(code, msg) @@ -34,7 +34,7 @@ func (h *Handler) CreateOrderHandler(c echo.Context) error { PaymentMethod: req.PaymentMethod, ProcessStatus: entity.WaitingToPay, PaymentStatus: entity.UnPaid, - Address: req.Address, + AddressID: req.AddressID, CreatedAt: time.Now(), UpdatedAt: time.Now(), } diff --git a/purchaseapp/service/order/param.go b/purchaseapp/delivery/http/order/param.go similarity index 82% rename from purchaseapp/service/order/param.go rename to purchaseapp/delivery/http/order/param.go index d4a589f9..dac464ba 100644 --- a/purchaseapp/service/order/param.go +++ b/purchaseapp/delivery/http/order/param.go @@ -1,13 +1,13 @@ package order import ( - "git.gocasts.ir/ebhomengo/niki/purchaseapp/entity" + "git.gocasts.ir/ebhomengo/niki/domain/order/entity" "git.gocasts.ir/ebhomengo/niki/types" ) type CreateOrderRequest struct { UserID types.ID `json:"user_id"` - Address string `json:"address"` + AddressID types.ID `json:"address_id"` ShippingID types.ID `json:"shipping_id"` PaymentMethod entity.PaymentMethod `json:"payment_method"` TotalAmount types.Price `json:"total_amount"` diff --git a/purchaseapp/delivery/http/order/route.go b/purchaseapp/delivery/http/order/route.go index 3db0dd1f..62b20d3c 100644 --- a/purchaseapp/delivery/http/order/route.go +++ b/purchaseapp/delivery/http/order/route.go @@ -4,5 +4,4 @@ import "github.com/labstack/echo/v4" func (h Handler) SetRoutes(e *echo.Echo) { - e.POST("/order/create", h.CreateOrderHandler) } diff --git a/purchaseapp/delivery/http/server.go b/purchaseapp/delivery/http/server.go index 1da710cb..0852e026 100644 --- a/purchaseapp/delivery/http/server.go +++ b/purchaseapp/delivery/http/server.go @@ -1,19 +1,19 @@ package http import ( - "git.gocasts.ir/ebhomengo/niki/purchaseapp/delivery/http/order" - orderService "git.gocasts.ir/ebhomengo/niki/purchaseapp/service/order" + order "git.gocasts.ir/ebhomengo/niki/domain/order/service" + orderHandler "git.gocasts.ir/ebhomengo/niki/purchaseapp/delivery/http/order" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" ) type Server struct { - OrderHandler *order.Handler + OrderHandler *orderHandler.Handler } -func New(orderSvc orderService.Service) *Server { +func New(orderSvc order.Service) *Server { return &Server{ - OrderHandler: order.New(orderSvc), + OrderHandler: orderHandler.New(orderSvc), } } diff --git a/purchaseapp/entity/order.go b/purchaseapp/entity/order.go deleted file mode 100644 index 0e235f66..00000000 --- a/purchaseapp/entity/order.go +++ /dev/null @@ -1,59 +0,0 @@ -package entity - -import ( - "git.gocasts.ir/ebhomengo/niki/types" - "time" -) - -type Order struct { - ID types.ID - UserID types.ID - TotalAmount types.Price - TotalDiscount types.Price - ShippingID types.ID - PaymentMethod PaymentMethod - ProcessStatus ProcessStatus - PaymentStatus PaymentStatus - Address string - CreatedAt time.Time - UpdatedAt time.Time -} - -type OrderItem struct { - ID types.ID - ProductID types.ID - Price types.Price - Quantity types.Count - PriceWithDiscount types.Price - OrderID types.ID - CreatedAt time.Time -} - -type PaymentMethod string - -const ( - Online PaymentMethod = "online" - Wallet = "wallet" - Cart = "cart" -) - -type ProcessStatus string - -const ( - WaitingToPay ProcessStatus = "waiting-to-pay" - processing = "processing" - accepted = "accepted" - preparing = "preparing" - prepared = "prepared" - givenToPost = "given-to-post" - delivered = "delivered" - cancelled = "cancelled" -) - -type PaymentStatus string - -const ( - Paid PaymentStatus = "paid" - UnPaid = "unpaid" - Cancelled = "cancelled" -) diff --git a/purchaseapp/repository/mysql/db.go b/purchaseapp/repository/mysql/db.go deleted file mode 100644 index b0843023..00000000 --- a/purchaseapp/repository/mysql/db.go +++ /dev/null @@ -1 +0,0 @@ -package mysql diff --git a/purchaseapp/service/order/validator.go b/purchaseapp/service/order/validator.go deleted file mode 100644 index 175f0c10..00000000 --- a/purchaseapp/service/order/validator.go +++ /dev/null @@ -1 +0,0 @@ -package order