diff --git a/cmd/purchaseapp/main.go b/cmd/purchaseapp/main.go index fdd06b0b..ad0283df 100644 --- a/cmd/purchaseapp/main.go +++ b/cmd/purchaseapp/main.go @@ -3,8 +3,8 @@ package main import ( "flag" "fmt" + purchaseMysql "git.gocasts.ir/ebhomengo/niki/domain/purchase/repository/mysql" "git.gocasts.ir/ebhomengo/niki/purchaseapp/delivery/http" - purchaseMysql "git.gocasts.ir/ebhomengo/niki/purchaseapp/repository/mysql" "git.gocasts.ir/ebhomengo/niki/purchaseapp/service/order" "git.gocasts.ir/ebhomengo/niki/repository/migrator" "git.gocasts.ir/ebhomengo/niki/repository/mysql" diff --git a/domain/order/entity/order.go b/domain/order/entity/order.go new file mode 100644 index 00000000..89f5499e --- /dev/null +++ b/domain/order/entity/order.go @@ -0,0 +1,49 @@ +package entity + +import ( + "git.gocasts.ir/ebhomengo/niki/types" + "time" +) + +type Order struct { + ID types.ID + UserID types.ID + TotalAmount types.Price + TotalDiscount types.Price + ShippingID types.ID + PaymentMethod PaymentMethod + ProcessStatus ProcessStatus + PaymentStatus PaymentStatus + AddressID types.ID + CreatedAt time.Time + UpdatedAt time.Time +} + +type PaymentMethod string + +const ( + Online PaymentMethod = "online" + Wallet = "wallet" + Cart = "cart" +) + +type ProcessStatus string + +const ( + WaitingToPay ProcessStatus = "waiting-to-pay" + Processing = "processing" + Accepted = "accepted" + Preparing = "preparing" + Prepared = "prepared" + GivenToPost = "given-to-post" + Delivered = "delivered" + Cancelled = "cancelled" + SystemCancellation = "system-cancellation" +) + +type PaymentStatus string + +const ( + Paid PaymentStatus = "paid" + UnPaid = "unpaid" +) diff --git a/domain/order/entity/order_item.go b/domain/order/entity/order_item.go new file mode 100644 index 00000000..0a9db823 --- /dev/null +++ b/domain/order/entity/order_item.go @@ -0,0 +1,16 @@ +package entity + +import ( + "git.gocasts.ir/ebhomengo/niki/types" + "time" +) + +type OrderItem struct { + ID types.ID + ProductID types.ID + Price types.Price + Quantity types.Count + PriceWithDiscount types.Price + OrderID types.ID + CreatedAt time.Time +} diff --git a/domain/order/entity/shipping.go b/domain/order/entity/shipping.go new file mode 100644 index 00000000..53d518ca --- /dev/null +++ b/domain/order/entity/shipping.go @@ -0,0 +1,10 @@ +package entity + +import "git.gocasts.ir/ebhomengo/niki/types" + +type Shipping struct { + ID types.ID + Name string + Price types.Price + IsActive bool +} diff --git a/purchaseapp/repository/migrations/2026010411120_create_orders_table.sql b/domain/order/repository/migrations/2026010411120_create_orders_table.sql similarity index 84% rename from purchaseapp/repository/migrations/2026010411120_create_orders_table.sql rename to domain/order/repository/migrations/2026010411120_create_orders_table.sql index 2fe04c92..55ff9490 100644 --- a/purchaseapp/repository/migrations/2026010411120_create_orders_table.sql +++ b/domain/order/repository/migrations/2026010411120_create_orders_table.sql @@ -3,11 +3,11 @@ -- https://www.grouparoo.com/blog/varchar-191#why-varchar-and-not-text CREATE TABLE `orders` ( `id` INT PRIMARY KEY AUTO_INCREMENT, - `user_id` INT NOT NULL, - `address` TEXT, + `user_id` INT, + `address_id` INT, `shipping_id` INT NOT NULL, `payment_method` ENUM('online', 'wallet', 'cart') DEFAULT 'online', - `payment_status` ENUM('unpaid', 'paid', 'cancelled') DEFAULT 'unpaid', + `payment_status` ENUM('unpaid', 'paid') DEFAULT 'unpaid', `process_status` ENUM('waiting-to-pay', 'processing', 'accepted', 'preparing', 'prepared', 'given-to-post', 'delivered', 'cancelled') DEFAULT 'waiting-to-pay', `total_amount` INT NOT NULL, `total_discount` INT NULL, @@ -15,7 +15,7 @@ CREATE TABLE `orders` ( `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -- FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) --- FOREIGN KEY (`shipping_id`) REFERENCES `shippings`(`id`) + FOREIGN KEY (`shipping_id`) REFERENCES `shippings`(`id`) ); diff --git a/purchaseapp/repository/migrations/20260104_11121_create_order_items_table.sql b/domain/order/repository/migrations/20260104_11121_create_order_items_table.sql similarity index 100% rename from purchaseapp/repository/migrations/20260104_11121_create_order_items_table.sql rename to domain/order/repository/migrations/20260104_11121_create_order_items_table.sql diff --git a/domain/order/repository/migrations/20261104_11122_create_shippings_table.sql b/domain/order/repository/migrations/20261104_11122_create_shippings_table.sql new file mode 100644 index 00000000..4a1375c0 --- /dev/null +++ b/domain/order/repository/migrations/20261104_11122_create_shippings_table.sql @@ -0,0 +1,16 @@ +-- +migrate Up +-- please read this article to understand why we use VARCHAR(191) +-- https://www.grouparoo.com/blog/varchar-191#why-varchar-and-not-text +CREATE TABLE `orders` ( + `id` INT PRIMARY KEY AUTO_INCREMENT, + `name` VARCHAR (191), + `price` INT NOT NULL , + `is_active` INT NOT NULL DEFAULT 1, + + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP + +); + +-- +migrate Down +DROP TABLE `orders`; diff --git a/domain/order/repository/mysql/db.go b/domain/order/repository/mysql/db.go new file mode 100644 index 00000000..0082acb0 --- /dev/null +++ b/domain/order/repository/mysql/db.go @@ -0,0 +1,11 @@ +package mysql + +import "git.gocasts.ir/ebhomengo/niki/repository/mysql" + +type DB struct { + conn *mysql.DB +} + +func New(db *mysql.DB) *DB { + return &DB{conn: db} +} diff --git a/purchaseapp/repository/mysql/order.go b/domain/order/repository/mysql/order.go similarity index 62% rename from purchaseapp/repository/mysql/order.go rename to domain/order/repository/mysql/order.go index 279aa351..748b6be0 100644 --- a/purchaseapp/repository/mysql/order.go +++ b/domain/order/repository/mysql/order.go @@ -1,23 +1,14 @@ package mysql import ( + entity "git.gocasts.ir/ebhomengo/niki/domain/order/entity" richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error" - "git.gocasts.ir/ebhomengo/niki/purchaseapp/entity" - "git.gocasts.ir/ebhomengo/niki/repository/mysql" "git.gocasts.ir/ebhomengo/niki/types" ) -type DB struct { - conn *mysql.DB -} - -func New(db *mysql.DB) *DB { - return &DB{conn: db} -} - func (d *DB) CreateOrder(order entity.Order, orderItems []entity.OrderItem) (types.ID, error) { - const Op = "repository.mysql.order.createorder" + const Op = "domain.repository.mysql.order.create-order" tx, err := d.conn.Conn().Begin() if err != nil { @@ -26,10 +17,10 @@ func (d *DB) CreateOrder(order entity.Order, orderItems []entity.OrderItem) (typ defer tx.Rollback() - query := "insert into orders(user_id, address, shipping_id," + + query := "insert into orders(user_id, address_id, shipping_id," + " payment_method, payment_status, process_status," + " total_amount, total_discount) values (?, ?, ?, ?, ?, ?, ?, ?);" - res, oErr := tx.Exec(query, order.UserID, order.Address, order.ShippingID, + res, oErr := tx.Exec(query, order.UserID, order.AddressID, order.ShippingID, order.PaymentMethod, order.PaymentStatus, order.ProcessStatus, order.TotalAmount, order.TotalDiscount) @@ -57,7 +48,7 @@ func (d *DB) CreateOrder(order entity.Order, orderItems []entity.OrderItem) (typ } func (d *DB) UpdateOrderProcessStatus(orderID types.ID, status string) (bool, error) { - const Op = "repository.mysql.order.update-order-process-status" + const Op = "domain.repository.mysql.order.update-order-process-status" _, err := d.conn.Conn().Exec("update orders set process_status=? where id=?;", status, orderID) if err != nil { @@ -67,3 +58,35 @@ func (d *DB) UpdateOrderProcessStatus(orderID types.ID, status string) (bool, er return true, nil } + +func (d *DB) GetShipping() ([]entity.Shipping, error) { + const Op = "domain.repository.mysql.order.get-shipping" + rows, err := d.conn.Conn().Query("select * from shippings where is_active=1") + + if err != nil { + return []entity.Shipping{}, richerror.New(Op).WithErr(err) + } + + defer rows.Close() + + var shippings []entity.Shipping + + for rows.Next() { + var s entity.Shipping + + err := rows.Scan( + &s.ID, + &s.Name, + &s.Price, + &s.IsActive, + ) + if err != nil { + return nil, richerror.New(Op).WithErr(err) + } + + shippings = append(shippings, s) + } + + return shippings, nil + +} diff --git a/purchaseapp/service/order/service.go b/domain/order/service/order.go similarity index 52% rename from purchaseapp/service/order/service.go rename to domain/order/service/order.go index 21690140..40255d49 100644 --- a/purchaseapp/service/order/service.go +++ b/domain/order/service/order.go @@ -1,9 +1,9 @@ -package order +package service import ( + entity "git.gocasts.ir/ebhomengo/niki/domain/order/entity" richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error" - "git.gocasts.ir/ebhomengo/niki/purchaseapp/entity" - "git.gocasts.ir/ebhomengo/niki/types" + types "git.gocasts.ir/ebhomengo/niki/types" ) type Service struct { @@ -13,26 +13,27 @@ type Service struct { type Repo interface { CreateOrder(order entity.Order, orderItems []entity.OrderItem) (types.ID, error) UpdateOrderProcessStatus(orderID types.ID, status string) (bool, error) + GetShipping() ([]entity.Shipping, error) } func New(orderRepo Repo) Service { return Service{repo: orderRepo} } -func (s Service) CreateOrder(order entity.Order, orderItems []entity.OrderItem) (CreateOrderResponse, error) { - const Op = "purchaseapp.service.CreateOrder" +func (s *Service) CreateOrder(order entity.Order, orderItems []entity.OrderItem) (types.ID, error) { + const Op = "domain.order.service.order.CreateOrder" orderID, err := s.repo.CreateOrder(order, orderItems) if err != nil { - return CreateOrderResponse{}, richerror.New(Op).WithErr(err) + return 0, richerror.New(Op).WithErr(err) } - return CreateOrderResponse{OrderID: orderID}, nil + return orderID, nil } -func (s Service) UpdateOrderProcessStatus(orderID types.ID, status string) (bool, error) { +func (s *Service) UpdateOrderProcessStatus(orderID types.ID, status string) (bool, error) { - const Op = "purchaseapp.service.UpdateOrderProcessStatus" + const Op = "domain.order.service.order.UpdateOrderProcessStatus" _, err := s.repo.UpdateOrderProcessStatus(orderID, status) if err != nil { return false, richerror.New(Op).WithErr(err) diff --git a/domain/order/service/shipping.go b/domain/order/service/shipping.go new file mode 100644 index 00000000..3d1f6dae --- /dev/null +++ b/domain/order/service/shipping.go @@ -0,0 +1,16 @@ +package service + +import ( + "git.gocasts.ir/ebhomengo/niki/domain/order/entity" + richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error" +) + +func (s *Service) GetShipping() ([]entity.Shipping, error) { + const Op = "domain.order.service.shipping.get-shipping" + shippings, err := s.repo.GetShipping() + if err != nil { + return []entity.Shipping{}, richerror.New(Op) + } + + return shippings, nil +} diff --git a/patientapp/app.go b/patientapp/app.go index dce0558e..c2450e4c 100644 --- a/patientapp/app.go +++ b/patientapp/app.go @@ -3,17 +3,17 @@ package patientapp import ( "git.gocasts.ir/ebhomengo/niki/patientapp/config" "git.gocasts.ir/ebhomengo/niki/patientapp/delivery/http/analytic" - "git.gocasts.ir/ebhomengo/niki/patientapp/repository/mysql" + "git.gocasts.ir/ebhomengo/niki/repository/mysql" "github.com/labstack/echo/v4" ) type Application struct { //Config Config HTTPServer *config.EchoServer - DB *mysql.DataBase + DB *mysql.DB } -func Setup(cfg config.Config, conn *mysql.DataBase) Application { +func Setup(cfg config.Config, db *mysql.DB) Application { e := echo.New() @@ -25,13 +25,13 @@ func Setup(cfg config.Config, conn *mysql.DataBase) Application { return Application{ //Config: config, HTTPServer: &server, - DB: conn, + DB: db, } } func (a Application) Start() { - server := analytic.NewServer(a.HTTPServer) + server := analytic.NewServer(a.HTTPServer, a.DB) _ = server.Serve() } diff --git a/patientapp/cmd/main.go b/patientapp/cmd/main.go index 0c28ac82..0f755aad 100644 --- a/patientapp/cmd/main.go +++ b/patientapp/cmd/main.go @@ -1,15 +1,26 @@ package main import ( + "os" + "strconv" "time" "git.gocasts.ir/ebhomengo/niki/patientapp" "git.gocasts.ir/ebhomengo/niki/patientapp/config" - "git.gocasts.ir/ebhomengo/niki/patientapp/repository/mysql" + "git.gocasts.ir/ebhomengo/niki/repository/mysql" ) func main() { - db := mysql.DataBase{} + 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, @@ -19,7 +30,7 @@ func main() { ShutDownCtxTimeout: 5 * time.Second, } - app := patientapp.Setup(cfg, &db) + app := patientapp.Setup(cfg, db) app.Start() diff --git a/patientapp/delivery/http/analytic/router.go b/patientapp/delivery/http/analytic/router.go index c6499428..56ed1136 100644 --- a/patientapp/delivery/http/analytic/router.go +++ b/patientapp/delivery/http/analytic/router.go @@ -1,15 +1,15 @@ package analytic import ( - "git.gocasts.ir/ebhomengo/niki/patientapp/repository/mysql" + 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) { +func NewPatientAnalyticRouter(s *echo.Group, db *mysql.DB) { - mysqlRepo := mysql.NewPatientRepo() - //rpcRepo := grpc.NewPatientRepo() + mysqlRepo := repo.NewPatientRepo(db) analyticService := analytic2.NewPatientAnalyticService(mysqlRepo) diff --git a/patientapp/delivery/http/analytic/server.go b/patientapp/delivery/http/analytic/server.go index 91ec8ba7..d92e59fc 100644 --- a/patientapp/delivery/http/analytic/server.go +++ b/patientapp/delivery/http/analytic/server.go @@ -5,16 +5,19 @@ import ( "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) *Server { +func NewServer(server *config.EchoServer, db *mysql.DB) *Server { return &Server{ HTTPServer: server, + Db: db, } } @@ -35,7 +38,7 @@ func (s Server) RegisterRoutes() { { // Analytic Group analyticGroup := v1.Group("/analytic") - NewPatientAnalyticRouter(analyticGroup) + NewPatientAnalyticRouter(analyticGroup, s.Db) } } diff --git a/patientapp/docker/docker-compose.yml b/patientapp/docker/docker-compose.yml new file mode 100644 index 00000000..2d45d595 --- /dev/null +++ b/patientapp/docker/docker-compose.yml @@ -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 \ No newline at end of file diff --git a/patientapp/docker/repository/migration/patient_schema.sql b/patientapp/docker/repository/migration/patient_schema.sql new file mode 100644 index 00000000..39700c33 --- /dev/null +++ b/patientapp/docker/repository/migration/patient_schema.sql @@ -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 diff --git a/patientapp/repository/grpc/analytic_repo.go b/patientapp/repository/grpc/analytic_repo.go deleted file mode 100644 index fe598750..00000000 --- a/patientapp/repository/grpc/analytic_repo.go +++ /dev/null @@ -1,35 +0,0 @@ -package grpc - -import ( - "context" - - "git.gocasts.ir/ebhomengo/niki/patientapp/service/analytic" - "git.gocasts.ir/ebhomengo/niki/patientapp/service/entity" -) - -type AnalyticRepository struct{} - -func NewPatientRepo() *AnalyticRepository { - - return &AnalyticRepository{} -} - -func (db *AnalyticRepository) GetPatients(ctx context.Context, f analytic.PatientFilter) ([]entity.Patient, error) { - - return nil, nil -} - -func (db *AnalyticRepository) CountPatients(ctx context.Context, f analytic.PatientFilter) (int, error) { - - return 0, nil -} - -func (db *AnalyticRepository) SummaryByCity(ctx context.Context, provinceID uint, f analytic.PatientMapFilter) (map[uint][]entity.MapSummaryItem, error) { - - return nil, nil -} - -func (db *AnalyticRepository) SummaryByProvince(ctx context.Context, f analytic.PatientMapFilter) (map[uint][]entity.MapSummaryItem, error) { - - return nil, nil -} diff --git a/patientapp/repository/mysql/analytic_repo.go b/patientapp/repository/mysql/analytic_repo.go index 223080cf..ff05fe65 100644 --- a/patientapp/repository/mysql/analytic_repo.go +++ b/patientapp/repository/mysql/analytic_repo.go @@ -2,35 +2,385 @@ 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{} - -func NewPatientRepo() *DataBase { - - return &DataBase{} +type DataBase struct { + conn *mysql.DB } -func (db *DataBase) GetPatients(ctx context.Context, f analytic.PatientFilter) ([]entity.Patient, error) { +func NewPatientRepo(db *mysql.DB) *DataBase { - return nil, nil + 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" - return 0, nil + 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" - return nil, nil + 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) { - return nil, nil + 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 } diff --git a/patientapp/service/analytic/param.go b/patientapp/service/analytic/param.go index da14af37..1e93eb41 100644 --- a/patientapp/service/analytic/param.go +++ b/patientapp/service/analytic/param.go @@ -6,12 +6,12 @@ import ( type ListPatientAnalyticRequest struct { // All fields are optional - MinAge *int `query:"minAge,omitempty"` - MaxAge *int `query:"maxAge,omitempty"` - Sex *entity.Sex `query:"sex,omitempty"` + MinAge *int `query:"minAge,omitempty"` + MaxAge *int `query:"maxAge,omitempty"` + Sex *string `query:"sex,omitempty"` - City *int64 `query:"city,omitempty"` - Province *int64 `query:"province,omitempty"` + City *uint16 `query:"city,omitempty"` + Province *uint16 `query:"province,omitempty"` Search *string `query:"search,omitempty"` @@ -19,11 +19,11 @@ type ListPatientAnalyticRequest struct { } type PatientAnalyticItem struct { - ID int64 `json:"id"` + ID uint64 `json:"id"` FirstName string `json:"first_name"` LastName string `json:"Last_name"` DateOfBirth string `json:"dob,omitempty"` - Sex entity.Sex `json:"sex"` + Sex string `json:"sex"` Phone string `json:"phone"` Address entity.Address `json:"address"` } @@ -33,17 +33,17 @@ type PatientAnalyticResponse struct { Total int `json:"total"` } -func ToPatientResponse(patient entity.Patient) PatientAnalyticItem { +func ToPatientResponse(patient entity.UserMeta) PatientAnalyticItem { return PatientAnalyticItem{ ID: patient.ID, - FirstName: patient.FirstName, + FirstName: patient.Name, LastName: patient.LastName, - DateOfBirth: patient.DateOfBirth, - Sex: patient.Sex, - Phone: patient.Phone, + DateOfBirth: patient.BirthDate, + Sex: patient.Gender, + Phone: *patient.Mobile, Address: entity.Address{ - ProvinceID: patient.Address.ProvinceID, - CityID: patient.Address.CityID, + ProvinceID: patient.AddressState, + CityID: patient.AddressCity, }, } } diff --git a/patientapp/service/analytic/patient_filter.go b/patientapp/service/analytic/patient_filter.go index c7dd7695..42a8e47d 100644 --- a/patientapp/service/analytic/patient_filter.go +++ b/patientapp/service/analytic/patient_filter.go @@ -7,11 +7,11 @@ import ( type PatientFilter struct { DOBFrom *string // born after DOBTo *string // born before - Sex *entity.Sex + Sex *string - City *int64 - Province *int64 - Country *int64 + Nationality string + AddressState uint16 + AddressCity uint16 Search *string diff --git a/patientapp/service/analytic/service.go b/patientapp/service/analytic/service.go index 518d8756..01f704c3 100644 --- a/patientapp/service/analytic/service.go +++ b/patientapp/service/analytic/service.go @@ -16,7 +16,7 @@ var ( ) type Repository interface { - GetPatients(ctx context.Context, f PatientFilter) ([]entity.Patient, error) + 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) @@ -41,14 +41,14 @@ func (s Service) List(ctx context.Context, req ListPatientAnalyticRequest) (Pati dobFrom, dobTo := ageRangeToDOB(req.MinAge, req.MaxAge, time.Now()) filter := PatientFilter{ - DOBFrom: dobFrom, - DOBTo: dobTo, - Sex: req.Sex, - City: req.City, - Province: req.Province, - Search: req.Search, - Limit: limit, - Offset: offset, + 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) diff --git a/patientapp/service/entity/address.go b/patientapp/service/entity/address.go index 193531a9..cdc36198 100644 --- a/patientapp/service/entity/address.go +++ b/patientapp/service/entity/address.go @@ -7,8 +7,8 @@ type Address struct { Name string Lat float64 Lon float64 - CityID uint - ProvinceID uint + CityID uint16 + ProvinceID uint16 } type AddressAggregated struct { diff --git a/patientapp/service/entity/map_summary.go b/patientapp/service/entity/map_summary.go index fcfd1284..e0768ae6 100644 --- a/patientapp/service/entity/map_summary.go +++ b/patientapp/service/entity/map_summary.go @@ -9,9 +9,8 @@ const ( ) type MapSummaryItem struct { - LocationID int64 - Name string - Count int - CentroidLat float64 - CentroidLng float64 + ID uint `json:"id"` // city_id OR state_id + Latitude float64 `json:"latitude"` + Longitude float64 `json:"longitude"` + Count int `json:"count"` } diff --git a/patientapp/service/entity/patient.go b/patientapp/service/entity/patient.go index 33857e26..519d9e38 100644 --- a/patientapp/service/entity/patient.go +++ b/patientapp/service/entity/patient.go @@ -1,5 +1,7 @@ package entity +import "time" + type Patient struct { ID int64 FirstName string @@ -15,6 +17,27 @@ type Patient struct { 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 diff --git a/purchaseapp/app.go b/purchaseapp/app.go index 7f62b636..a846535b 100644 --- a/purchaseapp/app.go +++ b/purchaseapp/app.go @@ -3,9 +3,9 @@ package purchaseapp import ( "context" "fmt" + purchaseMysql "git.gocasts.ir/ebhomengo/niki/domain/purchase/repository/mysql" purchaseHTTP "git.gocasts.ir/ebhomengo/niki/purchaseapp/delivery/http" purchaseHandler "git.gocasts.ir/ebhomengo/niki/purchaseapp/delivery/http/order" - purchaseMysql "git.gocasts.ir/ebhomengo/niki/purchaseapp/repository/mysql" purchaseService "git.gocasts.ir/ebhomengo/niki/purchaseapp/service/order" "git.gocasts.ir/ebhomengo/niki/repository/mysql" ) diff --git a/purchaseapp/delivery/http/order/handler.go b/purchaseapp/delivery/http/order/handler.go index 9ae96087..e9ba97f1 100644 --- a/purchaseapp/delivery/http/order/handler.go +++ b/purchaseapp/delivery/http/order/handler.go @@ -1,9 +1,9 @@ package order import ( + entity "git.gocasts.ir/ebhomengo/niki/domain/order/entity" + order "git.gocasts.ir/ebhomengo/niki/domain/order/service" richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error" - "git.gocasts.ir/ebhomengo/niki/purchaseapp/entity" - "git.gocasts.ir/ebhomengo/niki/purchaseapp/service/order" "github.com/labstack/echo/v4" "net/http" "time" @@ -18,7 +18,7 @@ func New(orderSvc order.Service) *Handler { } func (h *Handler) CreateOrderHandler(c echo.Context) error { - var req order.CreateOrderRequest + var req CreateOrderRequest if err := c.Bind(&req); err != nil { msg, code := getErrorDataFromRichError(err) return echo.NewHTTPError(code, msg) @@ -34,7 +34,7 @@ func (h *Handler) CreateOrderHandler(c echo.Context) error { PaymentMethod: req.PaymentMethod, ProcessStatus: entity.WaitingToPay, PaymentStatus: entity.UnPaid, - Address: req.Address, + AddressID: req.AddressID, CreatedAt: time.Now(), UpdatedAt: time.Now(), } diff --git a/purchaseapp/service/order/param.go b/purchaseapp/delivery/http/order/param.go similarity index 82% rename from purchaseapp/service/order/param.go rename to purchaseapp/delivery/http/order/param.go index d4a589f9..dac464ba 100644 --- a/purchaseapp/service/order/param.go +++ b/purchaseapp/delivery/http/order/param.go @@ -1,13 +1,13 @@ package order import ( - "git.gocasts.ir/ebhomengo/niki/purchaseapp/entity" + "git.gocasts.ir/ebhomengo/niki/domain/order/entity" "git.gocasts.ir/ebhomengo/niki/types" ) type CreateOrderRequest struct { UserID types.ID `json:"user_id"` - Address string `json:"address"` + AddressID types.ID `json:"address_id"` ShippingID types.ID `json:"shipping_id"` PaymentMethod entity.PaymentMethod `json:"payment_method"` TotalAmount types.Price `json:"total_amount"` diff --git a/purchaseapp/delivery/http/order/route.go b/purchaseapp/delivery/http/order/route.go index 3db0dd1f..62b20d3c 100644 --- a/purchaseapp/delivery/http/order/route.go +++ b/purchaseapp/delivery/http/order/route.go @@ -4,5 +4,4 @@ import "github.com/labstack/echo/v4" func (h Handler) SetRoutes(e *echo.Echo) { - e.POST("/order/create", h.CreateOrderHandler) } diff --git a/purchaseapp/delivery/http/server.go b/purchaseapp/delivery/http/server.go index 1da710cb..0852e026 100644 --- a/purchaseapp/delivery/http/server.go +++ b/purchaseapp/delivery/http/server.go @@ -1,19 +1,19 @@ package http import ( - "git.gocasts.ir/ebhomengo/niki/purchaseapp/delivery/http/order" - orderService "git.gocasts.ir/ebhomengo/niki/purchaseapp/service/order" + order "git.gocasts.ir/ebhomengo/niki/domain/order/service" + orderHandler "git.gocasts.ir/ebhomengo/niki/purchaseapp/delivery/http/order" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" ) type Server struct { - OrderHandler *order.Handler + OrderHandler *orderHandler.Handler } -func New(orderSvc orderService.Service) *Server { +func New(orderSvc order.Service) *Server { return &Server{ - OrderHandler: order.New(orderSvc), + OrderHandler: orderHandler.New(orderSvc), } } diff --git a/purchaseapp/entity/order.go b/purchaseapp/entity/order.go deleted file mode 100644 index 0e235f66..00000000 --- a/purchaseapp/entity/order.go +++ /dev/null @@ -1,59 +0,0 @@ -package entity - -import ( - "git.gocasts.ir/ebhomengo/niki/types" - "time" -) - -type Order struct { - ID types.ID - UserID types.ID - TotalAmount types.Price - TotalDiscount types.Price - ShippingID types.ID - PaymentMethod PaymentMethod - ProcessStatus ProcessStatus - PaymentStatus PaymentStatus - Address string - CreatedAt time.Time - UpdatedAt time.Time -} - -type OrderItem struct { - ID types.ID - ProductID types.ID - Price types.Price - Quantity types.Count - PriceWithDiscount types.Price - OrderID types.ID - CreatedAt time.Time -} - -type PaymentMethod string - -const ( - Online PaymentMethod = "online" - Wallet = "wallet" - Cart = "cart" -) - -type ProcessStatus string - -const ( - WaitingToPay ProcessStatus = "waiting-to-pay" - processing = "processing" - accepted = "accepted" - preparing = "preparing" - prepared = "prepared" - givenToPost = "given-to-post" - delivered = "delivered" - cancelled = "cancelled" -) - -type PaymentStatus string - -const ( - Paid PaymentStatus = "paid" - UnPaid = "unpaid" - Cancelled = "cancelled" -) diff --git a/purchaseapp/repository/mysql/db.go b/purchaseapp/repository/mysql/db.go deleted file mode 100644 index b0843023..00000000 --- a/purchaseapp/repository/mysql/db.go +++ /dev/null @@ -1 +0,0 @@ -package mysql diff --git a/purchaseapp/service/order/validator.go b/purchaseapp/service/order/validator.go deleted file mode 100644 index 175f0c10..00000000 --- a/purchaseapp/service/order/validator.go +++ /dev/null @@ -1 +0,0 @@ -package order