init project

This commit is contained in:
aditya.siregar
2024-05-28 14:14:55 +07:00
commit 67f1dbc850
141 changed files with 16879 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
package auth
+47
View File
@@ -0,0 +1,47 @@
package auth
import (
"context"
"errors"
"fmt"
"go.uber.org/zap"
"gorm.io/gorm"
"furtuna-be/internal/common/logger"
"furtuna-be/internal/entity"
)
type AuthRepository struct {
db *gorm.DB
}
func NewAuthRepository(db *gorm.DB) *AuthRepository {
return &AuthRepository{
db: db,
}
}
func (r *AuthRepository) CheckExistsUserAccount(ctx context.Context, email string) (*entity.UserDB, error) {
var user entity.UserDB
err := r.db.
Table("users").
Select("users.*, user_roles.role_id, user_roles.partner_id, roles.role_name, partners.name as partner_name").
Where("users.email = ?", email).
Joins("left join user_roles on users.id = user_roles.user_id").
Joins("left join roles on user_roles.role_id = roles.role_id").
Joins("left join partners on user_roles.partner_id = partners.id").
First(&user).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("user with email %s does not exist", email) // or use a custom error type
}
logger.ContextLogger(ctx).Error(fmt.Sprintf("Failed to get user with email: %s", email), zap.Error(err))
return nil, err
}
return &user, nil
}
+91
View File
@@ -0,0 +1,91 @@
package branches
import (
"context"
"furtuna-be/internal/common/logger"
"furtuna-be/internal/entity"
"go.uber.org/zap"
"gorm.io/gorm"
)
type BranchRepository struct {
db *gorm.DB
}
func NewBranchRepository(db *gorm.DB) *BranchRepository {
return &BranchRepository{
db: db,
}
}
func (b *BranchRepository) CreateBranch(ctx context.Context, branch *entity.BranchDB) (*entity.BranchDB, error) {
err := b.db.Create(branch).Error
if err != nil {
logger.ContextLogger(ctx).Error("error when create branch", zap.Error(err))
return nil, err
}
return branch, nil
}
func (b *BranchRepository) UpdateBranch(ctx context.Context, branch *entity.BranchDB) (*entity.BranchDB, error) {
if err := b.db.Save(branch).Error; err != nil {
logger.ContextLogger(ctx).Error("error when update branch", zap.Error(err))
return nil, err
}
return branch, nil
}
func (b *BranchRepository) GetBranchByID(ctx context.Context, id int64) (*entity.BranchDB, error) {
branch := new(entity.BranchDB)
if err := b.db.First(branch, id).Error; err != nil {
logger.ContextLogger(ctx).Error("error when get by id branch", zap.Error(err))
return nil, err
}
return branch, nil
}
func (b *BranchRepository) GetAllBranches(ctx context.Context, req entity.BranchSearch) (entity.BranchList, int, error) {
var branches []*entity.BranchDB
var total int64
query := b.db
query = query.Where("deleted_at is null")
if req.Search != "" {
query = query.Where("name ILIKE ?", "%"+req.Search+"%")
}
if req.Name != "" {
query = query.Where("name ILIKE ?", "%"+req.Name+"%")
}
if req.Limit > 0 {
query = query.Limit(req.Limit)
}
if req.Offset > 0 {
query = query.Offset(req.Offset)
}
if err := query.Find(&branches).Error; err != nil {
logger.ContextLogger(ctx).Error("error when get all branches", zap.Error(err))
return nil, 0, err
}
if err := b.db.Model(&entity.BranchDB{}).Where(query).Count(&total).Error; err != nil {
logger.ContextLogger(ctx).Error("error when count branches", zap.Error(err))
return nil, 0, err
}
return branches, int(total), nil
}
func (b *BranchRepository) DeleteBranch(ctx context.Context, id int64) error {
branch := new(entity.BranchDB)
branch.ID = id
if err := b.db.Delete(branch).Error; err != nil {
return err
}
return nil
}
+4
View File
@@ -0,0 +1,4 @@
//go:generate mockery --name Crypto --filename crypto.go --output ./mock --with-expecter
package crypto
+90
View File
@@ -0,0 +1,90 @@
package crypto
import (
"fmt"
"strconv"
"time"
"github.com/golang-jwt/jwt"
"golang.org/x/crypto/bcrypt"
"furtuna-be/internal/common/errors"
"furtuna-be/internal/entity"
)
func NewCrypto(config CryptoConfig) *CryptoImpl {
return &CryptoImpl{
Config: config,
}
}
type CryptoConfig interface {
AccessTokenSecret() string
AccessTokenExpiresDate() time.Time
}
type CryptoImpl struct {
Config CryptoConfig
}
func (c *CryptoImpl) CompareHashAndPassword(hash string, password string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}
func (c *CryptoImpl) ValidateWT(tokenString string) (*jwt.Token, error) {
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
return []byte(c.Config.AccessTokenSecret()), nil
})
return token, err
}
func (c *CryptoImpl) GenerateJWT(user *entity.User) (string, error) {
branchID := int64(0)
if user.PartnerID != nil {
branchID = *user.PartnerID
}
claims := &entity.JWTAuthClaims{
StandardClaims: jwt.StandardClaims{
Subject: strconv.FormatInt(user.ID, 10),
ExpiresAt: c.Config.AccessTokenExpiresDate().Unix(),
IssuedAt: time.Now().Unix(),
NotBefore: time.Now().Unix(),
},
UserID: user.ID,
Name: user.Name,
Email: user.Email,
Role: int(user.RoleID),
BranchID: branchID,
}
token, err := jwt.
NewWithClaims(jwt.SigningMethodHS256, claims).
SignedString([]byte(c.Config.AccessTokenSecret()))
if err != nil {
return "", err
}
return token, nil
}
func (c *CryptoImpl) ParseAndValidateJWT(tokenString string) (*entity.JWTAuthClaims, error) {
token, err := jwt.ParseWithClaims(tokenString, &entity.JWTAuthClaims{}, func(token *jwt.Token) (interface{}, error) {
return []byte(c.Config.AccessTokenSecret()), nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*entity.JWTAuthClaims); ok && token.Valid {
return claims, nil
} else {
return nil, errors.ErrorUnauthorized
}
}
+86
View File
@@ -0,0 +1,86 @@
package event
import (
"context"
"go.uber.org/zap"
"gorm.io/gorm"
"furtuna-be/internal/common/logger"
"furtuna-be/internal/entity"
)
type EventRepoImpl struct {
DB *gorm.DB
}
func NewEventRepo(db *gorm.DB) *EventRepoImpl {
return &EventRepoImpl{DB: db}
}
func (e *EventRepoImpl) CreateEvent(ctx context.Context, event *entity.EventDB) (*entity.EventDB, error) {
err := e.DB.Create(event).Error
if err != nil {
logger.ContextLogger(ctx).Error("error when create event", zap.Error(err))
return nil, err
}
return event, nil
}
func (e *EventRepoImpl) UpdateEvent(ctx context.Context, event *entity.EventDB) (*entity.EventDB, error) {
if err := e.DB.Save(event).Error; err != nil {
logger.ContextLogger(ctx).Error("error when update event", zap.Error(err))
return nil, err
}
return event, nil
}
func (e *EventRepoImpl) GetEventByID(ctx context.Context, id int64) (*entity.EventDB, error) {
event := new(entity.EventDB)
if err := e.DB.First(event, id).Error; err != nil {
logger.ContextLogger(ctx).Error("error when get event by id", zap.Error(err))
return nil, err
}
return event, nil
}
func (e *EventRepoImpl) GetAllEvents(ctx context.Context, nameFilter string, limit, offset int) (entity.EventList, int, error) {
var events []*entity.EventDB
var total int64
query := e.DB
query = query.Where("deleted_at is null")
if nameFilter != "" {
query = query.Where("name LIKE ?", "%"+nameFilter+"%")
}
if limit > 0 {
query = query.Limit(limit)
}
if offset > 0 {
query = query.Offset(offset)
}
if err := query.Find(&events).Error; err != nil {
logger.ContextLogger(ctx).Error("error when get all events", zap.Error(err))
return nil, 0, err
}
if err := e.DB.Model(&entity.EventDB{}).Where(query).Count(&total).Error; err != nil {
logger.ContextLogger(ctx).Error("error when count event", zap.Error(err))
return nil, 0, err
}
return events, int(total), nil
}
func (e *EventRepoImpl) DeleteEvent(ctx context.Context, id int64) error {
event := new(entity.EventDB)
event.ID = id
if err := e.DB.Delete(event).Error; err != nil {
logger.ContextLogger(ctx).Error("error when get all events", zap.Error(err))
return err
}
return nil
}
+303
View File
@@ -0,0 +1,303 @@
package orders
import (
"context"
"fmt"
"furtuna-be/internal/common/logger"
"furtuna-be/internal/entity"
"go.uber.org/zap"
"gorm.io/gorm"
)
type OrderRepository struct {
db *gorm.DB
}
func NewOrderRepository(db *gorm.DB) *OrderRepository {
return &OrderRepository{
db: db,
}
}
func (o *OrderRepository) CreateOrder(ctx context.Context, order *entity.OrderDB) (*entity.OrderDB, error) {
tx := o.db.Begin()
if err := tx.Select("branch_id", "status", "customer_name", "customer_phone", "pax", "amount", "created_by").Create(order).Error; err != nil {
tx.Rollback()
logError(ctx, "creating order", err)
return nil, err
}
for i, orditem := range order.OrderItem {
orderItem := orditem.ToOrderItemDB()
orderItem.OrderID = order.ID
err := tx.Select("order_id", "item_id", "item_type", "price", "qty", "created_by").Create(orderItem).Error
if err != nil {
logger.ContextLogger(ctx).Error("error when create order item", zap.Error(err))
return nil, err
}
order.OrderItem[i] = *orderItem.ToOrderItem()
}
//insert transaction
transaction := order.Transaction.ToTransactionDB()
transaction.OrderID = order.ID
if err := tx.Select("branch_id", "status", "amount", "order_id", "payment_method", "customer_name", "customer_phone", "created_by").Create(transaction).Error; err != nil {
tx.Rollback()
logError(ctx, "creating transaction", err)
return nil, err
}
if err := tx.Commit().Error; err != nil {
tx.Rollback()
logError(ctx, "committing transaction", err)
return nil, err
}
return order, nil
}
func (b *OrderRepository) UpdateOrder(ctx context.Context, order *entity.OrderDB) (*entity.OrderDB, error) {
if err := b.db.Select("status", "updated_at", "updated_by").Save(order).Error; err != nil {
logError(ctx, "update order", err)
return nil, err
}
return order, nil
}
func (b *OrderRepository) GetAllOrders(ctx context.Context, req entity.OrderSearch) (entity.OrderList, int, error) {
var orders []*entity.OrderDB
var total int64
query := b.db.Table("orders").
Select("orders.id, orders.branch_id, b.name as branch_name, orders.status, orders.amount, orders.created_at, orders.updated_at, oi.order_item_id, oi.order_id, oi.item_id, oi.item_type, COALESCE(p.name, s.name, '') as item_name, oi.price, oi.qty, oi.created_at, oi.updated_at, COALESCE(t.payment_method, ''), COALESCE(orders.customer_name, ''), COALESCE(orders.customer_phone, ''), COALESCE(orders.pax, 0)").
Joins("LEFT JOIN order_items oi ON orders.id = oi.order_id").
Joins("LEFT JOIN transactions t ON orders.id = t.order_id").
Joins("LEFT JOIN products p ON oi.item_id = p.id AND oi.item_type ='PRODUCT' ").
Joins("LEFT JOIN studios s ON oi.item_id = s.id AND oi.item_type ='STUDIO' ").
Joins("LEFT JOIN branches b ON orders.branch_id = b.id")
if req.Search != "" {
query = query.Where("b.name ILIKE ? or orders.status ILIKE ? or oi.item_type ILIKE ? or p.name ILIKE ? or orders.customer_name ILIKE ? ", "%"+req.Search+"%", "%"+req.Search+"%", "%"+req.Search+"%", "%"+req.Search+"%", "%"+req.Search+"%")
}
if req.Status != "" {
query = query.Where("orders.status = ?", req.Status)
}
if req.BranchID > 0 {
query = query.Where("orders.branch_id = ?", req.BranchID)
}
if req.StatusActive.IsActive() {
query = query.Joins("INNER JOIN (SELECT o.id, oi.qty, o.created_at FROM orders o INNER JOIN order_items oi ON o.id = oi.order_id AND oi.item_type = 'STUDIO' where o.status != 'CANCEL' and CURRENT_TIMESTAMP > o.created_at AND CURRENT_TIMESTAMP < (o.created_at + (oi.qty || ' hours')::interval)) order_active on order_active.id=orders.id")
}
if req.Limit > 0 {
query = query.Limit(req.Limit)
}
if req.Offset > 0 {
query = query.Offset(req.Offset)
}
query.Order("orders.created_at DESC")
rows, err := query.Rows()
if err != nil {
return nil, 0, err
}
defer rows.Close()
ordersMap := make(map[int64]*entity.OrderDB) // Map to store orders by ID
for rows.Next() {
var ordr entity.OrderDB
var oi entity.OrderItem
err := rows.Scan(&ordr.ID, &ordr.BranchID, &ordr.BranchName, &ordr.Status, &ordr.Amount, &ordr.CreatedAt, &ordr.UpdatedAt,
&oi.OrderItemID, &oi.OrderID, &oi.ItemID, &oi.ItemType, &oi.ItemName, &oi.Price, &oi.Qty, &oi.CreatedAt, &oi.UpdatedAt,
&ordr.Transaction.PaymentMethod, &ordr.CustomerName, &ordr.CustomerPhone, &ordr.Pax)
if err != nil {
logger.ContextLogger(ctx).Error("error scanning rows", zap.Error(err))
return nil, 0, err
}
if order, ok := ordersMap[ordr.ID]; ok {
// Order already exists in map, append OrderItem to existing order
order.OrderItem = append(order.OrderItem, oi)
} else {
// Order doesn't exist in map, create a new OrderDB
newOrder := ordr
newOrder.OrderItem = []entity.OrderItem{oi}
ordersMap[ordr.ID] = &newOrder
orders = append(orders, &ordr)
}
}
// assign value order item
for _, v := range orders {
v.OrderItem = ordersMap[v.ID].OrderItem
}
//reset limit for count total data
query = query.Offset(-1).Limit(-1)
if err := query.Count(&total).Error; err != nil {
logger.ContextLogger(ctx).Error("error when count orders", zap.Error(err))
return nil, 0, err
}
return orders, int(total), nil
}
func (b *OrderRepository) GetOrderByID(ctx context.Context, id int64) (*entity.OrderDB, error) {
var orders *entity.OrderDB
query := b.db.Table("orders").
Select("orders.id, orders.branch_id, b.name as branch_name, orders.status, orders.amount, orders.created_at, orders.updated_at, oi.order_item_id, oi.order_id, oi.item_id, oi.item_type, COALESCE(p.name, s.name, '') as item_name, oi.price, oi.qty, oi.created_at, oi.updated_at, t.payment_method, orders.customer_name, orders.customer_phone, orders.pax").
Joins("LEFT JOIN order_items oi ON orders.id = oi.order_id").
Joins("LEFT JOIN transactions t ON orders.id = t.order_id").
Joins("LEFT JOIN products p ON oi.item_id = p.id AND oi.item_type ='PRODUCT' ").
Joins("LEFT JOIN studios s ON oi.item_id = s.id AND oi.item_type ='STUDIO' ").
Joins("LEFT JOIN branches b ON orders.branch_id = b.id").
Where("orders.id = ?", id)
rows, err := query.Rows()
if err != nil {
return nil, err
}
defer rows.Close()
var ordr entity.OrderDB // Map to store orders by ID
for rows.Next() {
var oi entity.OrderItem
err := rows.Scan(&ordr.ID, &ordr.BranchID, &ordr.BranchName, &ordr.Status, &ordr.Amount, &ordr.CreatedAt, &ordr.UpdatedAt,
&oi.OrderItemID, &oi.OrderID, &oi.ItemID, &oi.ItemType, &oi.ItemName, &oi.Price, &oi.Qty, &oi.CreatedAt, &oi.UpdatedAt,
&ordr.Transaction.PaymentMethod, &ordr.CustomerName, &ordr.CustomerPhone, &ordr.Pax)
if err != nil {
logger.ContextLogger(ctx).Error("error scanning rows", zap.Error(err))
return nil, err
}
ordr.OrderItem = append(ordr.OrderItem, oi)
}
orders = &ordr
if orders == nil {
return nil, fmt.Errorf("order not found")
}
return orders, nil
}
func (b *OrderRepository) GetTotalRevenue(ctx context.Context, req entity.OrderTotalRevenueSearch) (float64, int64, error) {
var (
totalmonthlyRevenue float64
totalmonthlyTrans int64
)
query := b.db.Table("orders").
Select("COALESCE(sum(amount),0) as total_amount, COALESCE(count(id),0) as total_transaction").
Where("status in ('NEW','PAID') ")
if req.BranchID > 0 {
query = query.Where("branch_id = ?", req.BranchID)
}
if req.Month > 0 {
query = query.Where("EXTRACT(MONTH FROM created_at) = ? ", req.Month)
}
if req.Year > 0 {
query = query.Where("EXTRACT(YEAR FROM created_at) = ? ", req.Year)
}
if req.DateStart != nil {
query = query.Where("created_at >= ? ", req.DateStart)
}
if req.DateEnd != nil {
query = query.Where("created_at <= ? ", req.DateEnd)
}
rows, err := query.Rows()
if err != nil {
return totalmonthlyRevenue, totalmonthlyTrans, err
}
defer rows.Close()
for rows.Next() {
err := rows.Scan(&totalmonthlyRevenue, &totalmonthlyTrans)
if err != nil {
logger.ContextLogger(ctx).Error("error scanning rows", zap.Error(err))
return totalmonthlyRevenue, totalmonthlyTrans, err
}
}
return totalmonthlyRevenue, totalmonthlyTrans, nil
}
func (b *OrderRepository) GetYearlyRevenue(ctx context.Context, year int) (entity.OrderYearlyRevenueList, error) {
var result entity.OrderYearlyRevenueList
err := b.db.Raw(` SELECT
oi.item_type,
EXTRACT(MONTH FROM o.created_at) AS month_number,
SUM(oi.price ) AS total_amount
FROM
orders o
JOIN
order_items oi ON o.id = oi.order_id
WHERE
EXTRACT(YEAR FROM o.created_at) = ?
AND o.status IN ('NEW', 'PAID')
GROUP BY
EXTRACT(MONTH FROM o.created_at),
oi.item_type
ORDER BY
month_number,
oi.item_type`, year).Scan(&result).Error
return result, err
}
func (b *OrderRepository) GetBranchRevenue(ctx context.Context, req entity.OrderBranchRevenueSearch) (entity.OrderBranchRevenueList, error) {
var result entity.OrderBranchRevenueList
query := b.db.Table("orders o").
Joins("JOIN branches ON branches.id = o.branch_id").
Select("o.branch_id, branches.name, branches.location, SUM(o.amount) as total_amount, COUNT(o.id) as total_trans").
Where("o.status IN ('NEW', 'PAID')").
Group("o.branch_id, branches.name, branches.location").
Order("total_amount DESC, total_trans DESC")
if req.DateStart != nil {
query = query.Where("o.created_at >= ? ", req.DateStart)
}
if req.DateEnd != nil {
query = query.Where("o.created_at <= ? ", req.DateEnd)
}
if err := query.Find(&result).Error; err != nil {
logger.ContextLogger(ctx).Error("error when GetBranchRevenue", zap.Error(err))
return nil, err
}
return result, nil
}
func logError(ctx context.Context, s string, err error) {
panic("unimplemented")
}
+67
View File
@@ -0,0 +1,67 @@
package oss
import (
"bytes"
"context"
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
)
type OSSConfig interface {
GetAccessKeyID() string
GetAccessKeySecret() string
GetEndpoint() string
GetBucketName() string
GetHostURL() string
}
const _awsRegion = "us-east-1"
const _s3ACL = "public-read"
type OssRepositoryImpl struct {
s3 *s3.S3
cfg OSSConfig
}
func NewOssRepositoryImpl(ossCfg OSSConfig) *OssRepositoryImpl {
sess, err := session.NewSession(&aws.Config{
S3ForcePathStyle: aws.Bool(true),
Endpoint: aws.String(ossCfg.GetEndpoint()),
Region: aws.String(_awsRegion),
Credentials: credentials.NewStaticCredentials(ossCfg.GetAccessKeyID(), ossCfg.GetAccessKeySecret(), ""),
})
if err != nil {
fmt.Println("Failed to create AWS session:", err)
return nil
}
return &OssRepositoryImpl{
s3: s3.New(sess),
cfg: ossCfg,
}
}
func (r *OssRepositoryImpl) UploadFile(ctx context.Context, fileName string, fileContent []byte) (fileUrl string, err error) {
reader := bytes.NewReader(fileContent)
_, err = r.s3.PutObject(&s3.PutObjectInput{
Bucket: aws.String(r.cfg.GetBucketName()),
Key: aws.String(fileName),
Body: reader,
ACL: aws.String(_s3ACL),
})
return r.GetPublicURL(fileName), err
}
func (r *OssRepositoryImpl) GetPublicURL(fileName string) string {
if fileName == "" {
return ""
}
return fmt.Sprintf("%s/%s%s", r.cfg.GetHostURL(), r.cfg.GetBucketName(), fileName)
}
+91
View File
@@ -0,0 +1,91 @@
package partners
import (
"context"
"furtuna-be/internal/common/logger"
"furtuna-be/internal/entity"
"go.uber.org/zap"
"gorm.io/gorm"
)
type PartnerRepository struct {
db *gorm.DB
}
func NewPartnerRepository(db *gorm.DB) *PartnerRepository {
return &PartnerRepository{
db: db,
}
}
func (b *PartnerRepository) Create(ctx context.Context, Partner *entity.PartnerDB) (*entity.PartnerDB, error) {
err := b.db.Create(Partner).Error
if err != nil {
logger.ContextLogger(ctx).Error("error when create Partner", zap.Error(err))
return nil, err
}
return Partner, nil
}
func (b *PartnerRepository) Update(ctx context.Context, Partner *entity.PartnerDB) (*entity.PartnerDB, error) {
if err := b.db.Save(Partner).Error; err != nil {
logger.ContextLogger(ctx).Error("error when update Partner", zap.Error(err))
return nil, err
}
return Partner, nil
}
func (b *PartnerRepository) GetByID(ctx context.Context, id int64) (*entity.PartnerDB, error) {
Partner := new(entity.PartnerDB)
if err := b.db.First(Partner, id).Error; err != nil {
logger.ContextLogger(ctx).Error("error when get by id Partner", zap.Error(err))
return nil, err
}
return Partner, nil
}
func (b *PartnerRepository) GetAll(ctx context.Context, req entity.PartnerSearch) (entity.PartnerList, int, error) {
var Partneres []*entity.PartnerDB
var total int64
query := b.db
query = query.Where("deleted_at is null")
if req.Search != "" {
query = query.Where("name ILIKE ?", "%"+req.Search+"%")
}
if req.Name != "" {
query = query.Where("name ILIKE ?", "%"+req.Name+"%")
}
if req.Limit > 0 {
query = query.Limit(req.Limit)
}
if req.Offset > 0 {
query = query.Offset(req.Offset)
}
if err := query.Find(&Partneres).Error; err != nil {
logger.ContextLogger(ctx).Error("error when get all Partneres", zap.Error(err))
return nil, 0, err
}
if err := b.db.Model(&entity.PartnerDB{}).Where(query).Count(&total).Error; err != nil {
logger.ContextLogger(ctx).Error("error when count Partneres", zap.Error(err))
return nil, 0, err
}
return Partneres, int(total), nil
}
func (b *PartnerRepository) Delete(ctx context.Context, id int64) error {
Partner := new(entity.PartnerDB)
Partner.ID = id
if err := b.db.Delete(Partner).Error; err != nil {
return err
}
return nil
}
+107
View File
@@ -0,0 +1,107 @@
package products
import (
"context"
"furtuna-be/internal/common/logger"
"furtuna-be/internal/entity"
"go.uber.org/zap"
"gorm.io/gorm"
)
type ProductRepository struct {
db *gorm.DB
}
func NewProductRepository(db *gorm.DB) *ProductRepository {
return &ProductRepository{
db: db,
}
}
func (b *ProductRepository) CreateProduct(ctx context.Context, product *entity.ProductDB) (*entity.ProductDB, error) {
err := b.db.Create(product).Error
if err != nil {
logger.ContextLogger(ctx).Error("error when create product", zap.Error(err))
return nil, err
}
return product, nil
}
func (b *ProductRepository) UpdateProduct(ctx context.Context, product *entity.ProductDB) (*entity.ProductDB, error) {
if err := b.db.Save(product).Error; err != nil {
logger.ContextLogger(ctx).Error("error when update product", zap.Error(err))
return nil, err
}
return product, nil
}
func (b *ProductRepository) GetProductByID(ctx context.Context, id int64) (*entity.ProductDB, error) {
product := new(entity.ProductDB)
if err := b.db.First(product, id).Error; err != nil {
logger.ContextLogger(ctx).Error("error when get by id product", zap.Error(err))
return nil, err
}
return product, nil
}
func (b *ProductRepository) GetAllProducts(ctx context.Context, req entity.ProductSearch) (entity.ProductList, int, error) {
var products []*entity.ProductDB
var total int64
query := b.db
query = query.Where("deleted_at is null")
if req.Search != "" {
query = query.Where("name ILIKE ?", "%"+req.Search+"%")
}
if req.Name != "" {
query = query.Where("name ILIKE ?", "%"+req.Name+"%")
}
if req.Type != "" {
query = query.Where("type = ? ", req.Type)
}
if req.BranchID > 0 {
query = query.Where("branch_id = ? ", req.BranchID)
}
if req.Available != "" {
if req.Available.IsAvailable() {
query = query.Where("stock_qty > 0 ")
} else if req.Available.IsUnavailable() {
query = query.Where("stock_qty < 1 ")
}
}
if req.Limit > 0 {
query = query.Limit(req.Limit)
}
if req.Offset > 0 {
query = query.Offset(req.Offset)
}
if err := query.Find(&products).Error; err != nil {
logger.ContextLogger(ctx).Error("error when get all products", zap.Error(err))
return nil, 0, err
}
if err := b.db.Model(&entity.ProductDB{}).Where(query).Count(&total).Error; err != nil {
logger.ContextLogger(ctx).Error("error when count products", zap.Error(err))
return nil, 0, err
}
return products, int(total), nil
}
func (b *ProductRepository) DeleteProduct(ctx context.Context, id int64) error {
product := new(entity.ProductDB)
product.ID = id
if err := b.db.Delete(product).Error; err != nil {
return err
}
return nil
}
+122
View File
@@ -0,0 +1,122 @@
package repository
import (
"context"
"furtuna-be/internal/repository/branches"
"furtuna-be/internal/repository/orders"
"furtuna-be/internal/repository/oss"
"furtuna-be/internal/repository/partners"
"furtuna-be/internal/repository/products"
"furtuna-be/internal/repository/studios"
"furtuna-be/internal/repository/users"
"github.com/golang-jwt/jwt"
"gorm.io/gorm"
"furtuna-be/config"
"furtuna-be/internal/entity"
"furtuna-be/internal/repository/auth"
"furtuna-be/internal/repository/crypto"
event "furtuna-be/internal/repository/events"
)
type RepoManagerImpl struct {
Crypto Crypto
Auth Auth
Event Event
User User
Branch Branch
Studio Studio
Product Product
Order Order
OSS OSSRepository
Partner PartnerRepository
}
func NewRepoManagerImpl(db *gorm.DB, cfg *config.Config) *RepoManagerImpl {
return &RepoManagerImpl{
Crypto: crypto.NewCrypto(cfg.Auth()),
Auth: auth.NewAuthRepository(db),
Event: event.NewEventRepo(db),
User: users.NewUserRepository(db),
Branch: branches.NewBranchRepository(db),
Studio: studios.NewStudioRepository(db),
Product: products.NewProductRepository(db),
Order: orders.NewOrderRepository(db),
OSS: oss.NewOssRepositoryImpl(cfg.OSSConfig),
Partner: partners.NewPartnerRepository(db),
}
}
type Auth interface {
CheckExistsUserAccount(ctx context.Context, email string) (*entity.UserDB, error)
}
type Event interface {
CreateEvent(ctx context.Context, event *entity.EventDB) (*entity.EventDB, error)
UpdateEvent(ctx context.Context, event *entity.EventDB) (*entity.EventDB, error)
GetEventByID(ctx context.Context, id int64) (*entity.EventDB, error)
GetAllEvents(ctx context.Context, nameFilter string, limit, offset int) (entity.EventList, int, error)
DeleteEvent(ctx context.Context, id int64) error
}
type Crypto interface {
CompareHashAndPassword(hash string, password string) bool
ValidateWT(tokenString string) (*jwt.Token, error)
GenerateJWT(user *entity.User) (string, error)
ParseAndValidateJWT(token string) (*entity.JWTAuthClaims, error)
}
type User interface {
Create(ctx context.Context, user *entity.UserDB) (*entity.UserDB, error)
GetAllUsers(ctx context.Context, req entity.UserSearch) (entity.UserList, int, error)
GetUserByID(ctx context.Context, id int64) (*entity.UserDB, error)
GetUserByEmail(ctx context.Context, email string) (*entity.UserDB, error)
UpdateUser(ctx context.Context, user *entity.UserDB) (*entity.UserDB, error)
}
type Branch interface {
CreateBranch(ctx context.Context, branch *entity.BranchDB) (*entity.BranchDB, error)
UpdateBranch(ctx context.Context, branch *entity.BranchDB) (*entity.BranchDB, error)
GetBranchByID(ctx context.Context, id int64) (*entity.BranchDB, error)
GetAllBranches(ctx context.Context, req entity.BranchSearch) (entity.BranchList, int, error)
DeleteBranch(ctx context.Context, id int64) error
}
type Studio interface {
CreateStudio(ctx context.Context, studio *entity.StudioDB) (*entity.StudioDB, error)
UpdateStudio(ctx context.Context, studio *entity.StudioDB) (*entity.StudioDB, error)
GetStudioByID(ctx context.Context, id int64) (*entity.StudioDB, error)
SearchStudios(ctx context.Context, req entity.StudioSearch) (entity.StudioList, int, error)
}
type Product interface {
CreateProduct(ctx context.Context, product *entity.ProductDB) (*entity.ProductDB, error)
UpdateProduct(ctx context.Context, product *entity.ProductDB) (*entity.ProductDB, error)
GetProductByID(ctx context.Context, id int64) (*entity.ProductDB, error)
GetAllProducts(ctx context.Context, req entity.ProductSearch) (entity.ProductList, int, error)
DeleteProduct(ctx context.Context, id int64) error
}
type Order interface {
CreateOrder(ctx context.Context, order *entity.OrderDB) (*entity.OrderDB, error)
UpdateOrder(ctx context.Context, order *entity.OrderDB) (*entity.OrderDB, error)
GetOrderByID(ctx context.Context, id int64) (*entity.OrderDB, error)
GetAllOrders(ctx context.Context, req entity.OrderSearch) (entity.OrderList, int, error)
GetTotalRevenue(ctx context.Context, req entity.OrderTotalRevenueSearch) (float64, int64, error)
GetYearlyRevenue(ctx context.Context, year int) (entity.OrderYearlyRevenueList, error)
GetBranchRevenue(ctx context.Context, req entity.OrderBranchRevenueSearch) (entity.OrderBranchRevenueList, error)
}
type OSSRepository interface {
UploadFile(ctx context.Context, fileName string, fileContent []byte) (fileUrl string, err error)
GetPublicURL(fileName string) string
}
type PartnerRepository interface {
Create(ctx context.Context, branch *entity.PartnerDB) (*entity.PartnerDB, error)
Update(ctx context.Context, branch *entity.PartnerDB) (*entity.PartnerDB, error)
GetByID(ctx context.Context, id int64) (*entity.PartnerDB, error)
GetAll(ctx context.Context, req entity.PartnerSearch) (entity.PartnerList, int, error)
Delete(ctx context.Context, id int64) error
}
+89
View File
@@ -0,0 +1,89 @@
package studios
import (
"context"
"furtuna-be/internal/common/logger"
"furtuna-be/internal/entity"
"go.uber.org/zap"
"gorm.io/gorm"
)
type StudioRepository struct {
db *gorm.DB
}
func NewStudioRepository(db *gorm.DB) *StudioRepository {
return &StudioRepository{
db: db,
}
}
func (s *StudioRepository) CreateStudio(ctx context.Context, studio *entity.StudioDB) (*entity.StudioDB, error) {
err := s.db.Omit("ID").Create(studio).Error
if err != nil {
logger.ContextLogger(ctx).Error("error when creating studio", zap.Error(err))
return nil, err
}
return studio, nil
}
func (s *StudioRepository) UpdateStudio(ctx context.Context, studio *entity.StudioDB) (*entity.StudioDB, error) {
if err := s.db.Save(studio).Error; err != nil {
logger.ContextLogger(ctx).Error("error when updating studio", zap.Error(err))
return nil, err
}
return studio, nil
}
func (s *StudioRepository) GetStudioByID(ctx context.Context, id int64) (*entity.StudioDB, error) {
studio := new(entity.StudioDB)
if err := s.db.First(studio, id).Error; err != nil {
logger.ContextLogger(ctx).Error("error when getting studio by ID", zap.Error(err))
return nil, err
}
return studio, nil
}
func (s *StudioRepository) SearchStudios(ctx context.Context, req entity.StudioSearch) (entity.StudioList, int, error) {
var studios []*entity.StudioDB
var total int64
query := s.db
if req.Id > 0 {
query = query.Where("id = ?", req.Id)
}
if req.Name != "" {
query = query.Where("name ILIKE ?", "%"+req.Name+"%")
}
if req.Status != "" {
query = query.Where("status = ?", req.Status)
}
if req.BranchId > 0 {
query = query.Where("branch_id = ?", req.BranchId)
}
if req.Limit > 0 {
query = query.Limit(req.Limit)
}
if req.Offset > 0 {
query = query.Offset(req.Offset)
}
if err := query.Find(&studios).Error; err != nil {
logger.ContextLogger(ctx).Error("error when getting all studios", zap.Error(err))
return nil, 0, err
}
if err := s.db.Model(&entity.StudioDB{}).Where(query).Count(&total).Error; err != nil {
logger.ContextLogger(ctx).Error("error when counting studios", zap.Error(err))
return nil, 0, err
}
return studios, int(total), nil
}
+166
View File
@@ -0,0 +1,166 @@
package users
import (
"context"
"furtuna-be/internal/common/logger"
"furtuna-be/internal/entity"
"go.uber.org/zap"
"gorm.io/gorm"
)
type UserRepository struct {
db *gorm.DB
}
func NewUserRepository(db *gorm.DB) *UserRepository {
return &UserRepository{
db: db,
}
}
func (r *UserRepository) Create(ctx context.Context, user *entity.UserDB) (*entity.UserDB, error) {
tx := r.db.Begin()
user.ID = 0
if err := tx.Select("name", "email", "password", "status", "created_by", "nik", "user_type", "phone_number").Create(user).Error; err != nil {
tx.Rollback()
logError(ctx, "creating user", err)
return nil, err
}
if err := tx.First(user, user.ID).Error; err != nil {
tx.Rollback()
logError(ctx, "retrieving user", err)
return nil, err
}
userRole := user.ToUserRoleDB()
if err := tx.Create(userRole).Error; err != nil {
tx.Rollback()
logError(ctx, "creating user role", err)
return nil, err
}
if err := tx.Commit().Error; err != nil {
tx.Rollback()
logError(ctx, "committing transaction", err)
return nil, err
}
return user, nil
}
func (b *UserRepository) GetAllUsers(ctx context.Context, req entity.UserSearch) (entity.UserList, int, error) {
var users []*entity.UserDB
var total int64
query := b.db.Table("users").
Select("users.id, users.email, users.name, users.status, users.created_at, users.updated_at, ur.role_id, r.role_name, ur.partner_id, b.name as partner_name").
Joins("LEFT JOIN user_roles ur ON users.id = ur.user_id").
Joins("LEFT JOIN roles r ON ur.role_id = r.role_id").
Joins("LEFT JOIN partners b ON ur.partner_id = b.id").
Where("users.deleted_at is null")
if req.Search != "" {
query = query.Where("users.name ILIKE ? or users.email ILIKE ? or r.role_name ILIKE ? or b.name ILIKE ? ", "%"+req.Search+"%", "%"+req.Search+"%", "%"+req.Search+"%", "%"+req.Search+"%")
}
if req.Name != "" {
query = query.Where("users.name ILIKE ?", "%"+req.Name+"%")
}
if req.RoleID > 0 {
query = query.Where("ur.role_id = ? ", req.RoleID)
}
if req.PartnerID > 0 {
query = query.Where("ur.partner_id = ? ", req.PartnerID)
}
// Get the total count without applying the limit and offset.
if err := query.Model(&entity.UserDB{}).Count(&total).Error; err != nil {
logger.ContextLogger(ctx).Error("error when count users", zap.Error(err))
return nil, 0, err
}
// Apply pagination.
query = query.Offset(req.Offset).Limit(req.Limit)
if err := query.Scan(&users).Error; err != nil {
logger.ContextLogger(ctx).Error("error when get all users", zap.Error(err))
return nil, 0, err
}
return users, int(total), nil
}
func (b *UserRepository) GetUserByID(ctx context.Context, id int64) (*entity.UserDB, error) {
var user *entity.UserDB
query := b.db.Table("users").
Select("users.id, users.email, users.name, users.status, users.created_at, users.updated_at, ur.role_id, r.role_name, ur.partner_id, b.name as partner_name").
Joins("LEFT JOIN user_roles ur ON users.id = ur.user_id").
Joins("LEFT JOIN roles r ON ur.role_id = r.role_id").
Joins("LEFT JOIN partners b ON ur.partner_id = b.id").
Where("users.id = ?", id)
if err := query.Scan(&user).Error; err != nil {
logger.ContextLogger(ctx).Error("error when get user", zap.Error(err))
return nil, err
}
return user, nil
}
func (b *UserRepository) GetUserByEmail(ctx context.Context, email string) (*entity.UserDB, error) {
var user *entity.UserDB
query := b.db.Table("users").
Select("users.id, users.email, users.name, users.status, users.created_at, users.updated_at, ur.role_id, r.role_name, ur.partner_id, b.name as partner_name").
Joins("LEFT JOIN user_roles ur ON users.id = ur.user_id").
Joins("LEFT JOIN roles r ON ur.role_id = r.role_id").
Joins("LEFT JOIN partners b ON ur.partner_id = b.id").
Where("users.email = ?", email)
if err := query.Scan(&user).Error; err != nil {
logger.ContextLogger(ctx).Error("error when get user", zap.Error(err))
return nil, err
}
return user, nil
}
func (r *UserRepository) UpdateUser(ctx context.Context, user *entity.UserDB) (*entity.UserDB, error) {
tx := r.db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
if err := tx.Select("name", "email", "password", "status", "deleted_at", "updated_by").Save(user).Error; err != nil {
tx.Rollback()
logError(ctx, "update user", err)
return nil, err
}
userRole := user.ToUserRoleDB()
if err := tx.Model(userRole).Where("user_id = ?", user.ID).Updates(userRole).Error; err != nil {
tx.Rollback()
logError(ctx, "update user role", err)
return nil, err
}
if err := tx.Commit().Error; err != nil {
tx.Rollback()
logError(ctx, "committing transaction", err)
return nil, err
}
return user, nil
}
func logError(ctx context.Context, action string, err error) {
logger.ContextLogger(ctx).Error("error when "+action, zap.Error(err))
}