375 lines
7.0 KiB
Go
375 lines
7.0 KiB
Go
package repository
|
||
|
||
import (
|
||
"bssapp-backend/models"
|
||
"database/sql"
|
||
"errors"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/lib/pq"
|
||
)
|
||
|
||
var ErrMkUserNotFound = errors.New("mk_user not found")
|
||
|
||
type MkUserRepository struct {
|
||
DB *sql.DB
|
||
}
|
||
|
||
func NewMkUserRepository(db *sql.DB) *MkUserRepository {
|
||
return &MkUserRepository{DB: db}
|
||
}
|
||
|
||
// -------------------------------------------------------
|
||
// 🔍 GET BY USERNAME
|
||
// -------------------------------------------------------
|
||
func (r *MkUserRepository) GetByUsername(username string) (*models.MkUser, error) {
|
||
username = strings.TrimSpace(username)
|
||
|
||
var u models.MkUser
|
||
|
||
err := r.DB.QueryRow(`SELECT
|
||
u.id,
|
||
u.username,
|
||
COALESCE(u.email,'') AS email,
|
||
u.is_active,
|
||
COALESCE(u.password_hash,'') AS password_hash,
|
||
u.force_password_change,
|
||
|
||
COALESCE(r.id, 0) AS role_id,
|
||
COALESCE(r.code, '') AS role_code,
|
||
|
||
-- ✅ DEPARTMENTS
|
||
COALESCE(
|
||
array_agg(DISTINCT d.code)
|
||
FILTER (WHERE d.code IS NOT NULL),
|
||
'{}'
|
||
) AS department_codes,
|
||
COALESCE(MAX(n.username), '') AS v3_username,
|
||
COALESCE(MAX(n.user_group_code::text), '') AS v3_usergroup,
|
||
|
||
u.password_updated_at,
|
||
u.created_at,
|
||
u.updated_at,
|
||
u.last_login_at
|
||
|
||
FROM mk_dfusr u
|
||
|
||
LEFT JOIN dfrole_usr ru
|
||
ON ru.dfusr_id = u.id
|
||
|
||
LEFT JOIN dfrole r
|
||
ON r.id = ru.dfrole_id
|
||
|
||
-- ✅ USER → DEPT
|
||
LEFT JOIN dfusr_dprt ud
|
||
ON ud.dfusr_id = u.id
|
||
AND ud.is_active = true
|
||
|
||
LEFT JOIN mk_dprt d
|
||
ON d.id = ud.dprt_id
|
||
|
||
LEFT JOIN dfusr_nebim_user un
|
||
ON un.dfusr_id = u.id
|
||
|
||
LEFT JOIN mk_nebim_user n
|
||
ON n.id = un.mk_nebim_user_id
|
||
AND n.is_active = true
|
||
|
||
WHERE LOWER(u.username) = LOWER($1)
|
||
|
||
GROUP BY
|
||
u.id, r.id
|
||
|
||
LIMIT 1
|
||
`, username).Scan(
|
||
&u.ID,
|
||
&u.Username,
|
||
&u.Email,
|
||
&u.IsActive,
|
||
&u.PasswordHash,
|
||
&u.ForcePasswordChange,
|
||
|
||
&u.RoleID,
|
||
&u.RoleCode,
|
||
|
||
pq.Array(&u.DepartmentCodes), // ✅
|
||
&u.V3Username,
|
||
&u.V3UserGroup,
|
||
|
||
&u.PasswordUpdatedAt,
|
||
|
||
&u.CreatedAt,
|
||
&u.UpdatedAt,
|
||
&u.LastLoginAt,
|
||
)
|
||
|
||
if err != nil {
|
||
if errors.Is(err, sql.ErrNoRows) {
|
||
return nil, ErrMkUserNotFound
|
||
}
|
||
return nil, err
|
||
}
|
||
|
||
return &u, nil
|
||
}
|
||
|
||
// -------------------------------------------------------
|
||
// 🔍 GET BY ID
|
||
// -------------------------------------------------------
|
||
func (r *MkUserRepository) GetByID(id int64) (*models.MkUser, error) {
|
||
var u models.MkUser
|
||
|
||
err := r.DB.QueryRow(`
|
||
SELECT
|
||
u.id,
|
||
u.username,
|
||
COALESCE(u.email,'') AS email,
|
||
u.is_active,
|
||
COALESCE(u.password_hash,'') AS password_hash,
|
||
u.force_password_change,
|
||
|
||
COALESCE(r.id, 0) AS role_id,
|
||
COALESCE(r.code, '') AS role_code,
|
||
|
||
-- ✅ DEPARTMENTS
|
||
COALESCE(
|
||
array_agg(DISTINCT d.code)
|
||
FILTER (WHERE d.code IS NOT NULL),
|
||
'{}'
|
||
) AS department_codes,
|
||
COALESCE(MAX(n.username), '') AS v3_username,
|
||
COALESCE(MAX(n.user_group_code::text), '') AS v3_usergroup,
|
||
|
||
u.password_updated_at,
|
||
u.created_at,
|
||
u.updated_at,
|
||
u.last_login_at
|
||
|
||
FROM mk_dfusr u
|
||
|
||
LEFT JOIN dfrole_usr ru
|
||
ON ru.dfusr_id = u.id
|
||
|
||
LEFT JOIN dfrole r
|
||
ON r.id = ru.dfrole_id
|
||
|
||
-- ✅ USER → DEPT
|
||
LEFT JOIN dfusr_dprt ud
|
||
ON ud.dfusr_id = u.id
|
||
AND ud.is_active = true
|
||
|
||
LEFT JOIN mk_dprt d
|
||
ON d.id = ud.dprt_id
|
||
|
||
LEFT JOIN dfusr_nebim_user un
|
||
ON un.dfusr_id = u.id
|
||
|
||
LEFT JOIN mk_nebim_user n
|
||
ON n.id = un.mk_nebim_user_id
|
||
AND n.is_active = true
|
||
|
||
WHERE u.id = $1
|
||
|
||
GROUP BY
|
||
u.id, r.id
|
||
|
||
LIMIT 1
|
||
`, id).Scan(
|
||
&u.ID,
|
||
&u.Username,
|
||
&u.Email,
|
||
&u.IsActive,
|
||
&u.PasswordHash,
|
||
&u.ForcePasswordChange,
|
||
|
||
&u.RoleID,
|
||
&u.RoleCode,
|
||
pq.Array(&u.DepartmentCodes), // ✅
|
||
&u.V3Username,
|
||
&u.V3UserGroup,
|
||
&u.PasswordUpdatedAt,
|
||
&u.CreatedAt,
|
||
&u.UpdatedAt,
|
||
&u.LastLoginAt,
|
||
)
|
||
|
||
if err != nil {
|
||
if errors.Is(err, sql.ErrNoRows) {
|
||
return nil, ErrMkUserNotFound
|
||
}
|
||
return nil, err
|
||
}
|
||
|
||
return &u, nil
|
||
}
|
||
|
||
// -------------------------------------------------------
|
||
// 🔁 CREATE FROM LEGACY (dfusr → mk_dfusr)
|
||
// - id = dfusr.id ✅
|
||
// - role / v3 YOK
|
||
// -------------------------------------------------------
|
||
// -------------------------------------------------------
|
||
// 🔁 CREATE FROM LEGACY (dfusr → mk_dfusr) FULL MIGRATION
|
||
// -------------------------------------------------------
|
||
func (r *MkUserRepository) CreateFromLegacy(
|
||
legacy *models.User,
|
||
passwordHash string,
|
||
) (*models.MkUser, error) {
|
||
|
||
var u models.MkUser
|
||
|
||
err := r.DB.QueryRow(`
|
||
INSERT INTO mk_dfusr (
|
||
id,
|
||
username,
|
||
email,
|
||
full_name,
|
||
mobile,
|
||
address,
|
||
is_active,
|
||
password_hash,
|
||
force_password_change,
|
||
created_at,
|
||
updated_at
|
||
)
|
||
VALUES (
|
||
$1,$2,$3,$4,$5,$6,$7,$8,true,now(),now()
|
||
)
|
||
ON CONFLICT (id)
|
||
DO UPDATE SET
|
||
email = EXCLUDED.email,
|
||
full_name = EXCLUDED.full_name,
|
||
mobile = EXCLUDED.mobile,
|
||
address = EXCLUDED.address,
|
||
password_hash = EXCLUDED.password_hash,
|
||
force_password_change= true,
|
||
updated_at = now()
|
||
RETURNING
|
||
id,
|
||
username,
|
||
COALESCE(email,''),
|
||
is_active,
|
||
COALESCE(password_hash,''),
|
||
force_password_change,
|
||
password_updated_at,
|
||
created_at,
|
||
updated_at,
|
||
last_login_at
|
||
`,
|
||
legacy.ID,
|
||
legacy.Username,
|
||
legacy.Email,
|
||
legacy.FullName,
|
||
legacy.Mobile,
|
||
legacy.Address,
|
||
legacy.IsActive,
|
||
passwordHash,
|
||
).Scan(
|
||
&u.ID,
|
||
&u.Username,
|
||
&u.Email,
|
||
&u.IsActive,
|
||
&u.PasswordHash,
|
||
&u.ForcePasswordChange,
|
||
&u.PasswordUpdatedAt,
|
||
&u.CreatedAt,
|
||
&u.UpdatedAt,
|
||
&u.LastLoginAt,
|
||
)
|
||
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return &u, nil
|
||
}
|
||
|
||
// -------------------------------------------------------
|
||
// ➕ CREATE NEW USER (NON-LEGACY)
|
||
// - id = sequence (>=1000)
|
||
// -------------------------------------------------------
|
||
func (r *MkUserRepository) CreateNewUser(
|
||
username string,
|
||
email string,
|
||
isActive bool,
|
||
) (*models.MkUser, error) {
|
||
|
||
var u models.MkUser
|
||
|
||
err := r.DB.QueryRow(`
|
||
INSERT INTO mk_dfusr (
|
||
username,
|
||
email,
|
||
is_active,
|
||
force_password_change,
|
||
created_at,
|
||
updated_at
|
||
)
|
||
VALUES (
|
||
$1,$2,$3,true,now(),now()
|
||
)
|
||
RETURNING
|
||
id,
|
||
username,
|
||
COALESCE(email,'') AS email,
|
||
is_active,
|
||
COALESCE(password_hash,'') AS password_hash,
|
||
force_password_change,
|
||
password_updated_at,
|
||
created_at,
|
||
updated_at,
|
||
last_login_at
|
||
`,
|
||
strings.TrimSpace(username),
|
||
strings.TrimSpace(email),
|
||
isActive,
|
||
).Scan(
|
||
&u.ID,
|
||
&u.Username,
|
||
&u.Email,
|
||
&u.IsActive,
|
||
&u.PasswordHash,
|
||
&u.ForcePasswordChange,
|
||
&u.PasswordUpdatedAt,
|
||
&u.CreatedAt,
|
||
&u.UpdatedAt,
|
||
&u.LastLoginAt,
|
||
)
|
||
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return &u, nil
|
||
}
|
||
|
||
// -------------------------------------------------------
|
||
// 🕒 TOUCH LAST LOGIN
|
||
// -------------------------------------------------------
|
||
func (r *MkUserRepository) TouchLastLogin(userID int64) error {
|
||
_, err := r.DB.Exec(`
|
||
UPDATE mk_dfusr
|
||
SET last_login_at = $1,
|
||
updated_at = $1
|
||
WHERE id = $2
|
||
`, time.Now(), userID)
|
||
return err
|
||
}
|
||
|
||
// -------------------------------------------------------
|
||
// 🔐 UPDATE PASSWORD
|
||
// -------------------------------------------------------
|
||
func (r *MkUserRepository) UpdatePassword(userID int64, newHash string) error {
|
||
_, err := r.DB.Exec(`
|
||
UPDATE mk_dfusr
|
||
SET
|
||
password_hash = $1,
|
||
force_password_change = false,
|
||
password_updated_at = NOW(),
|
||
updated_at = NOW()
|
||
WHERE id = $2
|
||
`, newHash, userID)
|
||
return err
|
||
}
|