202 lines
5.0 KiB
Go
202 lines
5.0 KiB
Go
package db
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"management/internal/pkg/config"
|
|
|
|
"github.com/jackc/pgconn"
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
)
|
|
|
|
// ****************** conn ******************
|
|
|
|
func newDsn(conf config.DB) string {
|
|
return fmt.Sprintf("postgresql://%s:%s@%s:%d/%s?sslmode=disable",
|
|
conf.Username,
|
|
conf.Password,
|
|
conf.Host,
|
|
conf.Port,
|
|
conf.DBName)
|
|
}
|
|
|
|
// ****************** errors ******************
|
|
|
|
const (
|
|
foreignKeyViolation = "23503"
|
|
uniqueViolation = "23505"
|
|
)
|
|
|
|
// var ErrUniqueViolation = &pgconn.PgError{
|
|
// Code: UniqueViolation,
|
|
// }
|
|
|
|
func ErrorCode(err error) string {
|
|
var pgErr *pgconn.PgError
|
|
if errors.As(err, &pgErr) {
|
|
return pgErr.Code
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// ****************** store ******************
|
|
var Engine Store
|
|
|
|
// Store defines all functions to execute db queries and transactions
|
|
type Store interface {
|
|
Querier
|
|
ExecTx(context.Context, func(*Queries) error) error
|
|
|
|
Pool() *pgxpool.Pool
|
|
|
|
SysDepartmentRebuildPath(ctx context.Context) error
|
|
SysRoleRebuildPath(ctx context.Context) error
|
|
CategoryRebuildPath(ctx context.Context) error
|
|
|
|
ListCustomerCondition(ctx context.Context, arg *ListCustomerConditionParam) ([]*CustomerView, int64, error)
|
|
ListProjectCondition(ctx context.Context, arg *ListProjectConditionParam) ([]*ProjectView, int64, error)
|
|
ListBudgetCondition(ctx context.Context, arg *ListBudgetConditionParam) ([]*BudgetView, int64, error)
|
|
ListIncomeCondition(ctx context.Context, arg *ListIncomeConditionParam) ([]*IncomeView, int64, error)
|
|
ListExpenseCondition(ctx context.Context, arg *ListExpenseConditionParam) ([]*ExpenseView, int64, error)
|
|
}
|
|
|
|
// SQLStore provides all functions to execute SQL queries and transactions
|
|
type SQLStore struct {
|
|
connPool *pgxpool.Pool
|
|
*Queries
|
|
}
|
|
|
|
func NewIStore(ctx context.Context, conf config.DB) (Store, error) {
|
|
pool, err := pgxpool.New(ctx, newDsn(conf))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = pool.Ping(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &SQLStore{
|
|
connPool: pool,
|
|
Queries: New(pool),
|
|
}, nil
|
|
}
|
|
|
|
// NewStore creates a new store
|
|
func NewStore(ctx context.Context) error {
|
|
pool, err := pgxpool.New(ctx, newDsn(config.File.DB))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = pool.Ping(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
Engine = &SQLStore{
|
|
connPool: pool,
|
|
Queries: New(pool),
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (store *SQLStore) Pool() *pgxpool.Pool {
|
|
return store.connPool
|
|
}
|
|
|
|
func IsUniqueViolation(err error) bool {
|
|
return ErrorCode(err) == uniqueViolation
|
|
}
|
|
|
|
func IsForeignKeyViolation(err error) bool {
|
|
return ErrorCode(err) == foreignKeyViolation
|
|
}
|
|
|
|
func IsNoRows(err error) bool {
|
|
return errors.Is(err, pgx.ErrNoRows)
|
|
}
|
|
|
|
func (store *SQLStore) ExecTx(ctx context.Context, fn func(*Queries) error) error {
|
|
tx, err := store.connPool.Begin(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
q := New(tx)
|
|
err = fn(q)
|
|
if err != nil {
|
|
if rbErr := tx.Rollback(ctx); rbErr != nil {
|
|
return fmt.Errorf("tx err: %v, rb err: %v", err, rbErr)
|
|
}
|
|
return err
|
|
}
|
|
|
|
return tx.Commit(ctx)
|
|
}
|
|
|
|
func (store *SQLStore) SysDepartmentRebuildPath(ctx context.Context) error {
|
|
query := `
|
|
UPDATE sys_department AS tm
|
|
SET parent_path = (SELECT ',' || string_agg(cast(t.parent_id AS VARCHAR), ',') || ','
|
|
FROM (WITH RECURSIVE temp (id, parent_id) AS (SELECT id, tm.parent_id
|
|
FROM sys_department
|
|
WHERE id = tm.id
|
|
UNION ALL
|
|
SELECT sys_department.id, sys_department.parent_id
|
|
FROM sys_department,
|
|
temp
|
|
WHERE sys_department.id = temp.parent_id)
|
|
SELECT id, parent_id
|
|
FROM temp
|
|
ORDER BY id) AS t)
|
|
WHERE tm.status = 0 ;`
|
|
_, err := store.db.Exec(ctx, query)
|
|
return err
|
|
}
|
|
|
|
func (store *SQLStore) SysRoleRebuildPath(ctx context.Context) error {
|
|
query := `
|
|
UPDATE sys_role AS tm
|
|
SET parent_path = (SELECT ',' || string_agg(cast(t.parent_id AS VARCHAR), ',') || ','
|
|
FROM (WITH RECURSIVE temp (id, parent_id) AS (SELECT id, tm.parent_id
|
|
FROM sys_role
|
|
WHERE id = tm.id
|
|
UNION ALL
|
|
SELECT sys_role.id, sys_role.parent_id
|
|
FROM sys_role,
|
|
temp
|
|
WHERE sys_role.id = temp.parent_id)
|
|
SELECT id, parent_id
|
|
FROM temp
|
|
ORDER BY id) AS t)
|
|
WHERE tm.status = 0;`
|
|
_, err := store.db.Exec(ctx, query)
|
|
return err
|
|
}
|
|
|
|
func (store *SQLStore) CategoryRebuildPath(ctx context.Context) error {
|
|
query := `
|
|
UPDATE categories AS tm
|
|
SET parent_path = (SELECT ',' || string_agg(cast(t.parent_id AS VARCHAR), ',') || ','
|
|
FROM (WITH RECURSIVE temp (id, parent_id) AS (SELECT id, tm.parent_id
|
|
FROM categories
|
|
WHERE id = tm.id
|
|
UNION ALL
|
|
SELECT categories.id, categories.parent_id
|
|
FROM categories,
|
|
temp
|
|
WHERE categories.id = temp.parent_id)
|
|
SELECT id, parent_id
|
|
FROM temp
|
|
ORDER BY id) AS t)
|
|
WHERE tm.status = 0;`
|
|
_, err := store.db.Exec(ctx, query)
|
|
return err
|
|
}
|