forked from ebhomengo/niki
387 lines
8.4 KiB
Go
387 lines
8.4 KiB
Go
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
|
|
}
|