forked from ebhomengo/niki
Merge branch 'develop' into mehdikeshavarz/driver(agent)/loginorregister
This commit is contained in:
commit
9ea2a5c493
|
|
@ -1,8 +1,62 @@
|
|||
package benefactorapp
|
||||
|
||||
import "net/http"
|
||||
import (
|
||||
"context"
|
||||
benefactorHttp "git.gocasts.ir/ebhomengo/niki/benefactorapp/delivery/http"
|
||||
repo "git.gocasts.ir/ebhomengo/niki/benefactorapp/repository/database"
|
||||
benefactor "git.gocasts.ir/ebhomengo/niki/benefactorapp/service"
|
||||
mySql "git.gocasts.ir/ebhomengo/niki/pkg/database/mysql"
|
||||
httpserver "git.gocasts.ir/ebhomengo/niki/pkg/httpserver"
|
||||
logger "git.gocasts.ir/ebhomengo/niki/pkg/logger"
|
||||
)
|
||||
|
||||
type Application struct {
|
||||
Config Config
|
||||
HTTPServer *http.Server
|
||||
Config Config
|
||||
HTTPServer benefactorHttp.Server
|
||||
BenefactorService benefactor.Service
|
||||
BenefactorHandler benefactorHttp.Handler
|
||||
BenefactorRepo benefactor.Repository
|
||||
DBConn mySql.DB
|
||||
}
|
||||
|
||||
func Setup(ctx context.Context, config Config, DB mySql.DB) *Application {
|
||||
log := logger.L()
|
||||
log.Info("logger starting ...")
|
||||
|
||||
db := mySql.New(config.MySQLDB)
|
||||
defer func() {
|
||||
if err := db.CloseStatements(); err != nil {
|
||||
log.Info("Error closing statements: %v\n", err)
|
||||
}
|
||||
}()
|
||||
log.Info("mysql connection starting ...")
|
||||
|
||||
// Initialize repositories
|
||||
benefactorRepo := repo.New(db)
|
||||
benefactorValidator := benefactor.NewValidator(benefactorRepo)
|
||||
|
||||
benefactorSvc := benefactor.NewService(benefactorRepo, benefactorValidator)
|
||||
benefactorHandler := benefactorHttp.NewHandler(benefactorSvc)
|
||||
|
||||
hServer, hErr := httpserver.New(config.HTTPServer)
|
||||
if hErr != nil {
|
||||
log.Error("Http Server error: %v,\n", hErr)
|
||||
}
|
||||
httpServer := benefactorHttp.NewServer(*hServer, *benefactorHandler)
|
||||
|
||||
return &Application{
|
||||
Config: config,
|
||||
HTTPServer: httpServer,
|
||||
BenefactorService: benefactorSvc,
|
||||
BenefactorHandler: *benefactorHandler,
|
||||
BenefactorRepo: benefactorRepo,
|
||||
DBConn: DB,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (app *Application) Start() {
|
||||
log := logger.L()
|
||||
log.Info("app starting ...")
|
||||
// TODO implementaion
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,23 @@
|
|||
package benefactorapp
|
||||
|
||||
import (
|
||||
database "git.gocasts.ir/ebhomengo/niki/pkg/database/mysql"
|
||||
httpserver "git.gocasts.ir/ebhomengo/niki/pkg/httpserver"
|
||||
"git.gocasts.ir/ebhomengo/niki/pkg/logger"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
// HTTP server config
|
||||
HTTPServer httpserver.Config `koanf:"http_server"`
|
||||
|
||||
// Database config
|
||||
MySQLDB database.Config `koanf:"mariadb"`
|
||||
|
||||
// Logger config
|
||||
Logger logger.Config `koanf:"logger"`
|
||||
|
||||
// Service config
|
||||
|
||||
// Database migration
|
||||
PathOfMigration string `koanf:"path_of_migration"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,20 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
benefactor "git.gocasts.ir/ebhomengo/niki/benefactorapp/service"
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type Handler struct{}
|
||||
type Handler struct {
|
||||
BebefactorService benefactor.Service
|
||||
}
|
||||
|
||||
func NewHandler() *Handler {
|
||||
return &Handler{}
|
||||
func NewHandler(bService benefactor.Service) *Handler {
|
||||
return &Handler{
|
||||
BebefactorService: bService,
|
||||
}
|
||||
}
|
||||
|
||||
func (h Handler) HealthCheck(c echo.Context) error {
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
package http
|
||||
|
||||
import httpserver "git.gocasts.ir/ebhomengo/niki/delivery/http_server"
|
||||
import httpserver "git.gocasts.ir/ebhomengo/niki/pkg/httpserver"
|
||||
|
||||
type Server struct {
|
||||
HTTPServer *httpserver.Server
|
||||
Handler *Handler
|
||||
HTTPServer httpserver.Server
|
||||
Handler Handler
|
||||
}
|
||||
|
||||
func NewServer(httpserver *httpserver.Server) *Server {
|
||||
return &Server{
|
||||
func NewServer(httpserver httpserver.Server, handler Handler) Server {
|
||||
return Server{
|
||||
HTTPServer: httpserver,
|
||||
Handler: NewHandler(),
|
||||
Handler: handler,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package database
|
||||
|
||||
import "git.gocasts.ir/ebhomengo/niki/repository/mysql"
|
||||
import "git.gocasts.ir/ebhomengo/niki/pkg/database/mysql"
|
||||
|
||||
type DB struct {
|
||||
conn *mysql.DB
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
-- +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 `benefactors1` (
|
||||
`id` INT PRIMARY KEY AUTO_INCREMENT,
|
||||
`first_name` VARCHAR(191),
|
||||
`last_name` VARCHAR(191),
|
||||
`phone_number` VARCHAR(191) NOT NULL UNIQUE,
|
||||
`description` TEXT,
|
||||
`email` VARCHAR(191),
|
||||
`gender` ENUM('male','female'),
|
||||
`birth_date` TIMESTAMP,
|
||||
`status` ENUM('active','inactive') NOT NULL DEFAULT 'active',
|
||||
|
||||
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- +migrate Down
|
||||
DROP TABLE `benefactors1`;
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
-- +migrate Up
|
||||
CREATE TABLE `addresses1` (
|
||||
`id` INT PRIMARY KEY AUTO_INCREMENT,
|
||||
`postal_code` VARCHAR(191) NOT NULL,
|
||||
`address` TEXT NOT NULL,
|
||||
`lat` FLOAT,
|
||||
`lon` FLOAT,
|
||||
`name` VARCHAR(191) NOT NULL,
|
||||
`city_id` INT NOT NULL,
|
||||
`province_id` INT NOT NULL,
|
||||
`benefactor_id` INT NOT NULL,
|
||||
|
||||
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
`deleted_at` TIMESTAMP,
|
||||
FOREIGN KEY (`province_id`) REFERENCES `provinces` (`id`),
|
||||
FOREIGN KEY (`city_id`) REFERENCES `cities` (`id`),
|
||||
FOREIGN KEY (`benefactor_id`) REFERENCES `benefactors1` (`id`)
|
||||
);
|
||||
|
||||
-- +migrate Down
|
||||
DROP TABLE `addresses1`;
|
||||
|
|
@ -1 +1,17 @@
|
|||
package service
|
||||
|
||||
type Service struct {
|
||||
repository Repository
|
||||
validator Validator
|
||||
}
|
||||
|
||||
type Repository interface {
|
||||
//GetList(ctx context.Context, ID types.ID) ([]entity.Benefactor, error)
|
||||
}
|
||||
|
||||
func NewService(repo Repository, validator Validator) Service {
|
||||
return Service{
|
||||
repository: repo,
|
||||
validator: validator,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1,11 @@
|
|||
package service
|
||||
|
||||
type ValidatorBenefactorRepository interface {
|
||||
}
|
||||
type Validator struct {
|
||||
repo ValidatorBenefactorRepository
|
||||
}
|
||||
|
||||
func NewValidator(repo ValidatorBenefactorRepository) Validator {
|
||||
return Validator{repo: repo}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
migrator "git.gocasts.ir/ebhomengo/niki/pkg/database/migrator"
|
||||
"git.gocasts.ir/ebhomengo/niki/pkg/logger"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var up bool
|
||||
var down bool
|
||||
|
||||
var migrateCmd = &cobra.Command{
|
||||
Use: "migrate",
|
||||
Short: "Run database migrations",
|
||||
Long: `This command runs the database migrations for the benefactor service.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
migrate()
|
||||
},
|
||||
}
|
||||
|
||||
func migrate() {
|
||||
var cfg = loadAppConfig()
|
||||
mysqlConfig := getMysqlConfig()
|
||||
|
||||
logger.Init(cfg.Logger)
|
||||
log := logger.L()
|
||||
|
||||
migratorCfg := migrator.Config{
|
||||
MysqlConfig: mysqlConfig,
|
||||
MigrationPath: cfg.PathOfMigration,
|
||||
MigrationDBName: "gorp_migrations",
|
||||
}
|
||||
// Run migrations if flags are set
|
||||
if migrateUp || migrateDown {
|
||||
mgr := migrator.New(migratorCfg)
|
||||
if migrateUp {
|
||||
log.Info("Running migrations up...")
|
||||
mgr.Up()
|
||||
log.Info("Migrations up completed.")
|
||||
}
|
||||
if migrateDown {
|
||||
log.Info("Running migrations down...")
|
||||
mgr.Down()
|
||||
log.Info("Migrations down completed.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
migrateCmd.Flags().BoolVar(&up, "up", false, "Run migrations up")
|
||||
migrateCmd.Flags().BoolVar(&down, "down", false, "Run migrations down")
|
||||
RootCmd.AddCommand(migrateCmd)
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.gocasts.ir/ebhomengo/niki/benefactorapp"
|
||||
cfgloader "git.gocasts.ir/ebhomengo/niki/pkg/cfg_loader"
|
||||
"git.gocasts.ir/ebhomengo/niki/pkg/database/mysql"
|
||||
database "git.gocasts.ir/ebhomengo/niki/pkg/database/mysql"
|
||||
serviceConfigMysql "git.gocasts.ir/ebhomengo/niki/pkg/database/mysql"
|
||||
"git.gocasts.ir/ebhomengo/niki/pkg/path"
|
||||
"github.com/spf13/cobra"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
var RootCmd = &cobra.Command{
|
||||
Use: "benefactor_service",
|
||||
Short: "A CLI for benefactor Service",
|
||||
Long: `benefactor Service CLI is a tool to manage and run
|
||||
the benefactor service, including migrations and server startup.`,
|
||||
}
|
||||
|
||||
func getMysqlConfig() mysql.Config {
|
||||
var cfg = loadAppConfig()
|
||||
|
||||
mysqlConfig := serviceConfigMysql.Config{
|
||||
Username: cfg.MySQLDB.Username,
|
||||
Password: cfg.MySQLDB.Password,
|
||||
Port: cfg.MySQLDB.Port,
|
||||
Host: cfg.MySQLDB.Host,
|
||||
DBName: cfg.MySQLDB.DBName,
|
||||
}
|
||||
|
||||
return mysqlConfig
|
||||
}
|
||||
|
||||
func getDB(cfg database.Config) *mysql.DB {
|
||||
|
||||
db := serviceConfigMysql.New(cfg)
|
||||
defer func() {
|
||||
if err := db.CloseStatements(); err != nil {
|
||||
fmt.Printf("Error closing statements: %v\n", err)
|
||||
}
|
||||
}()
|
||||
return db
|
||||
}
|
||||
func loadAppConfig() benefactorapp.Config {
|
||||
var cfg benefactorapp.Config
|
||||
|
||||
projectRoot, err := path.PathProjectRoot()
|
||||
if err != nil {
|
||||
log.Fatalf("Error finding project root: %v", err)
|
||||
}
|
||||
|
||||
yamlPath := os.Getenv("CONFIG_PATH")
|
||||
|
||||
if yamlPath == "" {
|
||||
defaultConfig := filepath.Join(projectRoot, "deploy", "benefactor", "development", "config.yml")
|
||||
if _, err := os.Stat(defaultConfig); err == nil {
|
||||
yamlPath = defaultConfig
|
||||
} else {
|
||||
yamlPath = filepath.Join(projectRoot, "deploy", "benefactor", "development", "config.local.yml")
|
||||
}
|
||||
}
|
||||
|
||||
options := cfgloader.Option{
|
||||
Prefix: "BENEFACTOR_",
|
||||
Delimiter: ".",
|
||||
Separator: "__",
|
||||
YamlFilePath: yamlPath,
|
||||
CallbackEnv: nil,
|
||||
}
|
||||
|
||||
if err := cfgloader.Load(options, &cfg); err != nil {
|
||||
log.Fatalf("Failed to load benefactor config: %v", err)
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"git.gocasts.ir/ebhomengo/niki/benefactorapp"
|
||||
"git.gocasts.ir/ebhomengo/niki/pkg/logger"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var migrateUp bool
|
||||
var migrateDown bool
|
||||
|
||||
var serveCmd = &cobra.Command{
|
||||
Use: "serve",
|
||||
Short: "Start the benefactor service",
|
||||
Long: `This command starts the main benefactor service.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
serve()
|
||||
},
|
||||
}
|
||||
|
||||
func serve() {
|
||||
var cfg = loadAppConfig()
|
||||
|
||||
// Initialize logger
|
||||
logger.Init(cfg.Logger)
|
||||
log := logger.L()
|
||||
|
||||
db := getDB(cfg.MySQLDB)
|
||||
migrate()
|
||||
|
||||
// Start the server
|
||||
log.Info("Starting benefactor Service...")
|
||||
|
||||
// Connect to the database
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
app := benefactorapp.Setup(ctx, cfg, *db)
|
||||
app.Start()
|
||||
}
|
||||
|
||||
func init() {
|
||||
serveCmd.Flags().BoolVar(&migrateUp, "migrate-up", false, "Run migrations up before starting the server")
|
||||
serveCmd.Flags().BoolVar(&migrateDown, "migrate-down", false, "Run migrations down before starting the server")
|
||||
RootCmd.AddCommand(serveCmd)
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"git.gocasts.ir/ebhomengo/niki/cmd/benefactor/command"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := command.RootCmd.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"git.gocasts.ir/ebhomengo/niki/repository/migrator"
|
||||
"git.gocasts.ir/ebhomengo/niki/repository/mysql"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var up bool
|
||||
var down bool
|
||||
|
||||
var migrateCmd = &cobra.Command{
|
||||
Use: "migrate",
|
||||
Short: "Run database migrations",
|
||||
Long: `This command runs the database migrations for the product service.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
migrate()
|
||||
},
|
||||
}
|
||||
|
||||
func migrate() {
|
||||
workingDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatalf("Error getting working directory: %v", err)
|
||||
}
|
||||
|
||||
migrationPath := filepath.Join(workingDir, "productapp", "repository", "migrations")
|
||||
|
||||
// to run migrations when you want to run product service locally
|
||||
if path := os.Getenv("MIGRATION_PATH"); path != "" {
|
||||
migrationPath = path
|
||||
log.Printf("Using override migration path: %s", migrationPath)
|
||||
} else {
|
||||
log.Printf("Using default migration path: %s", migrationPath)
|
||||
}
|
||||
|
||||
// TODO: Load config from environment or config file
|
||||
mgr := migrator.New(migrator.Config{
|
||||
MysqlConfig: mysql.Config{
|
||||
Username: getEnv("DB_USERNAME", "root"),
|
||||
Password: getEnv("DB_PASSWORD", ""),
|
||||
Port: 3306,
|
||||
Host: getEnv("DB_HOST", "localhost"),
|
||||
DBName: getEnv("DB_NAME", "niki_db"),
|
||||
},
|
||||
MigrationPath: migrationPath,
|
||||
MigrationDBName: "product_migrations",
|
||||
})
|
||||
|
||||
if up {
|
||||
mgr.Up()
|
||||
} else if down {
|
||||
mgr.Down()
|
||||
} else {
|
||||
log.Println("Please specify a migration direction with --up or --down")
|
||||
}
|
||||
}
|
||||
|
||||
func getEnv(key, defaultValue string) string {
|
||||
if value := os.Getenv(key); value != "" {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func init() {
|
||||
migrateCmd.Flags().BoolVar(&up, "up", false, "Run migrations up")
|
||||
migrateCmd.Flags().BoolVar(&down, "down", false, "Run migrations down")
|
||||
RootCmd.AddCommand(migrateCmd)
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package command
|
||||
|
||||
import "github.com/spf13/cobra"
|
||||
|
||||
var RootCmd = &cobra.Command{
|
||||
Use: "product_service",
|
||||
Short: "A CLI for Product Service",
|
||||
Long: `Product Service CLI is a tool to manage and run
|
||||
the product service, including migrations and server startup.`,
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var port string
|
||||
|
||||
var serveCmd = &cobra.Command{
|
||||
Use: "serve",
|
||||
Short: "Start the product service",
|
||||
Long: `This command starts the main product service.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
serve()
|
||||
},
|
||||
}
|
||||
|
||||
func serve() {
|
||||
log.Println("Product Service Starting...")
|
||||
|
||||
// TODO: Initialize database connection
|
||||
// TODO: Initialize service dependencies
|
||||
// TODO: Setup HTTP server with routes
|
||||
|
||||
// Setup graceful shutdown
|
||||
go func() {
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-sigCh
|
||||
log.Println("Shutting down Product Service gracefully...")
|
||||
os.Exit(0)
|
||||
}()
|
||||
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "Product Service OK!")
|
||||
})
|
||||
|
||||
log.Printf("Product Service listening on port %s", port)
|
||||
if err := http.ListenAndServe(":"+port, nil); err != nil {
|
||||
log.Fatalf("Failed to start server: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
serveCmd.Flags().StringVarP(&port, "port", "p", "8080", "Port to run the server on")
|
||||
RootCmd.AddCommand(serveCmd)
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"git.gocasts.ir/ebhomengo/niki/cmd/productapp/command"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := command.RootCmd.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
package command
|
||||
|
|
@ -0,0 +1 @@
|
|||
package command
|
||||
|
|
@ -0,0 +1 @@
|
|||
package command
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
purchaseMysql "git.gocasts.ir/ebhomengo/niki/domain/purchase/repository/mysql"
|
||||
"git.gocasts.ir/ebhomengo/niki/purchaseapp/delivery/http"
|
||||
"git.gocasts.ir/ebhomengo/niki/purchaseapp/service/order"
|
||||
"git.gocasts.ir/ebhomengo/niki/repository/migrator"
|
||||
"git.gocasts.ir/ebhomengo/niki/repository/mysql"
|
||||
)
|
||||
|
||||
func MariaDB() *mysql.DB {
|
||||
cfg := mysql.Config{
|
||||
Username: "niki",
|
||||
Password: "nikiappt0lk2o20",
|
||||
Port: 3306,
|
||||
Host: "localhost",
|
||||
DBName: "niki_db",
|
||||
}
|
||||
migrate := flag.Bool("migrate", false, "perform database migration")
|
||||
flag.Parse()
|
||||
if *migrate {
|
||||
migrator.New(migrator.Config{
|
||||
MysqlConfig: cfg,
|
||||
MigrationPath: "./purchaseapp/repository/mysql/migration",
|
||||
MigrationDBName: "gorp_migrations",
|
||||
}).Up()
|
||||
}
|
||||
|
||||
return mysql.New(cfg)
|
||||
}
|
||||
|
||||
func main() {
|
||||
cfg := mysql.Config{
|
||||
Username: "niki",
|
||||
Password: "nikiappt0lk2o20",
|
||||
Port: 3306,
|
||||
Host: "localhost",
|
||||
DBName: "niki_db",
|
||||
}
|
||||
db := mysql.New(cfg)
|
||||
defer func() {
|
||||
if err := db.CloseStatements(); err != nil {
|
||||
fmt.Printf("Error closing statements: %v\n", err)
|
||||
}
|
||||
}()
|
||||
|
||||
orderRepo := purchaseMysql.New(db)
|
||||
|
||||
orderSvc := Service(orderRepo)
|
||||
server := HTTPServer(orderSvc)
|
||||
server.Serve()
|
||||
|
||||
}
|
||||
|
||||
func HTTPServer(orderSvc order.Service) *http.Server {
|
||||
return http.New(orderSvc)
|
||||
}
|
||||
|
||||
func Service(orderRepo *purchaseMysql.DB) order.Service {
|
||||
return order.New(orderRepo)
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
package command
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
cfgloader "git.gocasts.ir/ebhomengo/niki/pkg/cfg_loader"
|
||||
"git.gocasts.ir/ebhomengo/niki/pkg/path"
|
||||
"git.gocasts.ir/ebhomengo/niki/shoppingbasketapp"
|
||||
"github.com/spf13/cobra"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
var RootCmd = &cobra.Command{
|
||||
Use: "shoppingbasket_service",
|
||||
Short: "A CLI for shoppingbasket service",
|
||||
Long: `shoppingbasket Service CLI is a tool to manage and run
|
||||
the shoppingbasket service, including migrations and server startup.`,
|
||||
}
|
||||
|
||||
func loadAppConfig() shoppingbasketapp.Config {
|
||||
var cfg shoppingbasketapp.Config
|
||||
|
||||
projectRoot, err := path.PathProjectRoot()
|
||||
if err != nil {
|
||||
log.Fatalf("error finding project root: %v", err)
|
||||
}
|
||||
|
||||
yamlPath := os.Getenv("CONFIG_PATH")
|
||||
|
||||
if yamlPath == "" {
|
||||
defaultConfig := filepath.Join(projectRoot, "deploy", "shoppingbasket", "development", "config.yml")
|
||||
if _, err := os.Stat(defaultConfig); err == nil {
|
||||
yamlPath = defaultConfig
|
||||
} else {
|
||||
yamlPath = filepath.Join(projectRoot, "deploy", "shoppingbasket", "development", "config.local.yml")
|
||||
}
|
||||
}
|
||||
|
||||
options := cfgloader.Option{
|
||||
Prefix: "SHOPPINGBASKET_",
|
||||
Delimiter: ".",
|
||||
Separator: "__",
|
||||
YamlFilePath: yamlPath,
|
||||
CallbackEnv: nil,
|
||||
}
|
||||
|
||||
if err := cfgloader.Load(options, &cfg); err != nil {
|
||||
log.Fatalf("Failed to load benefactor config: %v", err)
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"git.gocasts.ir/ebhomengo/niki/pkg/logger"
|
||||
"git.gocasts.ir/ebhomengo/niki/shoppingbasketapp"
|
||||
"github.com/labstack/gommon/log"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var ServeCmd = &cobra.Command{
|
||||
Use: "serve",
|
||||
Short: "Start shoppingbasket service",
|
||||
Long: `This command starts the main shoppingbasket service.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
func serve() {
|
||||
var cfg = loadAppConfig()
|
||||
|
||||
logger.Init(cfg.Logger)
|
||||
l := logger.L()
|
||||
|
||||
l.Info("Starting shoppingbasket service...")
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
app, err := shoppingbasketapp.Setup(ctx, cfg)
|
||||
if err != nil {
|
||||
l.Error("failed initialize shopping basket app", "error", err)
|
||||
log.Fatalf(fmt.Sprintf("error starting shopping basket app: %v", err))
|
||||
}
|
||||
|
||||
app.Start()
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(ServeCmd)
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"git.gocasts.ir/ebhomengo/niki/cmd/shoppingbasketapp/command"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := command.RootCmd.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,143 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"git.gocasts.ir/ebhomengo/niki/staffapp/repository/database"
|
||||
"git.gocasts.ir/ebhomengo/niki/staffapp/service"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(" Staffapp Server Starting...")
|
||||
|
||||
staffDb := database.New()
|
||||
staffService := service.NewStaffService(staffDb)
|
||||
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "Staffapp OK!")
|
||||
})
|
||||
|
||||
http.HandleFunc("/staff", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
var newStaff service.Staff
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err := decoder.Decode(&newStaff)
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid request payload: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
createdStaff, err := staffService.RegisterStaff(newStaff.Name, newStaff.LastName, newStaff.PhoneNumber)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to register staff: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(createdStaff)
|
||||
})
|
||||
|
||||
http.HandleFunc("/staff/", func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
idStr := r.URL.Path[len("/staff/"):]
|
||||
if idStr == "" {
|
||||
http.Error(w, "Missing staff ID", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
id, err := strconv.Atoi(idStr)
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid staff ID format", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
|
||||
st, err := staffService.Get(id)
|
||||
if err != nil {
|
||||
|
||||
if err.Error() == "staff not found" {
|
||||
http.Error(w, "Staff not found", http.StatusNotFound)
|
||||
} else {
|
||||
http.Error(w, "Failed to fetch staff: "+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(st)
|
||||
|
||||
case http.MethodPut:
|
||||
|
||||
var staffData struct {
|
||||
Name string `json:"Name"`
|
||||
LastName string `json:"LastName"`
|
||||
PhoneNumber string `json:"PhoneNumber"`
|
||||
}
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
if err := decoder.Decode(&staffData); err != nil {
|
||||
http.Error(w, "Invalid request payload: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
updatedStaff, err := staffService.Update(id, staffData.Name, staffData.LastName, staffData.PhoneNumber)
|
||||
if err != nil {
|
||||
|
||||
if err.Error() == "staff not found" {
|
||||
http.Error(w, "Staff not found", http.StatusNotFound)
|
||||
} else {
|
||||
http.Error(w, "Failed to update staff: "+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(updatedStaff)
|
||||
|
||||
case http.MethodDelete:
|
||||
|
||||
err = staffService.Remove(id)
|
||||
if err != nil {
|
||||
if err.Error() == "staff not found" {
|
||||
http.Error(w, "Staff not found", http.StatusNotFound)
|
||||
} else {
|
||||
http.Error(w, "Failed to remove staff: "+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
||||
default:
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
})
|
||||
|
||||
http.HandleFunc("/staffs", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
list, err := staffService.List()
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to fetch staff list: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(list)
|
||||
})
|
||||
|
||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,12 +12,13 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
defaultPrefix = "EB_"
|
||||
defaultDelimiter = "."
|
||||
defaultSeparator = "__"
|
||||
defaultYamlFilePath = "config.yml"
|
||||
defaultPrefix = "EB_"
|
||||
defaultDelimiter = "."
|
||||
defaultSeparator = "__"
|
||||
)
|
||||
|
||||
var defaultYamlFilePath = "config.yml"
|
||||
|
||||
var c Config
|
||||
|
||||
type Option struct {
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ type TestContainer struct {
|
|||
dockerPool *dockertest.Pool // the connection pool to Docker.
|
||||
mariaResource *dockertest.Resource // MariaDB Docker container resource.
|
||||
redisResource *dockertest.Resource // Redis Docker container resource.
|
||||
mariaDBConn *mysql.DB // Connection to the MariaDB database.
|
||||
redisDBConn *redisadapter.Adapter // Connection to the Redis database.
|
||||
mariaDBConn *mysql.DB // Connection to the MariaDB mysql.
|
||||
redisDBConn *redisadapter.Adapter // Connection to the Redis mysql.
|
||||
containerExpiryInSeconds uint
|
||||
}
|
||||
|
||||
|
|
@ -158,7 +158,7 @@ func (t *TestContainer) Start() {
|
|||
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Fatalf("Could not connect to database: %s", err)
|
||||
log.Fatalf("Could not connect to mysql: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
FROM golang:1.25-alpine
|
||||
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
http_server:
|
||||
host: ""
|
||||
port: 1308
|
||||
shutdown_context_timeout: 10s
|
||||
cors:
|
||||
allow_origins:
|
||||
- "*"
|
||||
|
||||
logger:
|
||||
level: "debug" # Can be `debug`, `info`, `warn`, `error`
|
||||
file_path: "logs/benefactorapp/service.log"
|
||||
use_local_time: true
|
||||
file_max_size_in_mb: 10
|
||||
file_max_age_in_days: 7
|
||||
|
||||
database_retry:
|
||||
max_retries: 3
|
||||
retry_delay: 100ms
|
||||
|
||||
total_shutdown_timeout: 30m
|
||||
|
||||
path_of_migration: "./benefactorapp/repository/migration"
|
||||
|
||||
mariadb:
|
||||
port: 3306
|
||||
host: localhost
|
||||
db_name: niki_db
|
||||
username: niki
|
||||
password: nikiappt0lk2o20
|
||||
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
redis:
|
||||
host: "localhost"
|
||||
port: 6379
|
||||
password: ""
|
||||
db: 0
|
||||
|
||||
repo:
|
||||
kart_key_prefix: "shopping-basket-cart:"
|
||||
ttl: 3600s
|
||||
|
||||
http_server:
|
||||
host: "localhost"
|
||||
port: 8080
|
||||
shutdown_context_timeout: 10s
|
||||
cors:
|
||||
allow_origins:
|
||||
- "*"
|
||||
|
||||
|
||||
logger:
|
||||
level: "debug" # Can be `debug`, `info`, `warn`, `error`
|
||||
file_path: "logs/shoppingbasketapp/service.log"
|
||||
use_local_time: true
|
||||
file_max_size_in_mb: 10
|
||||
file_max_age_in_days: 7
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package domain
|
||||
|
||||
import "time"
|
||||
|
||||
type ID uint64
|
||||
|
||||
type CampaignStatus string
|
||||
|
||||
const (
|
||||
CampaignDraft CampaignStatus = "draft"
|
||||
CampaignActive CampaignStatus = "active"
|
||||
CampaignFinished CampaignStatus = "completed"
|
||||
CampaignPaused CampaignStatus = "paused"
|
||||
CampaignCanceled CampaignStatus = "cancelled"
|
||||
)
|
||||
|
||||
type Campaign struct {
|
||||
ID ID `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
GoalAmount float64 `json:"goal_amount"`
|
||||
RaisedAmount float64 `json:"raised_amount"`
|
||||
Status CampaignStatus `json:"status"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
DeadlineAt *time.Time `json:"deadline_at,omitempty"`
|
||||
AdminID ID `json:"creator_id"`
|
||||
}
|
||||
|
||||
// Behavior
|
||||
func (c *Campaign) Activate() {
|
||||
if c.Status == CampaignDraft {
|
||||
c.Status = CampaignActive
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Campaign) AddFunds(amount float64) {
|
||||
c.RaisedAmount += amount
|
||||
if c.RaisedAmount >= c.GoalAmount {
|
||||
c.Status = CampaignFinished
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Campaign) IsExpired(now time.Time) bool {
|
||||
if c.DeadlineAt == nil {
|
||||
return false
|
||||
}
|
||||
return now.After(*c.DeadlineAt)
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
package campaign
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors" // For standard errors
|
||||
"time"
|
||||
|
||||
"your_project/domain"
|
||||
"your_project/pkg/richerror"
|
||||
"your_project/repository"
|
||||
)
|
||||
|
||||
type ID = unit64
|
||||
type EntityCampaign = domain.Campaign
|
||||
|
||||
|
||||
type CampaignServiceImp interface {
|
||||
CreateCampaign(ctx context.Context, req CampaignRepository) (types.ID, error)
|
||||
}
|
||||
|
||||
|
||||
type CampaignService struct {
|
||||
repo repository.CampaignRepository
|
||||
}
|
||||
|
||||
func NewCampaignService(
|
||||
campaignRepo repository.CampaignRepository,
|
||||
) *CampaignService {
|
||||
return &CampaignService{
|
||||
repo: campaignRepo,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func (s *CampaignService) CreateCampaign(ctx context.Context, req CreateCampaignRequest) (ID, error) {
|
||||
const Op = "service.campaign.create_campaign"
|
||||
|
||||
if req.Title == "" {
|
||||
return 0, richerror.New(Op).WithMessage("title is required")
|
||||
}
|
||||
if req.GoalAmount <= 0 {
|
||||
return 0, richerror.New(Op).WithMessage("goal_amount must be greater than 0")
|
||||
}
|
||||
if req.AdminID == 0 {
|
||||
return 0, richerror.New(Op).WithMessage("admin_id is required")
|
||||
}
|
||||
|
||||
validStatuses := map[string]bool{
|
||||
"draft": true,
|
||||
"active": true,
|
||||
"completed": true,
|
||||
"cancelled": true,
|
||||
"paused": true
|
||||
}
|
||||
if !validStatuses[req.Status] {
|
||||
return 0, richerror.New(Op).WithMessage("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(),
|
||||
}
|
||||
|
||||
createdCampaignID, err := s.repo.CreateAndSave(ctx, newCampaign)
|
||||
if err != nil {
|
||||
return 0, richerror.New(Op).WithErr(err)
|
||||
}
|
||||
|
||||
return createdCampaignID, nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package entity
|
||||
|
||||
type Channel struct {
|
||||
ID int8
|
||||
Type NotificationType
|
||||
Provider string
|
||||
Config string
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package entity
|
||||
|
||||
type Notification struct {
|
||||
ID int8
|
||||
Type NotificationType
|
||||
Recipinet string
|
||||
Body string
|
||||
Status NotificationStatus
|
||||
}
|
||||
|
||||
type NotificationType uint8
|
||||
|
||||
const (
|
||||
Email NotificationType = iota + 1
|
||||
SMS
|
||||
Push
|
||||
)
|
||||
|
||||
func (t NotificationType) String() string {
|
||||
switch t {
|
||||
case Email:
|
||||
return "Email"
|
||||
case SMS:
|
||||
return "SMS"
|
||||
case Push:
|
||||
return "Push"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
type NotificationStatus uint8
|
||||
|
||||
const (
|
||||
Pending NotificationStatus = iota + 1
|
||||
Success
|
||||
Failed
|
||||
)
|
||||
|
||||
func (t NotificationStatus) String() string {
|
||||
switch t {
|
||||
case Pending:
|
||||
return "Pending"
|
||||
case Success:
|
||||
return "Success"
|
||||
case Failed:
|
||||
return "Failed"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package messagebroker
|
||||
|
||||
import "git.gocasts.ir/ebhomengo/niki/domain/notification/entity"
|
||||
|
||||
type redis struct {
|
||||
}
|
||||
|
||||
func (r *redis) AddItem(notification entity.Notification) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *redis) RemoveItem(notification entity.Notification) error {
|
||||
return nil
|
||||
}
|
||||
func (r *redis) HealthCheck() error {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
_ "time"
|
||||
|
||||
"git.gocasts.ir/ebhomengo/niki/domain/notification/entity"
|
||||
)
|
||||
|
||||
type Notifservice struct {
|
||||
MessageBroker MessageBroker
|
||||
Repository Repository
|
||||
Channel Channel
|
||||
}
|
||||
|
||||
type MessageBroker interface {
|
||||
AddItem(notification entity.Notification) error
|
||||
RemoveItem(notification entity.Notification) error
|
||||
HealthCheck() error
|
||||
}
|
||||
|
||||
type Repository interface {
|
||||
AddItem(notification entity.Notification) error
|
||||
}
|
||||
|
||||
type Channel interface {
|
||||
SendMessage(notification entity.Notification) error
|
||||
}
|
||||
type NotificationServiceRequest struct {
|
||||
Body string
|
||||
Type entity.NotificationType
|
||||
Recipinet string
|
||||
}
|
||||
|
||||
func (n *Notifservice) NewNotification(r NotificationServiceRequest) *entity.Notification {
|
||||
// TODO add validation of notification properties
|
||||
return &entity.Notification{
|
||||
Type: r.Type,
|
||||
Recipinet: r.Recipinet,
|
||||
Body: r.Body,
|
||||
Status: entity.Pending,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Notifservice) Send(notification entity.Notification) error {
|
||||
if err := n.Channel.SendMessage(notification); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *Notifservice) Add(notification *entity.Notification) error {
|
||||
err := n.MessageBroker.AddItem(*notification)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *Notifservice) Archive(notification *entity.Notification) error {
|
||||
if err := n.MessageBroker.RemoveItem(*notification); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := n.Repository.AddItem(*notification); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package service
|
||||
|
||||
import "git.gocasts.ir/ebhomengo/niki/domain/notification/entity"
|
||||
|
||||
type sender interface {
|
||||
Send(notification entity.Notification) error
|
||||
}
|
||||
|
|
@ -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"
|
||||
)
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
-- +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,
|
||||
`user_id` INT,
|
||||
`address_id` INT,
|
||||
`shipping_id` INT NOT NULL,
|
||||
`payment_method` ENUM('online', 'wallet', 'cart') DEFAULT 'online',
|
||||
`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,
|
||||
|
||||
`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`)
|
||||
|
||||
);
|
||||
|
||||
-- +migrate Down
|
||||
DROP TABLE `orders`;
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
-- +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 `order_items` (
|
||||
`id` INT PRIMARY KEY AUTO_INCREMENT,
|
||||
`order_id` INT NOT NULL,
|
||||
`product_id` INT NOT NULL,
|
||||
`quantity` INT DEFAULT 1,
|
||||
`price` INT NOT NULL,
|
||||
`price_with_discount` INT NULL,
|
||||
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
|
||||
FOREIGN KEY (`order_id`) REFERENCES `orders`(`id`)
|
||||
|
||||
);
|
||||
|
||||
-- +migrate Down
|
||||
DROP TABLE `order_items`;
|
||||
|
|
@ -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`;
|
||||
|
|
@ -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}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
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/types"
|
||||
)
|
||||
|
||||
func (d *DB) CreateOrder(order entity.Order, orderItems []entity.OrderItem) (types.ID, error) {
|
||||
|
||||
const Op = "domain.repository.mysql.order.create-order"
|
||||
tx, err := d.conn.Conn().Begin()
|
||||
|
||||
if err != nil {
|
||||
return 0, richerror.New(Op).WithErr(err)
|
||||
}
|
||||
|
||||
defer tx.Rollback()
|
||||
|
||||
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.AddressID, order.ShippingID,
|
||||
order.PaymentMethod, order.PaymentStatus, order.ProcessStatus,
|
||||
order.TotalAmount, order.TotalDiscount)
|
||||
|
||||
if oErr != nil {
|
||||
return 0, richerror.New(Op).WithErr(oErr)
|
||||
}
|
||||
orderID, insertIDErr := res.LastInsertId()
|
||||
if insertIDErr != nil {
|
||||
return 0, richerror.New(Op).WithErr(insertIDErr)
|
||||
}
|
||||
|
||||
orderItemQuery := "insert into order_items(order_id, product_id, quantity, price, price_with_discount) values(?, ?, ?, ?, ?);"
|
||||
for _, item := range orderItems {
|
||||
_, iErr := tx.Exec(orderItemQuery, orderID, item.ProductID, item.Quantity, item.Price, item.PriceWithDiscount)
|
||||
if iErr != nil {
|
||||
return 0, richerror.New(Op).WithErr(iErr)
|
||||
}
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return 0, richerror.New(Op).WithErr(err)
|
||||
}
|
||||
|
||||
return types.ID(orderID), nil
|
||||
}
|
||||
|
||||
func (d *DB) UpdateOrderProcessStatus(orderID types.ID, status string) (bool, error) {
|
||||
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 {
|
||||
return false, richerror.New(Op).WithErr(err)
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
entity "git.gocasts.ir/ebhomengo/niki/domain/order/entity"
|
||||
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
|
||||
types "git.gocasts.ir/ebhomengo/niki/types"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
repo Repo
|
||||
}
|
||||
|
||||
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) (types.ID, error) {
|
||||
const Op = "domain.order.service.order.CreateOrder"
|
||||
orderID, err := s.repo.CreateOrder(order, orderItems)
|
||||
|
||||
if err != nil {
|
||||
return 0, richerror.New(Op).WithErr(err)
|
||||
}
|
||||
|
||||
return orderID, nil
|
||||
}
|
||||
|
||||
func (s *Service) UpdateOrderProcessStatus(orderID types.ID, status string) (bool, error) {
|
||||
|
||||
const Op = "domain.order.service.order.UpdateOrderProcessStatus"
|
||||
_, err := s.repo.UpdateOrderProcessStatus(orderID, status)
|
||||
if err != nil {
|
||||
return false, richerror.New(Op).WithErr(err)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
package donate
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
package donateapp
|
||||
|
||||
type Config struct{
|
||||
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package donate_server
|
||||
|
||||
type Handler struct {
|
||||
}
|
||||
|
||||
func NewHandler() Handler {
|
||||
return Handler{}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
-- +migrate Up
|
||||
CREATE TABLE `donates` (
|
||||
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE INDEX `id`(`id` ASC) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 84 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_persian_ci ROW_FORMAT = Dynamic;
|
||||
|
||||
-- +migrate Down
|
||||
DROP TABLE IF EXISTS `donates`;
|
||||
|
|
@ -1 +0,0 @@
|
|||
package mysql
|
||||
|
|
@ -1 +0,0 @@
|
|||
package service
|
||||
|
|
@ -1 +0,0 @@
|
|||
package service
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
package doanteApp
|
||||
package donate_app
|
||||
|
||||
import "net/http"
|
||||
|
||||
type Application struct {
|
||||
Config Config
|
||||
HTTPServer *http.Server
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package donate_app
|
||||
|
||||
|
||||
|
||||
|
||||
import (
|
||||
"git.gocasts.ir/ebhomengo/niki/repository/mysql"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Mysql mysql.Config `koanf:"mariadb"`
|
||||
}
|
||||
|
||||
|
||||
type Config struct{
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package donate_server
|
||||
|
||||
type Handler struct{}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
// --- Type Aliases ---
|
||||
package pkg
|
||||
|
||||
type ID uint64
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
-- +migrate Up
|
||||
|
||||
CREATE TABLE `campaigns` (
|
||||
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
`title` VARCHAR(255) NOT NULL,
|
||||
`description` TEXT,
|
||||
`goal_amount` DECIMAL(15,2) NOT NULL,
|
||||
`raised_amount` DECIMAL(15,2) DEFAULT 0,
|
||||
`status` VARCHAR(50) NOT NULL,
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
`deadline_at` TIMESTAMP,
|
||||
`admin_id` BIGINT NOT NULL,
|
||||
FOREIGN KEY (`admin_id`) REFERENCES `users`(`id`) ON DELETE RESTRICT
|
||||
);
|
||||
|
||||
|
||||
-- +migrate Down
|
||||
DROP TABLE `campaigns`;
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
-- +migrate Up
|
||||
|
||||
CREATE TABLE `campaign_participants` (
|
||||
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
`campaign_id` BIGINT NOT NULL,
|
||||
`user_id` BIGINT NOT NULL,
|
||||
`amount` DECIMAL(15,2) NOT NULL,
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (`campaign_id`)
|
||||
REFERENCES `campaigns`(`id`)
|
||||
ON DELETE CASCADE,
|
||||
FOREIGN KEY (`user_id`)
|
||||
REFERENCES `users`(`id`)
|
||||
ON DELETE CASCADE,
|
||||
UNIQUE KEY `unique_campaign_user` (`campaign_id`, `user_id`)
|
||||
);
|
||||
|
||||
-- +migrate Down
|
||||
DROP TABLE `campaign_participants`;
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
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 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
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 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,
|
||||
participant.ID,
|
||||
participant.CampaignID,
|
||||
participant.UserID,
|
||||
participant.Amount,
|
||||
participant.CreatedAt
|
||||
)
|
||||
if err != nil {
|
||||
return 0, richerror.New(Op).WithErr(err)
|
||||
}
|
||||
|
||||
id, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
return 0, richerror.New(Op).WithErr(err)
|
||||
}
|
||||
|
||||
return types.ID(id), nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package service
|
||||
|
||||
|
||||
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"`
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
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,
|
||||
}
|
||||
}
|
||||
|
||||
33
go.mod
33
go.mod
|
|
@ -7,23 +7,26 @@ require (
|
|||
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible
|
||||
github.com/go-ozzo/ozzo-validation/v4 v4.3.0
|
||||
github.com/go-sql-driver/mysql v1.9.3
|
||||
github.com/gocasters/rankr v0.0.0-20260222055437-aadc1fdc6a1d
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2
|
||||
github.com/kavenegar/kavenegar-go v0.0.0-20240205151018-77039f51467d
|
||||
github.com/knadh/koanf v1.5.0
|
||||
github.com/knadh/koanf/v2 v2.3.0
|
||||
github.com/labstack/echo-jwt/v4 v4.4.0
|
||||
github.com/labstack/echo/v4 v4.15.1
|
||||
github.com/ory/dockertest/v3 v3.12.0
|
||||
github.com/redis/go-redis/v9 v9.18.0
|
||||
github.com/rubenv/sql-migrate v1.8.1
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/swaggo/echo-swagger v1.5.2
|
||||
github.com/swaggo/swag v1.16.6
|
||||
golang.org/x/crypto v0.46.0
|
||||
golang.org/x/crypto v0.48.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
)
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
|
|
@ -36,20 +39,21 @@ require (
|
|||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/docker/cli v27.4.1+incompatible // indirect
|
||||
github.com/docker/docker v27.1.1+incompatible // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/docker v28.3.3+incompatible // indirect
|
||||
github.com/docker/go-connections v0.6.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/fatih/structs v1.1.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/go-gorp/gorp/v3 v3.1.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/spec v0.20.9 // indirect
|
||||
github.com/go-openapi/swag v0.22.3 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.1.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/labstack/gommon v0.4.2 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
|
|
@ -59,14 +63,15 @@ require (
|
|||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/sys/user v0.3.0 // indirect
|
||||
github.com/moby/sys/user v0.4.0 // indirect
|
||||
github.com/moby/term v0.5.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/opencontainers/runc v1.2.3 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/sv-tools/openapi v0.2.1 // indirect
|
||||
github.com/swaggo/files/v2 v2.0.0 // indirect
|
||||
|
|
@ -77,13 +82,13 @@ require (
|
|||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
golang.org/x/mod v0.30.0 // indirect
|
||||
golang.org/x/net v0.48.0 // indirect
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/sys v0.39.0 // indirect
|
||||
golang.org/x/text v0.32.0 // indirect
|
||||
golang.org/x/mod v0.33.0 // indirect
|
||||
golang.org/x/net v0.50.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.41.0 // indirect
|
||||
golang.org/x/text v0.35.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
golang.org/x/tools v0.39.0 // indirect
|
||||
golang.org/x/tools v0.42.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
|
|
|
|||
70
go.sum
70
go.sum
|
|
@ -1,7 +1,7 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
|
||||
|
|
@ -58,6 +58,7 @@ github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un
|
|||
github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
|
|
@ -68,10 +69,10 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
|
|||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/docker/cli v27.4.1+incompatible h1:VzPiUlRJ/xh+otB75gva3r05isHMo5wXDfPRi5/b4hI=
|
||||
github.com/docker/cli v27.4.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
|
||||
github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||
github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI=
|
||||
github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
|
||||
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
|
|
@ -84,8 +85,9 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
|
|||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs=
|
||||
github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw=
|
||||
|
|
@ -117,8 +119,10 @@ github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1
|
|||
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/go-viper/mapstructure/v2 v2.1.0 h1:gHnMa2Y/pIxElCH2GlZZ1lZSsn6XMtufpGyP1XxdC/w=
|
||||
github.com/go-viper/mapstructure/v2 v2.1.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/gocasters/rankr v0.0.0-20260222055437-aadc1fdc6a1d h1:uFg0KexbouyuGSzmcU9bp/Y7Ml4NXNYjqsRcF/1hnpk=
|
||||
github.com/gocasters/rankr v0.0.0-20260222055437-aadc1fdc6a1d/go.mod h1:nupI7yU3J4wEdsa/dUte/z9apDKkUWeRXlK4dGfntcY=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
|
|
@ -198,6 +202,8 @@ github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKe
|
|||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
github.com/hjson/hjson-go/v4 v4.0.0 h1:wlm6IYYqHjOdXH1gHev4VoXCaW20HdQAGCxdOEEg2cs=
|
||||
github.com/hjson/hjson-go/v4 v4.0.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||
|
|
@ -218,6 +224,8 @@ github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBF
|
|||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/knadh/koanf v1.5.0 h1:q2TSd/3Pyc/5yP9ldIrSdIz26MCcyNQzW0pEAugLPNs=
|
||||
github.com/knadh/koanf v1.5.0/go.mod h1:Hgyjp4y8v44hpZtPzs7JZfRAW5AhN7KfZcwv1RYggDs=
|
||||
github.com/knadh/koanf/v2 v2.3.0 h1:Qg076dDRFHvqnKG97ZEsi9TAg2/nFTa9hCdcSa1lvlM=
|
||||
github.com/knadh/koanf/v2 v2.3.0/go.mod h1:gRb40VRAbd4iJMYYD5IxZ6hfuopFcXBpc9bbQpZwo28=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
|
|
@ -278,8 +286,8 @@ github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zx
|
|||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
|
||||
github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
||||
github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
|
||||
github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
|
|
@ -293,8 +301,8 @@ github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnu
|
|||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||
github.com/opencontainers/runc v1.2.3 h1:fxE7amCzfZflJO2lHXf4y/y8M1BoAqp+FVmG19oYB80=
|
||||
github.com/opencontainers/runc v1.2.3/go.mod h1:nSxcWUydXrsBZVYNSkTjoQ/N6rcyTtn+1SD5D4+kRIM=
|
||||
github.com/ory/dockertest/v3 v3.12.0 h1:3oV9d0sDzlSQfHtIaB5k6ghUCVMVLpAY8hwrqoCyRCw=
|
||||
|
|
@ -337,6 +345,7 @@ github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZV
|
|||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rubenv/sql-migrate v1.8.1 h1:EPNwCvjAowHI3TnZ+4fQu3a915OpnQoPAjTXCGOy2U0=
|
||||
github.com/rubenv/sql-migrate v1.8.1/go.mod h1:BTIKBORjzyxZDS6dzoiw6eAFYJ1iNlGAtjn4LGeVjS8=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||
|
|
@ -346,8 +355,12 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
|
|||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
|
|
@ -399,13 +412,14 @@ go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
|||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
||||
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
|
|
@ -415,8 +429,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
|||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
||||
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
||||
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
|
||||
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
|
@ -434,8 +448,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
|
|||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
|
||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
|
|
@ -448,8 +462,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
|
@ -483,8 +497,8 @@ golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
|
|
@ -492,8 +506,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
||||
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
|
|
@ -508,8 +522,8 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK
|
|||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
|
||||
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
||||
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
|
||||
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@ const (
|
|||
)
|
||||
|
||||
type Config struct {
|
||||
FilePath string
|
||||
UseLocalTime bool
|
||||
FileMaxSizeInMB int
|
||||
FileMaxAgeInDays int
|
||||
FilePath string `koanf:"file_path"`
|
||||
UseLocalTime bool `koanf:"use_local_time"`
|
||||
FileMaxSizeInMB int `koanf:"file_max_size_in_mb"`
|
||||
FileMaxAgeInDays int `koanf:"file_max_age_in_days"`
|
||||
}
|
||||
|
||||
var l *slog.Logger
|
||||
|
|
|
|||
|
|
@ -1 +1,37 @@
|
|||
package patientapp
|
||||
|
||||
import (
|
||||
"git.gocasts.ir/ebhomengo/niki/patientapp/config"
|
||||
"git.gocasts.ir/ebhomengo/niki/patientapp/delivery/http/analytic"
|
||||
"git.gocasts.ir/ebhomengo/niki/repository/mysql"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type Application struct {
|
||||
//Config Config
|
||||
HTTPServer *config.EchoServer
|
||||
DB *mysql.DB
|
||||
}
|
||||
|
||||
func Setup(cfg config.Config, db *mysql.DB) Application {
|
||||
|
||||
e := echo.New()
|
||||
|
||||
server := config.EchoServer{
|
||||
Router: e,
|
||||
Config: cfg,
|
||||
}
|
||||
|
||||
return Application{
|
||||
//Config: config,
|
||||
HTTPServer: &server,
|
||||
DB: db,
|
||||
}
|
||||
}
|
||||
|
||||
func (a Application) Start() {
|
||||
|
||||
server := analytic.NewServer(a.HTTPServer, a.DB)
|
||||
|
||||
_ = server.Serve()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"git.gocasts.ir/ebhomengo/niki/patientapp"
|
||||
"git.gocasts.ir/ebhomengo/niki/patientapp/config"
|
||||
"git.gocasts.ir/ebhomengo/niki/repository/mysql"
|
||||
)
|
||||
|
||||
func main() {
|
||||
dbConf := mysql.Config{
|
||||
Username: os.Getenv("DB_USER"),
|
||||
Password: os.Getenv("DB_PASS"),
|
||||
Host: os.Getenv("DB_HOST"),
|
||||
DBName: os.Getenv("DB_NAME"),
|
||||
}
|
||||
port, _ := strconv.Atoi(os.Getenv("DB_PORT"))
|
||||
dbConf.Port = port
|
||||
|
||||
db := mysql.New(dbConf)
|
||||
|
||||
cfg := config.Config{
|
||||
Port: 8080,
|
||||
Cors: config.Cors{
|
||||
AllowOrigins: []string{"*"},
|
||||
},
|
||||
ShutDownCtxTimeout: 5 * time.Second,
|
||||
}
|
||||
|
||||
app := patientapp.Setup(cfg, db)
|
||||
|
||||
app.Start()
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Port int `koanf:"port"`
|
||||
Cors Cors `koanf:"cors"`
|
||||
ShutDownCtxTimeout time.Duration `koanf:"shutdown_context_timeout"`
|
||||
}
|
||||
|
||||
type Cors struct {
|
||||
AllowOrigins []string `koanf:"allow_origins"`
|
||||
}
|
||||
|
||||
type EchoServer struct {
|
||||
Router *echo.Echo
|
||||
Config Config
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package analytic
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
svc "git.gocasts.ir/ebhomengo/niki/patientapp/service/analytic"
|
||||
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
service svc.Service
|
||||
}
|
||||
|
||||
func NewHandler(service svc.Service) *Handler {
|
||||
return &Handler{
|
||||
service: service,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) Health(e echo.Context) error {
|
||||
return e.JSON(http.StatusOK, map[string]interface{}{"status": "ok"})
|
||||
}
|
||||
|
||||
func (h *Handler) PatientsAnalytic(e echo.Context) error {
|
||||
var req svc.ListPatientAnalyticRequest
|
||||
|
||||
richErr := richerror.New(richerror.Op("fetchingPatientList.PatientsAnalytic"))
|
||||
|
||||
if err := e.Bind(&req); err != nil {
|
||||
richErr = richErr.WithErr(err)
|
||||
richErr = richErr.WithKind(1)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, richErr)
|
||||
}
|
||||
|
||||
response, err := h.service.List(e.Request().Context(), req)
|
||||
if err != nil {
|
||||
richErr = richErr.WithErr(err)
|
||||
richErr = richErr.WithKind(4)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, richErr)
|
||||
}
|
||||
|
||||
return e.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
func (h *Handler) PatientsMapSummary(e echo.Context) error {
|
||||
richErr := richerror.New(richerror.Op("fetchingPatientMapSummary.PatientsMapSummary"))
|
||||
|
||||
var req svc.GetPatientMapSummaryRequest
|
||||
|
||||
if err := e.Bind(&req); err != nil {
|
||||
richErr = richErr.WithErr(err)
|
||||
richErr = richErr.WithKind(1)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, richErr)
|
||||
}
|
||||
|
||||
resp, svcErr := h.service.GetMapSummary(e.Request().Context(), req)
|
||||
if svcErr != nil {
|
||||
richErr = richErr.WithErr(svcErr)
|
||||
richErr = richErr.WithKind(4)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, richErr)
|
||||
}
|
||||
|
||||
return e.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package analytic
|
||||
|
||||
import (
|
||||
repo "git.gocasts.ir/ebhomengo/niki/patientapp/repository/mysql"
|
||||
analytic2 "git.gocasts.ir/ebhomengo/niki/patientapp/service/analytic"
|
||||
"git.gocasts.ir/ebhomengo/niki/repository/mysql"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func NewPatientAnalyticRouter(s *echo.Group, db *mysql.DB) {
|
||||
|
||||
mysqlRepo := repo.NewPatientRepo(db)
|
||||
|
||||
analyticService := analytic2.NewPatientAnalyticService(mysqlRepo)
|
||||
|
||||
h := NewHandler(analyticService)
|
||||
|
||||
s.GET("/patients", h.PatientsAnalytic)
|
||||
s.GET("/patients-summary", h.PatientsMapSummary)
|
||||
s.GET("/health", h.Health)
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package analytic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"git.gocasts.ir/ebhomengo/niki/patientapp/config"
|
||||
"git.gocasts.ir/ebhomengo/niki/repository/mysql"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
HTTPServer *config.EchoServer
|
||||
Db *mysql.DB
|
||||
}
|
||||
|
||||
func NewServer(server *config.EchoServer, db *mysql.DB) *Server {
|
||||
|
||||
return &Server{
|
||||
HTTPServer: server,
|
||||
Db: db,
|
||||
}
|
||||
}
|
||||
|
||||
func (s Server) Serve() error {
|
||||
s.RegisterRoutes()
|
||||
// Start server
|
||||
return s.HTTPServer.Router.Start(fmt.Sprintf(":%d", s.HTTPServer.Config.Port))
|
||||
}
|
||||
|
||||
func (s Server) Stop(ctx context.Context) error {
|
||||
return s.HTTPServer.Router.Shutdown(ctx)
|
||||
|
||||
}
|
||||
|
||||
func (s Server) RegisterRoutes() {
|
||||
|
||||
v1 := s.HTTPServer.Router.Group("/v1")
|
||||
{
|
||||
// Analytic Group
|
||||
analyticGroup := v1.Group("/analytic")
|
||||
NewPatientAnalyticRouter(analyticGroup, s.Db)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
version: "3.9"
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:8.0
|
||||
container_name: patient
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: rootpass
|
||||
MYSQL_DATABASE: patient
|
||||
MYSQL_USER: appuser
|
||||
MYSQL_PASSWORD: apppass
|
||||
TZ: Asia/Tehran
|
||||
command: [
|
||||
"--character-set-server=utf8mb4",
|
||||
"--collation-server=utf8mb4_unicode_ci",
|
||||
"--default-authentication-plugin=mysql_native_password"
|
||||
]
|
||||
ports:
|
||||
- "3306:3306"
|
||||
volumes:
|
||||
- mysql_data:/var/lib/mysql
|
||||
- ./repository/migration:/docker-entrypoint-initdb.d
|
||||
networks:
|
||||
- backend
|
||||
|
||||
volumes:
|
||||
mysql_data:
|
||||
|
||||
networks:
|
||||
backend:
|
||||
driver: bridge
|
||||
|
|
@ -0,0 +1,481 @@
|
|||
-- MySQL dump 10.13 Distrib 8.0.29, for Linux (x86_64)
|
||||
--
|
||||
-- Host: localhost Database: patient
|
||||
-- ------------------------------------------------------
|
||||
-- Server version 8.0.29
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!50503 SET NAMES utf8mb4 */;
|
||||
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||
|
||||
--
|
||||
-- Table structure for table `audits`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `audits`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `audits` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
`user_type` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`user_id` bigint unsigned DEFAULT NULL,
|
||||
`event` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`auditable_type` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`auditable_id` bigint unsigned NOT NULL,
|
||||
`old_values` text COLLATE utf8mb4_unicode_ci,
|
||||
`new_values` text COLLATE utf8mb4_unicode_ci,
|
||||
`url` text COLLATE utf8mb4_unicode_ci,
|
||||
`ip_address` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`user_agent` varchar(1023) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`tags` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `audits_auditable_type_auditable_id_index` (`auditable_type`,`auditable_id`),
|
||||
KEY `audits_user_id_user_type_index` (`user_id`,`user_type`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `bandage_user`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `bandage_user`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `bandage_user` (
|
||||
`user_id` bigint unsigned NOT NULL,
|
||||
`bandage_id` bigint unsigned NOT NULL,
|
||||
`qty` int unsigned NOT NULL DEFAULT '0',
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `bandages`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `bandages`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `bandages` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `cities`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `cities`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `cities` (
|
||||
`id` int unsigned NOT NULL AUTO_INCREMENT,
|
||||
`state_id` int unsigned NOT NULL,
|
||||
`name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=445 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `drug_user`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `drug_user`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `drug_user` (
|
||||
`user_id` bigint unsigned NOT NULL,
|
||||
`drug_id` bigint unsigned NOT NULL,
|
||||
`qty` int unsigned NOT NULL DEFAULT '0',
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `drugs`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `drugs`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `drugs` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `failed_jobs`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `failed_jobs`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `failed_jobs` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
`uuid` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`connection` text COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`queue` text COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `failed_jobs_uuid_unique` (`uuid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `logs`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `logs`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `logs` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int unsigned NOT NULL,
|
||||
`type` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`log` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=20739 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `migrations`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `migrations`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `migrations` (
|
||||
`id` int unsigned NOT NULL AUTO_INCREMENT,
|
||||
`migration` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`batch` int NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=47 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `model_has_permissions`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `model_has_permissions`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `model_has_permissions` (
|
||||
`permission_id` bigint unsigned NOT NULL,
|
||||
`model_type` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`model_id` bigint unsigned NOT NULL,
|
||||
PRIMARY KEY (`permission_id`,`model_id`,`model_type`),
|
||||
KEY `model_has_permissions_model_id_model_type_index` (`model_id`,`model_type`),
|
||||
CONSTRAINT `model_has_permissions_permission_id_foreign` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `model_has_roles`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `model_has_roles`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `model_has_roles` (
|
||||
`role_id` bigint unsigned NOT NULL,
|
||||
`model_type` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`model_id` bigint unsigned NOT NULL,
|
||||
PRIMARY KEY (`role_id`,`model_id`,`model_type`),
|
||||
KEY `model_has_roles_model_id_model_type_index` (`model_id`,`model_type`),
|
||||
CONSTRAINT `model_has_roles_role_id_foreign` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `password_resets`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `password_resets`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `password_resets` (
|
||||
`id` int unsigned NOT NULL AUTO_INCREMENT,
|
||||
`user_id` bigint DEFAULT NULL,
|
||||
`time` int DEFAULT NULL,
|
||||
`token` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=227 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `permissions`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `permissions`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `permissions` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`guard_name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `reports`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `reports`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `reports` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
`user_id` bigint unsigned NOT NULL,
|
||||
`agent_id` bigint unsigned NOT NULL,
|
||||
`type` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`interviewee` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`interviewee_name` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`interviewee_ratio` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`content` text COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `reports_user_id_foreign` (`user_id`),
|
||||
CONSTRAINT `reports_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=19846 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `role_has_permissions`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `role_has_permissions`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `role_has_permissions` (
|
||||
`permission_id` bigint unsigned NOT NULL,
|
||||
`role_id` bigint unsigned NOT NULL,
|
||||
PRIMARY KEY (`permission_id`,`role_id`),
|
||||
KEY `role_has_permissions_role_id_foreign` (`role_id`),
|
||||
CONSTRAINT `role_has_permissions_permission_id_foreign` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `role_has_permissions_role_id_foreign` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `roles`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `roles`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `roles` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`guard_name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `states`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `states`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `states` (
|
||||
`id` int unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `user_files`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `user_files`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `user_files` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
`user_id` bigint unsigned NOT NULL,
|
||||
`name` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`type` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`label` varchar(150) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`status` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`message` varchar(150) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=40633 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `user_metas`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `user_metas`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `user_metas` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
`user_id` bigint unsigned NOT NULL,
|
||||
`birthPlaceState` smallint unsigned NOT NULL DEFAULT '0',
|
||||
`birthPlaceCity` smallint unsigned NOT NULL DEFAULT '0',
|
||||
`religion` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`atba` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`nationality` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`atba_birthPlaceState` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`atba_birthPlaceCity` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`height` smallint unsigned DEFAULT NULL,
|
||||
`weight` smallint unsigned DEFAULT NULL,
|
||||
`eyeColor` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`bloodType` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`addressState` smallint unsigned NOT NULL,
|
||||
`addressCity` smallint unsigned NOT NULL,
|
||||
`address` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`postalCode` varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`phone` varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`mobile` varchar(30) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`fMobile` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`mMobile` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`ePhoneName` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`ePhoneNumber` varchar(30) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`houseType` varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`housePrice` varchar(30) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`houseMortgage` varchar(30) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`houseRent` varchar(30) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`fAlive` tinyint(1) DEFAULT NULL,
|
||||
`divorced` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`devorced` tinyint NOT NULL DEFAULT '0',
|
||||
`fName` varchar(90) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`fBirthDate` date DEFAULT NULL,
|
||||
`fNationalCode` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`fAtba` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`fNationality` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`fJob` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`fEdu` varchar(90) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`mAlive` tinyint(1) DEFAULT NULL,
|
||||
`mName` varchar(90) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`mBirthDate` date DEFAULT NULL,
|
||||
`mNationalCode` varchar(30) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`mAtba` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`mNationality` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`mJob` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`mEdu` varchar(90) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`ratio` tinyint(1) DEFAULT NULL,
|
||||
`ratioType` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`liveChild` tinyint unsigned NOT NULL,
|
||||
`ebChild` tinyint unsigned NOT NULL,
|
||||
`Edu` varchar(90) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`skills` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`insurance` tinyint(1) NOT NULL,
|
||||
`insuranceYear` tinyint unsigned DEFAULT NULL,
|
||||
`insuranceMonth` tinyint unsigned DEFAULT NULL,
|
||||
`insuranceCoverage` tinyint(1) NOT NULL,
|
||||
`insuranceCoverageNumber` varchar(30) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`insuranceCoveragBranch` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`insuranceCoverageType` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`supplementaryInsurance` tinyint(1) NOT NULL,
|
||||
`supplementaryInsuranceName` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`supportOrganization` tinyint(1) NOT NULL,
|
||||
`supportOrganizationName` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`dailyProblemType` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`description` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
`spouseName` varchar(90) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`spouseBirthDate` date DEFAULT NULL,
|
||||
`spouseNationalCode` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`spouseAtba` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`spouseNationality` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`spouseJob` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`spouseEdu` varchar(90) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`spouseMobile` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`spouseAlive` tinyint(1) DEFAULT NULL,
|
||||
`shenasname` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`shenasnamePlace` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `user_metas_user_id_foreign` (`user_id`),
|
||||
CONSTRAINT `user_metas_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1461 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `users`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `users`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `users` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`last_name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`username` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`password` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`pic` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`birth_date` date DEFAULT NULL,
|
||||
`gender` tinyint(1) NOT NULL,
|
||||
`marital` smallint unsigned DEFAULT NULL,
|
||||
`deceased` date DEFAULT NULL,
|
||||
`seyyed` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`profile_number` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`sheba` varchar(90) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`bank_account` varchar(90) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`bank_account_name` varchar(90) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`profile_status` tinyint unsigned DEFAULT NULL,
|
||||
`medical_information_status` tinyint unsigned DEFAULT NULL,
|
||||
`remember_token` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
`drug_description` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`bandage_description` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`bandage_period` tinyint unsigned DEFAULT NULL,
|
||||
`bandage_months` text COLLATE utf8mb4_unicode_ci,
|
||||
`twoFA_enabled` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`twoFA_secret` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`is_active` tinyint(1) NOT NULL DEFAULT '1',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `users_username_unique` (`username`),
|
||||
KEY `users_twofa_enabled_index` (`twoFA_enabled`),
|
||||
KEY `users_is_active_index` (`is_active`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1918 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||
|
||||
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||
|
||||
-- Dump completed on 2026-04-14 7:28:21
|
||||
|
|
@ -0,0 +1,386 @@
|
|||
package mysql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"git.gocasts.ir/ebhomengo/niki/patientapp/service/analytic"
|
||||
"git.gocasts.ir/ebhomengo/niki/patientapp/service/entity"
|
||||
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
|
||||
"git.gocasts.ir/ebhomengo/niki/repository/mysql"
|
||||
)
|
||||
|
||||
type DataBase struct {
|
||||
conn *mysql.DB
|
||||
}
|
||||
|
||||
func NewPatientRepo(db *mysql.DB) *DataBase {
|
||||
|
||||
return &DataBase{
|
||||
conn: db,
|
||||
}
|
||||
}
|
||||
|
||||
func (db *DataBase) GetPatients(ctx context.Context, f analytic.PatientFilter) ([]entity.UserMeta, error) {
|
||||
const Op = "repository.mysql.patient.get"
|
||||
|
||||
tx, err := db.conn.Conn().BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return []entity.UserMeta{}, richerror.New(Op).WithErr(err)
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
query := `
|
||||
SELECT
|
||||
um.id,
|
||||
um.user_id,
|
||||
u.birthDate,
|
||||
u.sex,
|
||||
um.birthPlaceState,
|
||||
um.birthPlaceCity,
|
||||
um.nationality,
|
||||
um.addressState,
|
||||
um.addressCity,
|
||||
um.address,
|
||||
um.phone,
|
||||
um.mobile,
|
||||
um.spouseName,
|
||||
um.created_at,
|
||||
um.updated_at
|
||||
FROM user_metas um
|
||||
JOIN users u ON u.id = um.user_id
|
||||
WHERE 1=1
|
||||
`
|
||||
|
||||
args := []any{}
|
||||
|
||||
// Birthdate filters (FROM = born after)
|
||||
if f.DOBFrom != nil && *f.DOBFrom != "" {
|
||||
query += " AND u.birth_date >= ?"
|
||||
args = append(args, *f.DOBFrom)
|
||||
}
|
||||
if f.DOBTo != nil && *f.DOBTo != "" {
|
||||
query += " AND u.birth_date <= ?"
|
||||
args = append(args, *f.DOBTo)
|
||||
}
|
||||
|
||||
// Sex
|
||||
if f.Sex != nil && *f.Sex != "" {
|
||||
query += " AND u.sex = ?"
|
||||
args = append(args, *f.Sex)
|
||||
}
|
||||
|
||||
// Nationality
|
||||
if f.Nationality != "" {
|
||||
query += " AND um.nationality = ?"
|
||||
args = append(args, f.Nationality)
|
||||
}
|
||||
|
||||
// Address
|
||||
if f.AddressState != 0 {
|
||||
query += " AND um.addressState = ?"
|
||||
args = append(args, f.AddressState)
|
||||
}
|
||||
if f.AddressCity != 0 {
|
||||
query += " AND um.addressCity = ?"
|
||||
args = append(args, f.AddressCity)
|
||||
}
|
||||
|
||||
// Search on fields from user_metas and users
|
||||
if f.Search != nil && *f.Search != "" {
|
||||
like := "%" + *f.Search + "%"
|
||||
query += `
|
||||
AND (
|
||||
um.fName LIKE ? OR
|
||||
um.mName LIKE ? OR
|
||||
um.spouseName LIKE ? OR
|
||||
um.phone LIKE ? OR
|
||||
um.mobile LIKE ?
|
||||
)
|
||||
`
|
||||
args = append(args, like, like, like, like, like)
|
||||
}
|
||||
|
||||
query += " ORDER BY id DESC"
|
||||
|
||||
if f.Limit > 0 {
|
||||
query += " LIMIT ?"
|
||||
args = append(args, f.Limit)
|
||||
}
|
||||
if f.Offset > 0 {
|
||||
query += " OFFSET ?"
|
||||
args = append(args, f.Offset)
|
||||
}
|
||||
|
||||
rows, err := tx.QueryContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
|
||||
return nil, richerror.New(Op).WithErr(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var result []entity.UserMeta
|
||||
|
||||
for rows.Next() {
|
||||
var u entity.UserMeta
|
||||
if err := rows.Scan(
|
||||
&u.ID, &u.UserID, &u.BirthDate, &u.Gender, &u.BirthPlaceState, &u.BirthPlaceCity,
|
||||
&u.Religion, &u.Nationality, &u.AddressState, &u.AddressCity,
|
||||
&u.Address, &u.Mobile, &u.SpouseName, &u.SpouseAlive,
|
||||
&u.CreatedAt, &u.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, richerror.New(Op).WithErr(err)
|
||||
}
|
||||
result = append(result, u)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, richerror.New(Op).WithErr(err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
||||
}
|
||||
|
||||
func (db *DataBase) CountPatients(ctx context.Context, f analytic.PatientFilter) (int, error) {
|
||||
const Op = "repository.mysql.patient.count"
|
||||
|
||||
tx, err := db.conn.Conn().BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return 0, richerror.New(Op).WithErr(err)
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
query := `
|
||||
SELECT COUNT(*)
|
||||
FROM user_metas um
|
||||
JOIN users u ON u.id = um.user_id
|
||||
WHERE 1=1
|
||||
`
|
||||
|
||||
args := []any{}
|
||||
|
||||
// Birthdate range
|
||||
if f.DOBFrom != nil && *f.DOBFrom != "" {
|
||||
query += " AND u.birth_date >= ?"
|
||||
args = append(args, *f.DOBFrom)
|
||||
}
|
||||
if f.DOBTo != nil && *f.DOBTo != "" {
|
||||
query += " AND u.birth_date <= ?"
|
||||
args = append(args, *f.DOBTo)
|
||||
}
|
||||
|
||||
// Sex
|
||||
if f.Sex != nil && *f.Sex != "" {
|
||||
query += " AND u.sex = ?"
|
||||
args = append(args, *f.Sex)
|
||||
}
|
||||
|
||||
// Nationality
|
||||
if f.Nationality != "" {
|
||||
query += " AND um.nationality = ?"
|
||||
args = append(args, f.Nationality)
|
||||
}
|
||||
|
||||
// Address
|
||||
if f.AddressState != 0 {
|
||||
query += " AND um.addressState = ?"
|
||||
args = append(args, f.AddressState)
|
||||
}
|
||||
if f.AddressCity != 0 {
|
||||
query += " AND um.addressCity = ?"
|
||||
args = append(args, f.AddressCity)
|
||||
}
|
||||
|
||||
// Search
|
||||
if f.Search != nil && *f.Search != "" {
|
||||
like := "%" + *f.Search + "%"
|
||||
query += `
|
||||
AND (
|
||||
um.fName LIKE ? OR
|
||||
um.mName LIKE ? OR
|
||||
um.spouseName LIKE ? OR
|
||||
um.phone LIKE ? OR
|
||||
um.mobile LIKE ?
|
||||
)
|
||||
`
|
||||
args = append(args, like, like, like, like, like)
|
||||
}
|
||||
|
||||
var count int
|
||||
|
||||
err = tx.QueryRowContext(ctx, query, args...).Scan(&count)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("%s: query error: %w", Op, err)
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (db *DataBase) SummaryByCity(ctx context.Context, provinceID uint, f analytic.PatientMapFilter) (map[uint][]entity.MapSummaryItem, error) {
|
||||
const Op = "repository.mysql.patient.map_summary"
|
||||
|
||||
tx, err := db.conn.Conn().BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, richerror.New(Op).WithErr(err)
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
query := `
|
||||
SELECT
|
||||
um.addressCity AS city_id,
|
||||
ANY_VALUE(um.lat) AS lat,
|
||||
ANY_VALUE(um.lng) AS lng,
|
||||
COUNT(*) AS user_count
|
||||
FROM user_metas um
|
||||
JOIN users u ON u.id = um.user_id
|
||||
JOIN cities c ON c.id = um.addressCity
|
||||
WHERE c.state_id = ?
|
||||
`
|
||||
|
||||
args := []any{provinceID}
|
||||
// Birthdate filters
|
||||
if f.MinDOB != nil && *f.MinDOB != "" {
|
||||
query += " AND u.birth_date >= ?"
|
||||
args = append(args, *f.MinDOB)
|
||||
}
|
||||
if f.MaxDOB != nil && *f.MaxDOB != "" {
|
||||
query += " AND u.birth_date <= ?"
|
||||
args = append(args, *f.MaxDOB)
|
||||
}
|
||||
|
||||
// Sex filter
|
||||
if f.Sex != nil && *f.Sex != "" {
|
||||
query += " AND u.sex = ?"
|
||||
args = append(args, *f.Sex)
|
||||
}
|
||||
|
||||
// Search filter
|
||||
if f.Search != nil && *f.Search != "" {
|
||||
like := "%" + *f.Search + "%"
|
||||
query += `
|
||||
AND (
|
||||
um.fName LIKE ? OR
|
||||
um.mName LIKE ? OR
|
||||
um.spouseName LIKE ? OR
|
||||
um.phone LIKE ? OR
|
||||
um.mobile LIKE ?
|
||||
)
|
||||
`
|
||||
args = append(args, like, like, like, like, like)
|
||||
}
|
||||
|
||||
// Group by city
|
||||
query += " GROUP BY um.addressCity"
|
||||
|
||||
rows, err := tx.QueryContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: query error: %w", Op, err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
result := make(map[uint][]entity.MapSummaryItem)
|
||||
|
||||
for rows.Next() {
|
||||
var item entity.MapSummaryItem
|
||||
|
||||
err := rows.Scan(
|
||||
&item.ID,
|
||||
&item.Latitude,
|
||||
&item.Longitude,
|
||||
&item.Count,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: scan error: %w", Op, err)
|
||||
}
|
||||
|
||||
result[item.ID] = append(result[item.ID], item)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, fmt.Errorf("%s: rows error: %w", Op, err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (db *DataBase) SummaryByProvince(ctx context.Context, f analytic.PatientMapFilter) (map[uint][]entity.MapSummaryItem, error) {
|
||||
|
||||
const Op = "repository.mysql.patient.summary_by_province"
|
||||
|
||||
tx, err := db.conn.Conn().BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, richerror.New(Op).WithErr(err)
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
query := `
|
||||
SELECT
|
||||
c.state_id,
|
||||
ANY_VALUE(c.lat) AS lat,
|
||||
ANY_VALUE(c.lng) AS lng,
|
||||
COUNT(*) AS total
|
||||
FROM user_metas um
|
||||
JOIN users u ON u.id = um.user_id
|
||||
JOIN cities c ON c.id = um.addressCity
|
||||
WHERE 1 = 1
|
||||
`
|
||||
args := []any{}
|
||||
|
||||
// Birthdate filters
|
||||
if f.MinDOB != nil && *f.MinDOB != "" {
|
||||
query += " AND u.birth_date >= ?"
|
||||
args = append(args, *f.MinDOB)
|
||||
}
|
||||
if f.MaxDOB != nil && *f.MaxDOB != "" {
|
||||
query += " AND u.birth_date <= ?"
|
||||
args = append(args, *f.MaxDOB)
|
||||
}
|
||||
|
||||
// Sex filter
|
||||
if f.Sex != nil && *f.Sex != "" {
|
||||
query += " AND u.sex = ?"
|
||||
args = append(args, *f.Sex)
|
||||
}
|
||||
|
||||
// Search filter
|
||||
if f.Search != nil && *f.Search != "" {
|
||||
like := "%" + *f.Search + "%"
|
||||
query += `
|
||||
AND (
|
||||
um.fName LIKE ? OR
|
||||
um.mName LIKE ? OR
|
||||
um.spouseName LIKE ? OR
|
||||
um.phone LIKE ? OR
|
||||
um.mobile LIKE ?
|
||||
)
|
||||
`
|
||||
args = append(args, like, like, like, like, like)
|
||||
}
|
||||
|
||||
query += " GROUP BY c.state_id"
|
||||
|
||||
rows, err := tx.QueryContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: query error: %w", Op, err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
result := make(map[uint][]entity.MapSummaryItem)
|
||||
|
||||
for rows.Next() {
|
||||
var item entity.MapSummaryItem
|
||||
|
||||
if err := rows.Scan(&item.ID, &item.Latitude, &item.Longitude, &item.Count); err != nil {
|
||||
return nil, fmt.Errorf("%s: scan error: %w", Op, err)
|
||||
}
|
||||
|
||||
result[item.ID] = []entity.MapSummaryItem{item}
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, fmt.Errorf("%s: rows error: %w", Op, err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package analytic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jalaali/go-jalaali"
|
||||
"time"
|
||||
)
|
||||
|
||||
func normalizeLimitOffset(limit, offset int) (int, int) {
|
||||
if limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
if limit > 100 {
|
||||
limit = 100
|
||||
}
|
||||
if offset < 0 {
|
||||
offset = 0
|
||||
}
|
||||
return limit, offset
|
||||
}
|
||||
|
||||
// convert age range -> DOB range
|
||||
func ageRangeToDOB(minAge, maxAge *int, now time.Time) (dobFrom, dobTo *string) {
|
||||
if maxAge != nil {
|
||||
t := now.AddDate(-(*maxAge + 1), 0, 1)
|
||||
jy, jm, jd, err := jalaali.ToJalaali(t.Year(), t.Month(), t.Day())
|
||||
if err != nil {
|
||||
}
|
||||
s := fmt.Sprintf("%04d/%02d/%02d", jy, jm, jd)
|
||||
dobFrom = &s
|
||||
}
|
||||
|
||||
if minAge != nil {
|
||||
t := now.AddDate(-*minAge, 0, 0)
|
||||
jy, jm, jd, err := jalaali.ToJalaali(t.Year(), t.Month(), t.Day())
|
||||
if err != nil {
|
||||
}
|
||||
s := fmt.Sprintf("%04d/%02d/%02d", jy, jm, jd)
|
||||
dobTo = &s
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
package analytic
|
||||
|
||||
import (
|
||||
"git.gocasts.ir/ebhomengo/niki/patientapp/service/entity"
|
||||
)
|
||||
|
||||
type ListPatientAnalyticRequest struct {
|
||||
// All fields are optional
|
||||
MinAge *int `query:"minAge,omitempty"`
|
||||
MaxAge *int `query:"maxAge,omitempty"`
|
||||
Sex *string `query:"sex,omitempty"`
|
||||
|
||||
City *uint16 `query:"city,omitempty"`
|
||||
Province *uint16 `query:"province,omitempty"`
|
||||
|
||||
Search *string `query:"search,omitempty"`
|
||||
|
||||
Pagination *Pagination `query:"pagination,omitempty"`
|
||||
}
|
||||
|
||||
type PatientAnalyticItem struct {
|
||||
ID uint64 `json:"id"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"Last_name"`
|
||||
DateOfBirth string `json:"dob,omitempty"`
|
||||
Sex string `json:"sex"`
|
||||
Phone string `json:"phone"`
|
||||
Address entity.Address `json:"address"`
|
||||
}
|
||||
type PatientAnalyticResponse struct {
|
||||
Items []PatientAnalyticItem `json:"items"`
|
||||
Pagination *Pagination `json:"pagination"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
func ToPatientResponse(patient entity.UserMeta) PatientAnalyticItem {
|
||||
return PatientAnalyticItem{
|
||||
ID: patient.ID,
|
||||
FirstName: patient.Name,
|
||||
LastName: patient.LastName,
|
||||
DateOfBirth: patient.BirthDate,
|
||||
Sex: patient.Gender,
|
||||
Phone: *patient.Mobile,
|
||||
Address: entity.Address{
|
||||
ProvinceID: patient.AddressState,
|
||||
CityID: patient.AddressCity,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GetPatientMapSummaryRequest =========================== Map ==================================
|
||||
type GetPatientMapSummaryRequest struct {
|
||||
Level entity.MapLevel `query:"level"`
|
||||
ParentID *int `query:"parentID"`
|
||||
|
||||
MinAge *int `query:"minAge,omitempty"`
|
||||
MaxAge *int `query:"maxAge,omitempty"`
|
||||
Sex *entity.Sex `query:"sex,omitempty"`
|
||||
Search *string `query:"search,omitempty"`
|
||||
}
|
||||
|
||||
type GetPatientMapSummaryResponse struct {
|
||||
Level entity.MapLevel `json:"level"`
|
||||
Items map[uint][]entity.MapSummaryItem `json:"items"`
|
||||
}
|
||||
|
||||
// Pagination ================================ Pagination =============================
|
||||
type Pagination struct {
|
||||
Limit int `query:"limit,omitempty"`
|
||||
Offset int `query:"offset,omitempty"`
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package analytic
|
||||
|
||||
import (
|
||||
"git.gocasts.ir/ebhomengo/niki/patientapp/service/entity"
|
||||
)
|
||||
|
||||
type PatientFilter struct {
|
||||
DOBFrom *string // born after
|
||||
DOBTo *string // born before
|
||||
Sex *string
|
||||
|
||||
Nationality string
|
||||
AddressState uint16
|
||||
AddressCity uint16
|
||||
|
||||
Search *string
|
||||
|
||||
Limit int
|
||||
Offset int
|
||||
}
|
||||
|
||||
type PatientMapFilter struct {
|
||||
MinDOB *string
|
||||
MaxDOB *string
|
||||
Sex *entity.Sex
|
||||
Search *string
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
package analytic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.gocasts.ir/ebhomengo/niki/patientapp/service/entity"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidProvinceID = errors.New("invalid province id")
|
||||
ErrInvalidCountryID = errors.New("invalid country id")
|
||||
ErrInvalidMapLevel = errors.New("invalid map level")
|
||||
)
|
||||
|
||||
type Repository interface {
|
||||
GetPatients(ctx context.Context, f PatientFilter) ([]entity.UserMeta, error)
|
||||
CountPatients(ctx context.Context, f PatientFilter) (int, error)
|
||||
|
||||
SummaryByCity(ctx context.Context, provinceID uint, f PatientMapFilter) (map[uint][]entity.MapSummaryItem, error)
|
||||
SummaryByProvince(ctx context.Context, f PatientMapFilter) (map[uint][]entity.MapSummaryItem, error)
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
repository Repository
|
||||
}
|
||||
|
||||
func NewPatientAnalyticService(repo Repository) Service {
|
||||
return Service{
|
||||
repository: repo,
|
||||
}
|
||||
}
|
||||
|
||||
func (s Service) List(ctx context.Context, req ListPatientAnalyticRequest) (PatientAnalyticResponse, error) {
|
||||
|
||||
limit, offset := normalizeLimitOffset(req.Pagination.Limit, req.Pagination.Offset)
|
||||
|
||||
// convert age range
|
||||
dobFrom, dobTo := ageRangeToDOB(req.MinAge, req.MaxAge, time.Now())
|
||||
|
||||
filter := PatientFilter{
|
||||
DOBFrom: dobFrom,
|
||||
DOBTo: dobTo,
|
||||
Sex: req.Sex,
|
||||
AddressCity: *req.City,
|
||||
AddressState: *req.Province,
|
||||
Search: req.Search,
|
||||
Limit: limit,
|
||||
Offset: offset,
|
||||
}
|
||||
|
||||
items, err := s.repository.GetPatients(ctx, filter)
|
||||
if err != nil {
|
||||
return PatientAnalyticResponse{}, fmt.Errorf("GetPatients: %w", err)
|
||||
}
|
||||
|
||||
total, err := s.repository.CountPatients(ctx, filter)
|
||||
if err != nil {
|
||||
return PatientAnalyticResponse{}, fmt.Errorf("CountPatients: %w", err)
|
||||
}
|
||||
|
||||
// mapping response
|
||||
out := make([]PatientAnalyticItem, 0, len(items))
|
||||
for _, value := range items {
|
||||
out = append(out, ToPatientResponse(value))
|
||||
}
|
||||
|
||||
return PatientAnalyticResponse{
|
||||
Items: out,
|
||||
Pagination: &Pagination{
|
||||
Limit: limit,
|
||||
Offset: offset,
|
||||
},
|
||||
Total: total,
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func (s Service) GetMapSummary(ctx context.Context, req GetPatientMapSummaryRequest) (GetPatientMapSummaryResponse, error) {
|
||||
|
||||
dobFrom, dobTo := ageRangeToDOB(req.MinAge, req.MaxAge, time.Now())
|
||||
|
||||
filter := PatientMapFilter{
|
||||
MinDOB: dobFrom,
|
||||
MaxDOB: dobTo,
|
||||
Sex: req.Sex,
|
||||
Search: req.Search,
|
||||
}
|
||||
|
||||
switch req.Level {
|
||||
case entity.MapLevelCity:
|
||||
if req.ParentID == nil || *req.ParentID <= 0 {
|
||||
return GetPatientMapSummaryResponse{}, ErrInvalidProvinceID
|
||||
}
|
||||
|
||||
items, err := s.repository.SummaryByCity(ctx, uint(*req.ParentID), filter)
|
||||
if err != nil {
|
||||
return GetPatientMapSummaryResponse{}, fmt.Errorf("SummaryByCity: %w", err)
|
||||
}
|
||||
return GetPatientMapSummaryResponse{Level: req.Level, Items: items}, nil
|
||||
|
||||
case entity.MapLevelProvince:
|
||||
if req.ParentID == nil || *req.ParentID <= 0 {
|
||||
return GetPatientMapSummaryResponse{}, ErrInvalidCountryID
|
||||
}
|
||||
|
||||
items, err := s.repository.SummaryByProvince(ctx, filter)
|
||||
if err != nil {
|
||||
return GetPatientMapSummaryResponse{}, fmt.Errorf("SummaryByProvince: %w", err)
|
||||
}
|
||||
return GetPatientMapSummaryResponse{Level: req.Level, Items: items}, nil
|
||||
|
||||
default:
|
||||
return GetPatientMapSummaryResponse{}, ErrInvalidMapLevel
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package entity
|
||||
|
||||
type Address struct {
|
||||
ID uint
|
||||
PostalCode string
|
||||
Address string
|
||||
Name string
|
||||
Lat float64
|
||||
Lon float64
|
||||
CityID uint16
|
||||
ProvinceID uint16
|
||||
}
|
||||
|
||||
type AddressAggregated struct {
|
||||
Address Address
|
||||
Province Province
|
||||
City City
|
||||
}
|
||||
|
||||
type Province struct {
|
||||
ID uint
|
||||
Name string
|
||||
}
|
||||
type City struct {
|
||||
ID uint
|
||||
Name string
|
||||
ProvinceID uint
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package entity
|
||||
|
||||
type MapLevel string
|
||||
|
||||
const (
|
||||
MapLevelCity MapLevel = "city"
|
||||
MapLevelProvince MapLevel = "province"
|
||||
MapLevelCountry MapLevel = "country"
|
||||
)
|
||||
|
||||
type MapSummaryItem struct {
|
||||
ID uint `json:"id"` // city_id OR state_id
|
||||
Latitude float64 `json:"latitude"`
|
||||
Longitude float64 `json:"longitude"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
package entity
|
||||
|
||||
import "time"
|
||||
|
||||
type Patient struct {
|
||||
ID int64
|
||||
FirstName string
|
||||
LastName string
|
||||
DateOfBirth string
|
||||
Sex Sex
|
||||
Phone string
|
||||
Address Address
|
||||
CaseStatus CaseStatus
|
||||
ReferralSource ReferralSource
|
||||
AssignedStaffId int64
|
||||
StartDate string
|
||||
EndDate string
|
||||
}
|
||||
|
||||
type UserMeta struct {
|
||||
ID uint64 `db:"id"`
|
||||
UserID uint64 `db:"user_id"`
|
||||
Name string `db:"name"`
|
||||
LastName string `db:"last_name"`
|
||||
Gender string `db:"gender"`
|
||||
BirthDate string `db:"birth_date"`
|
||||
BirthPlaceState uint16 `db:"birthPlaceState"`
|
||||
BirthPlaceCity uint16 `db:"birthPlaceCity"`
|
||||
Religion *string `db:"religion"`
|
||||
Nationality *string `db:"nationality"`
|
||||
AddressState uint16 `db:"addressState"`
|
||||
AddressCity uint16 `db:"addressCity"`
|
||||
Address string `db:"address"`
|
||||
Mobile *string `db:"mobile"`
|
||||
SpouseName *string `db:"spouseName"`
|
||||
SpouseAlive *bool `db:"spouseAlive"`
|
||||
CreatedAt *time.Time `db:"created_at"`
|
||||
UpdatedAt *time.Time `db:"updated_at"`
|
||||
}
|
||||
|
||||
// Sex ================================== Sex type ==========================================
|
||||
type Sex string
|
||||
|
||||
const (
|
||||
SexUnknown Sex = "unknown"
|
||||
SexMale Sex = "male"
|
||||
SexFemale Sex = "female"
|
||||
SexOther Sex = "other"
|
||||
)
|
||||
|
||||
func (s Sex) SexValidation() bool {
|
||||
switch s {
|
||||
case SexUnknown, SexMale, SexFemale, SexOther:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// CaseStatus =================================== Case Status =======================================
|
||||
type CaseStatus string
|
||||
|
||||
const (
|
||||
Open CaseStatus = "open"
|
||||
Close CaseStatus = "close"
|
||||
InProgress CaseStatus = "inProgress"
|
||||
)
|
||||
|
||||
func (s CaseStatus) CaseStatusValidation() bool {
|
||||
switch s {
|
||||
case Open, Close, InProgress:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// ReferralSource =================================== Referral source =======================================
|
||||
type ReferralSource string
|
||||
|
||||
const (
|
||||
Hospital ReferralSource = "hospital"
|
||||
Community ReferralSource = "community"
|
||||
Other ReferralSource = "other"
|
||||
)
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package cfgloader
|
||||
|
||||
import (
|
||||
"github.com/knadh/koanf"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/knadh/koanf/parsers/yaml"
|
||||
"github.com/knadh/koanf/providers/env"
|
||||
"github.com/knadh/koanf/providers/file"
|
||||
//"github.com/knadh/koanf/v2"
|
||||
)
|
||||
|
||||
type Option struct {
|
||||
Prefix string
|
||||
Delimiter string
|
||||
Separator string
|
||||
YamlFilePath string
|
||||
CallbackEnv func(string) string
|
||||
}
|
||||
|
||||
// defaultCallbackEnv processes environment variable keys based on provided prefix and separator
|
||||
func defaultCallbackEnv(source, prefix, separator string) string {
|
||||
base := strings.ToLower(strings.TrimPrefix(source, prefix))
|
||||
return strings.ReplaceAll(base, separator, ".")
|
||||
}
|
||||
|
||||
// Load function loads configuration from YAML file and environment variables based on provided options
|
||||
func Load(options Option, config interface{}) error {
|
||||
k := koanf.New(options.Delimiter)
|
||||
|
||||
// Load configuration from YAML file if provided
|
||||
if options.YamlFilePath != "" {
|
||||
if err := k.Load(file.Provider(options.YamlFilePath), yaml.Parser()); err != nil {
|
||||
log.Fatalf("Error loading config file: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Define callback function for environment variables
|
||||
callback := options.CallbackEnv
|
||||
if callback == nil {
|
||||
// Set default callback using the prefix and separator from options
|
||||
callback = func(source string) string {
|
||||
return defaultCallbackEnv(source, options.Prefix, options.Separator)
|
||||
}
|
||||
}
|
||||
|
||||
// Load environment variables with the specified prefix and callback
|
||||
if err := k.Load(env.Provider(options.Prefix, options.Delimiter, callback), nil); err != nil {
|
||||
log.Fatalf("Error loading environment variables: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Unmarshal into provided config structure (passing address)
|
||||
if err := k.Unmarshal("", &config); err != nil {
|
||||
log.Fatalf("Error unmarshalling config: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
package migrator
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"git.gocasts.ir/ebhomengo/niki/pkg/database/mysql"
|
||||
migrate "github.com/rubenv/sql-migrate"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
MysqlConfig mysql.Config
|
||||
MigrationPath string
|
||||
MigrationDBName string
|
||||
}
|
||||
|
||||
type Migrator struct {
|
||||
cfg Config
|
||||
dialect string
|
||||
migrations *migrate.FileMigrationSource
|
||||
}
|
||||
|
||||
// TODO - set migration table name
|
||||
// TODO - add limit to Up and Down method
|
||||
|
||||
func New(cfg Config) Migrator {
|
||||
// OR: Read migrations from a folder:
|
||||
migrations := &migrate.FileMigrationSource{
|
||||
Dir: cfg.MigrationPath,
|
||||
}
|
||||
|
||||
return Migrator{cfg: cfg, dialect: "mysql", migrations: migrations}
|
||||
}
|
||||
|
||||
func (m Migrator) Up() {
|
||||
migrate.SetTable(m.cfg.MigrationDBName)
|
||||
|
||||
db, err := sql.Open(m.dialect, fmt.Sprintf("%s:%s@(%s:%d)/%s?parseTime=true",
|
||||
m.cfg.MysqlConfig.Username, m.cfg.MysqlConfig.Password, m.cfg.MysqlConfig.Host, m.cfg.MysqlConfig.Port, m.cfg.MysqlConfig.DBName))
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("can't open mysql db: %w", err))
|
||||
}
|
||||
|
||||
n, err := migrate.Exec(db, m.dialect, m.migrations, migrate.Up)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("can't apply migrations: %w", err))
|
||||
}
|
||||
fmt.Printf("Applied %d migrations!\n", n)
|
||||
}
|
||||
|
||||
func (m Migrator) Down() {
|
||||
migrate.SetTable(m.cfg.MigrationDBName)
|
||||
|
||||
db, err := sql.Open(m.dialect, fmt.Sprintf("%s:%s@(%s:%d)/%s?parseTime=true",
|
||||
m.cfg.MysqlConfig.Username, m.cfg.MysqlConfig.Password, m.cfg.MysqlConfig.Host, m.cfg.MysqlConfig.Port, m.cfg.MysqlConfig.DBName))
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("can't open mysql db: %w", err))
|
||||
}
|
||||
|
||||
n, err := migrate.Exec(db, m.dialect, m.migrations, migrate.Down)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("can't rollback migrations: %w", err))
|
||||
}
|
||||
fmt.Printf("Rollbacked %d migrations!\n", n)
|
||||
}
|
||||
|
||||
func (m Migrator) Status() {
|
||||
// TODO - add status
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
package mysql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
querier "git.gocasts.ir/ebhomengo/niki/pkg/query_transaction/sql"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Username string `koanf:"username"`
|
||||
Password string `koanf:"password"`
|
||||
Port int `koanf:"port"`
|
||||
Host string `koanf:"host"`
|
||||
DBName string `koanf:"db_name"`
|
||||
}
|
||||
|
||||
type DB struct {
|
||||
config Config
|
||||
db *querier.SQLDB
|
||||
mu sync.Mutex
|
||||
statements map[statementKey]*sql.Stmt
|
||||
}
|
||||
|
||||
func (db *DB) Conn() *querier.SQLDB {
|
||||
return db.db
|
||||
}
|
||||
|
||||
// TODO: this temporary to ignore linter error (magic number).
|
||||
const (
|
||||
dbMaxConnLifetime = time.Minute * 3
|
||||
dbMaxOpenConns = 10
|
||||
dbMaxIdleConns = 10
|
||||
)
|
||||
|
||||
func New(config Config) *DB {
|
||||
// parseTime=true changes the output type of DATE and DATETIME values to time.Time
|
||||
// instead of []byte / string
|
||||
// The date or datetime like 0000-00-00 00:00:00 is converted into zero value of time.Time
|
||||
db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@(%s:%d)/%s?parseTime=true",
|
||||
config.Username, config.Password, config.Host, config.Port, config.DBName))
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("can't open mysql db: %w", err))
|
||||
}
|
||||
|
||||
// See "Important settings" section.
|
||||
db.SetConnMaxLifetime(dbMaxConnLifetime)
|
||||
db.SetMaxOpenConns(dbMaxOpenConns)
|
||||
db.SetMaxIdleConns(dbMaxIdleConns)
|
||||
|
||||
return &DB{
|
||||
config: config,
|
||||
db: &querier.SQLDB{DB: db},
|
||||
statements: make(map[statementKey]*sql.Stmt),
|
||||
}
|
||||
}
|
||||
|
||||
func (db *DB) PrepareStatement(ctx context.Context, key statementKey, query string) (*sql.Stmt, error) {
|
||||
db.mu.Lock()
|
||||
defer db.mu.Unlock()
|
||||
|
||||
if stmt, ok := db.statements[key]; ok {
|
||||
return stmt, nil
|
||||
}
|
||||
|
||||
stmt, err := db.db.PrepareContext(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
db.statements[key] = stmt
|
||||
|
||||
return stmt, nil
|
||||
}
|
||||
|
||||
func (db *DB) CloseStatements() error {
|
||||
db.mu.Lock()
|
||||
defer db.mu.Unlock()
|
||||
|
||||
for _, stmt := range db.statements {
|
||||
err := stmt.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
production:
|
||||
dialect: mysql
|
||||
datasource: niki:nikiappt0lk2o20@(localhost:3306)/niki_db?parseTime=true
|
||||
dir: pkg/database/mysql/migration
|
||||
table: gorp_migrations
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package mysql
|
||||
|
||||
type statementKey uint
|
||||
|
||||
const (
|
||||
StatementKeyAddressCreateForBenefactor statementKey = iota + 1
|
||||
StatementKeyAddressDeleteForBenefactor
|
||||
StatementKeyAddressIsExistByID
|
||||
StatementKeyAddressGetByID
|
||||
StatementKeyAddressGetAllByBenefactorID
|
||||
StatementKeyAddressUpdate
|
||||
StatementKeyCityGetProvinceIDByID
|
||||
StatementKeyCityIsExistByID
|
||||
StatementKeyCityGetAll
|
||||
StatementKeyProvinceIsExistByID
|
||||
StatementKeyProvinceGetAll
|
||||
StatementKeyAdminAccessControlGetPermissions
|
||||
StatementKeyAdminAdd
|
||||
StatementKeyAdminExistByEmail
|
||||
StatementKeyAdminExistByPhoneNumber
|
||||
StatementKeyAdminAgentExistByID
|
||||
StatementKeyAdminGetByID
|
||||
StatementKeyAdminGetByPhoneNumber
|
||||
StatementKeyAdminAgentGetAll
|
||||
StatementKeyBenefactorGetByID
|
||||
StatementKeyBenefactorGetByIDs
|
||||
StatementKeyBenefactorGetByPhoneNumber
|
||||
StatementKeyBenefactorCreate
|
||||
StatementKeyBenefactorGetAll
|
||||
StatementKeyKindBoxAdd
|
||||
StatementKeyKindBoxAssignReceiverAgent
|
||||
StatementKeyKindBoxEnumerate
|
||||
StatementKeyKindBoxExistForBenefactor
|
||||
StatementKeyKindBoxGetByID
|
||||
StatementKeyKindBoxGetAwaitingReturnByAgent
|
||||
StatementKeyKindBoxRegisterEmptyingRequest
|
||||
StatementKeyKindBoxReturn
|
||||
StatementKeyKindBoxReqAccept
|
||||
StatementKeyKindBoxReqAdd
|
||||
StatementKeyKindBoxReqAssignSenderAgent
|
||||
StatementKeyKindBoxReqDeleteByID
|
||||
StatementKeyKindBoxReqGetByID
|
||||
StatementKeyKindBoxReqGetAwaitingDeliveryByAgent
|
||||
StatementKeyKindBoxReqRollbackToPendingStatus
|
||||
StatementKeyKindBoxReqReject
|
||||
StatementKeyKindBoxReqUpdate
|
||||
StatementKeyKindBoxUpdate
|
||||
StatementKeyReferTimeGetByID
|
||||
StatementKeyReferTimeGetAll
|
||||
StatementKeyBenefactorUpdate
|
||||
StatementKeyBenefactorUpdateStatus
|
||||
)
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package mysql
|
||||
|
||||
type Scanner interface {
|
||||
Scan(dest ...any) error
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package redisotp
|
||||
|
||||
import "git.gocasts.ir/ebhomengo/niki/adapter/redis"
|
||||
|
||||
type DB struct {
|
||||
adapter *redis.Adapter
|
||||
}
|
||||
|
||||
func New(adapter *redis.Adapter) *DB {
|
||||
return &DB{adapter: adapter}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package date_parser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ParseDate parses a date string in "YYYY-MM-DD" format and returns a time.Time object
|
||||
func ParseDate(input string) (time.Time, error) {
|
||||
const layout = "2006-01-02"
|
||||
|
||||
// Parse the input string
|
||||
convertedDate, err := time.Parse(layout, input)
|
||||
if err != nil {
|
||||
return time.Time{}, fmt.Errorf("invalid date format: %v", err)
|
||||
}
|
||||
|
||||
return convertedDate, nil
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue