This commit is contained in:
2026-02-11 17:46:22 +03:00
commit eacfacb13b
266 changed files with 51337 additions and 0 deletions

View File

@@ -0,0 +1,286 @@
package repository
import (
"context"
"database/sql"
"fmt"
"strings"
"time"
)
type ActivityLogRow struct {
CreatedAt time.Time `json:"created_at"`
RequestStartedAt *time.Time `json:"request_started_at,omitempty"`
RequestFinishedAt *time.Time `json:"request_finished_at,omitempty"`
DurationMs *int `json:"duration_ms,omitempty"`
HttpStatus *int `json:"http_status,omitempty"`
Username string `json:"username"`
RoleCode string `json:"role_code"`
ActionCategory string `json:"action_category"`
ActionType string `json:"action_type"`
ActionTarget string `json:"action_target"`
Description string `json:"description"`
// ✅ TARGET USER
TargetDfUsrID int64 `json:"target_dfusr_id"`
TargetUsername string `json:"target_username"`
// ✅ DIFF
ChangeBefore string `json:"change_before"`
ChangeAfter string `json:"change_after"`
IpAddress string `json:"ip_address"`
IsSuccess bool `json:"is_success"`
ErrorMsg string `json:"error_message"`
UserAgent string `json:"user_agent"`
SessionID string `json:"session_id"`
// audit ids
UserUUID string `json:"user_id"`
DfUsrID int64 `json:"dfusr_id"`
Email string `json:"email"`
IsActive bool `json:"is_active"`
}
type ActivityLogQuery struct {
Username string
RoleCode string
ActionCategory string
ActionType string
ActionTarget string
Success *bool
StatusMin *int
StatusMax *int
DateFrom *time.Time
DateTo *time.Time
Page int
Limit int
}
type ActivityLogResult struct {
Total int64 `json:"total"`
Items []ActivityLogRow `json:"items"`
}
func ListActivityLogs(
ctx context.Context,
db *sql.DB,
q ActivityLogQuery,
) (ActivityLogResult, error) {
// ---------- defaults ----------
if q.Page <= 0 {
q.Page = 1
}
// limit <=0 → unlimited
if q.Limit < 0 {
q.Limit = 50
}
if q.Limit > 200 {
q.Limit = 200
}
offset := 0
if q.Limit > 0 {
offset = (q.Page - 1) * q.Limit
}
where := []string{}
args := []interface{}{}
add := func(cond string, val interface{}) {
where = append(where, cond)
args = append(args, val)
}
// ---------- filters ----------
if strings.TrimSpace(q.Username) != "" {
add(fmt.Sprintf("l.username ILIKE $%d", len(args)+1),
"%"+strings.TrimSpace(q.Username)+"%")
}
if strings.TrimSpace(q.RoleCode) != "" {
add(fmt.Sprintf("r.code = $%d", len(args)+1),
strings.TrimSpace(q.RoleCode))
}
if strings.TrimSpace(q.ActionCategory) != "" {
add(fmt.Sprintf("l.action_category = $%d", len(args)+1),
strings.TrimSpace(q.ActionCategory))
}
if strings.TrimSpace(q.ActionType) != "" {
add(fmt.Sprintf("l.action_type = $%d", len(args)+1),
strings.TrimSpace(q.ActionType))
}
if strings.TrimSpace(q.ActionTarget) != "" {
add(fmt.Sprintf("l.action_target ILIKE $%d", len(args)+1),
"%"+strings.TrimSpace(q.ActionTarget)+"%")
}
if q.Success != nil {
add(fmt.Sprintf("l.is_success = $%d", len(args)+1), *q.Success)
}
if q.StatusMin != nil {
add(fmt.Sprintf("l.http_status >= $%d", len(args)+1), *q.StatusMin)
}
if q.StatusMax != nil {
add(fmt.Sprintf("l.http_status <= $%d", len(args)+1), *q.StatusMax)
}
if q.DateFrom != nil {
add(fmt.Sprintf("l.created_at >= $%d", len(args)+1), *q.DateFrom)
}
if q.DateTo != nil {
add(fmt.Sprintf("l.created_at < $%d", len(args)+1),
q.DateTo.Add(24*time.Hour))
}
whereSQL := ""
if len(where) > 0 {
whereSQL = "WHERE " + strings.Join(where, " AND ")
}
// ---------- COUNT ----------
countSQL := fmt.Sprintf(`
SELECT count(*)
FROM mk_user_activity_log l
LEFT JOIN mk_dfusr u ON u.id = l.dfusr_id
LEFT JOIN dfrole_usr ru ON ru.dfusr_id = u.id
LEFT JOIN dfrole r ON r.id = ru.dfrole_id
%s
`, whereSQL)
var total int64
if err := db.QueryRowContext(ctx, countSQL, args...).Scan(&total); err != nil {
return ActivityLogResult{}, err
}
// ---------- LIST ----------
listArgs := append([]interface{}{}, args...)
listSQL := fmt.Sprintf(`
SELECT
l.created_at,
l.request_started_at,
l.request_finished_at,
l.duration_ms,
l.http_status,
COALESCE(u.username, l.username, '') AS username,
COALESCE(r.code,''),
COALESCE(l.action_category,''),
COALESCE(l.action_type,''),
COALESCE(l.action_target,''),
COALESCE(l.description,''),
COALESCE(l.target_dfusr_id, 0),
COALESCE(l.target_username, ''),
COALESCE(l.change_before::text, ''),
COALESCE(l.change_after::text, ''),
COALESCE(l.ip_address,''),
COALESCE(l.is_success,false),
COALESCE(l.error_message,''),
COALESCE(l.user_agent,''),
COALESCE(l.session_id,''),
COALESCE(l.user_id::text,''),
COALESCE(l.dfusr_id,0),
COALESCE(u.email,''),
COALESCE(u.is_active,false)
FROM mk_user_activity_log l
LEFT JOIN mk_dfusr u ON u.id = l.dfusr_id
LEFT JOIN dfrole_usr ru ON ru.dfusr_id = u.id
LEFT JOIN dfrole r ON r.id = ru.dfrole_id
%s
ORDER BY l.created_at DESC
`, whereSQL)
// LIMIT sadece istenirse
if q.Limit > 0 {
listSQL += fmt.Sprintf(
" LIMIT $%d OFFSET $%d",
len(listArgs)+1,
len(listArgs)+2,
)
listArgs = append(listArgs, q.Limit, offset)
}
rows, err := db.QueryContext(ctx, listSQL, listArgs...)
if err != nil {
return ActivityLogResult{}, err
}
defer rows.Close()
items := []ActivityLogRow{}
for rows.Next() {
var it ActivityLogRow
var started, finished sql.NullTime
var dur, status sql.NullInt64
if err := rows.Scan(
&it.CreatedAt,
&started,
&finished,
&dur,
&status,
&it.Username,
&it.RoleCode,
&it.ActionCategory,
&it.ActionType,
&it.ActionTarget,
&it.Description,
// ✅ NEW
&it.TargetDfUsrID,
&it.TargetUsername,
&it.ChangeBefore,
&it.ChangeAfter,
&it.IpAddress,
&it.IsSuccess,
&it.ErrorMsg,
&it.UserAgent,
&it.SessionID,
&it.UserUUID,
&it.DfUsrID,
&it.Email,
&it.IsActive,
); err != nil {
return ActivityLogResult{}, err
}
if started.Valid {
it.RequestStartedAt = &started.Time
}
if finished.Valid {
it.RequestFinishedAt = &finished.Time
}
if dur.Valid {
v := int(dur.Int64)
it.DurationMs = &v
}
if status.Valid {
v := int(status.Int64)
it.HttpStatus = &v
}
items = append(items, it)
}
return ActivityLogResult{
Total: total,
Items: items,
}, nil
}

View File

@@ -0,0 +1,352 @@
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,
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
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.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,
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
WHERE LOWER(u.username) = LOWER($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.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
}

View File

@@ -0,0 +1,33 @@
// repository/permission_repository.go
package repository
import "database/sql"
type PermissionRepository struct {
DB *sql.DB
}
func NewPermissionRepository(db *sql.DB) *PermissionRepository {
return &PermissionRepository{DB: db}
}
func (r *PermissionRepository) GetPermissionsByRoleID(roleID int64) ([]string, error) {
rows, err := r.DB.Query(`
SELECT route
FROM mkdf_rol_per
WHERE role_id = $1 AND can_access = true
`, roleID)
if err != nil {
return nil, err
}
defer rows.Close()
perms := []string{}
for rows.Next() {
var route string
if err := rows.Scan(&route); err == nil {
perms = append(perms, route)
}
}
return perms, nil
}

View File

@@ -0,0 +1,80 @@
package repository
import (
"database/sql"
"errors"
"time"
)
var ErrRefreshTokenInvalid = errors.New("refresh token invalid")
type RefreshTokenRepository struct {
DB *sql.DB
}
func NewRefreshTokenRepository(db *sql.DB) *RefreshTokenRepository {
return &RefreshTokenRepository{DB: db}
}
// Yeni refresh (HASH saklanır)
func (r *RefreshTokenRepository) IssueRefreshToken(
mkUserID int64,
tokenHash string,
expiresAt time.Time,
) error {
_, err := r.DB.Exec(`
INSERT INTO mk_refresh_tokens (mk_user_id, token_hash, expires_at)
VALUES ($1,$2,$3)
`, mkUserID, tokenHash, expiresAt)
return err
}
// Tek refreshi revoke et (rotation / logout)
func (r *RefreshTokenRepository) RevokeByHash(hash string) error {
_, err := r.DB.Exec(`
UPDATE mk_refresh_tokens
SET revoked_at = now()
WHERE token_hash = $1
AND revoked_at IS NULL
`, hash)
return err
}
// Kullanıcının tüm refreshlerini revoke et (logout-all / password change)
func (r *RefreshTokenRepository) RevokeAllForUser(mkUserID int64) error {
_, err := r.DB.Exec(`
UPDATE mk_refresh_tokens
SET revoked_at = now()
WHERE mk_user_id = $1
AND revoked_at IS NULL
`, mkUserID)
return err
}
// Geçerli refreshi tüket (validate + rotate)
func (r *RefreshTokenRepository) ConsumeValid(tokenHash string) (int64, error) {
var mkUserID int64
err := r.DB.QueryRow(`
SELECT mk_user_id
FROM mk_refresh_tokens
WHERE token_hash = $1
AND revoked_at IS NULL
AND expires_at > now()
`, tokenHash).Scan(&mkUserID)
if err != nil {
if err == sql.ErrNoRows {
return 0, ErrRefreshTokenInvalid
}
return 0, err
}
// tek kullanımlık: eskiyi revoke et
_, _ = r.DB.Exec(`
UPDATE mk_refresh_tokens
SET revoked_at = now()
WHERE token_hash = $1
`, tokenHash)
return mkUserID, nil
}

276
svc/repository/user_repo.go Normal file
View File

@@ -0,0 +1,276 @@
package repository
import (
"bssapp-backend/models"
"database/sql"
"errors"
"fmt"
"log"
"strings"
)
// UserRepository → kullanıcı işlemleri için repository katmanı
type UserRepository struct {
DB *sql.DB
}
// NewUserRepository → yeni bir UserRepository döner
func NewUserRepository(db *sql.DB) *UserRepository {
return &UserRepository{DB: db}
}
// -------------------------------------------------------
// 📋 GetUserList (User Management)
// -------------------------------------------------------
func (r *UserRepository) GetUserList() ([]models.User, error) {
query := `
SELECT
u.id,
u.code,
COALESCE(u.upass,'') as upass,
u.is_active,
COALESCE(u.email,''),
COALESCE(uru.dfrole_id, 0) AS role_id,
COALESCE(dr.code, '') AS role_code
FROM dfusr u
LEFT JOIN dfrole_usr uru ON u.id = uru.dfusr_id
LEFT JOIN dfrole dr ON dr.id = uru.dfrole_id
ORDER BY u.code
`
rows, err := r.DB.Query(query)
if err != nil {
return nil, err
}
defer rows.Close()
var users []models.User
for rows.Next() {
var u models.User
if err := rows.Scan(
&u.ID,
&u.Username,
&u.Upass, // ✅ dfusr.upass
&u.IsActive,
&u.Email,
&u.RoleID,
&u.RoleCode,
); err != nil {
return nil, err
}
users = append(users, u)
}
return users, nil
}
// -------------------------------------------------------
// CreateUser (legacy dfusr insert) [kullanıyorsan kalsın]
// -------------------------------------------------------
func (r *UserRepository) CreateUser(u *models.User) (int, error) {
query := `
INSERT INTO dfusr (
code,
upass,
is_active,
email,
v3_username,
v3usergroup
)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING id
`
var newID int
err := r.DB.QueryRow(
query,
u.Username,
u.Upass, // ✅ upass
u.IsActive,
u.Email,
u.V3Username,
u.V3UserGroup,
).Scan(&newID)
if err != nil {
return 0, err
}
return newID, nil
}
// -------------------------------------------------------
// 🔍 GetUserByUsername (legacy) [login için şart değil ama düzelttim]
// -------------------------------------------------------
func (r *UserRepository) GetUserByUsername(username string) (*models.User, error) {
clean := strings.TrimSpace(username)
query := `
SELECT
u.id,
u.code,
COALESCE(u.upass,'') as upass,
u.is_active,
COALESCE(u.email,''),
COALESCE(uru.dfrole_id,0) AS role_id,
COALESCE(dr.code,'') AS role_code,
COALESCE(u.v3_username,'') as v3_username,
COALESCE(u.v3_usergroup,0) as v3_usergroup,
COALESCE(u.force_password_change,false) as force_password_change
FROM dfusr u
LEFT JOIN dfrole_usr uru
ON uru.dfusr_id = u.id
LEFT JOIN dfrole dr
ON dr.id = uru.dfrole_id
WHERE u.is_active = true
AND (
LOWER(u.code) = LOWER($1)
OR LOWER(u.email) = LOWER($1)
)
LIMIT 1;
`
row := r.DB.QueryRow(query, clean)
var user models.User
err := row.Scan(
&user.ID,
&user.Username,
&user.Upass,
&user.IsActive,
&user.Email,
&user.RoleID,
&user.RoleCode,
&user.V3Username,
&user.V3UserGroup,
&user.ForcePasswordChange,
)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, errors.New("kullanıcı bulunamadı")
}
return nil, err
}
return &user, nil
}
// -------------------------------------------------------
// 🧪 DebugListUsers
// -------------------------------------------------------
func (r *UserRepository) DebugListUsers() {
rows, err := r.DB.Query(`SELECT id, code FROM dfusr ORDER BY id LIMIT 10`)
if err != nil {
fmt.Println("❌ [DEBUG] Listeleme hatası:", err)
return
}
defer rows.Close()
fmt.Println("📋 [DEBUG] İlk 10 kullanıcı:")
for rows.Next() {
var id int
var code string
if err := rows.Scan(&id, &code); err == nil {
fmt.Printf(" - %d : %s\n", id, code)
}
}
}
// -------------------------------------------------------
// 🔁 SetUserRoles
// -------------------------------------------------------
func (r *UserRepository) SetUserRoles(userID int, roleIDs []int) error {
tx, err := r.DB.Begin()
if err != nil {
return err
}
defer tx.Rollback()
if _, err := tx.Exec(`DELETE FROM dfrole_usr WHERE dfusr_id = $1`, userID); err != nil {
return err
}
for _, roleID := range roleIDs {
if _, err := tx.Exec(
`INSERT INTO dfrole_usr (dfrole_id, dfusr_id) VALUES ($1, $2)`,
roleID, userID,
); err != nil {
return err
}
}
return tx.Commit()
}
// -------------------------------------------------------
// 🔐 GetLegacyUserForLogin ✅ LOGIN İÇİN TEK DOĞRU FONKSİYON
// -------------------------------------------------------
func (r *UserRepository) GetLegacyUserForLogin(login string) (*models.User, error) {
log.Println("🟡 LEGACY LOGIN QUERY HIT:", login)
login = strings.TrimSpace(login)
var u models.User
err := r.DB.QueryRow(`
SELECT
u.id,
u.code,
COALESCE(u.upass,'') as upass,
u.is_active,
COALESCE(u.email,''),
COALESCE(u.dfrole_id,0) as role_id,
COALESCE(dr.code,'') as role_code,
COALESCE(u.force_password_change,false)
FROM dfusr u
LEFT JOIN dfrole dr ON dr.id = u.dfrole_id
WHERE u.is_active = true
AND (
LOWER(u.code) = LOWER($1)
OR LOWER(u.email) = LOWER($1)
)
LIMIT 1
`, login).Scan(
&u.ID,
&u.Username,
&u.Upass,
&u.IsActive,
&u.Email,
&u.RoleID,
&u.RoleCode,
&u.ForcePasswordChange,
)
if err != nil {
log.Printf("❌ LEGACY SCAN ERROR: %v", err)
return nil, err
}
prefix := u.Upass
if len(prefix) > 4 {
prefix = prefix[:4]
}
log.Printf(
"🧪 LEGACY UPASS OK len=%d prefix=%s",
len(u.Upass),
prefix,
)
return &u, nil
}

View File

@@ -0,0 +1,32 @@
package repository
import "database/sql"
type UserRole struct {
RoleID int
RoleCode string
IsSystem bool
}
// repository/user_roles.go
// repository/user_roles.go
func GetUserRolesByUserID(db *sql.DB, userID int64) ([]int, error) {
rows, err := db.Query(`
SELECT role_id
FROM dfrole_usr
WHERE mk_dfusr_id = $1
`, userID)
if err != nil {
return nil, err
}
defer rows.Close()
var roles []int
for rows.Next() {
var rid int
if err := rows.Scan(&rid); err == nil {
roles = append(roles, rid)
}
}
return roles, nil
}