init push

This commit is contained in:
2026-07-16 22:16:45 +07:00
commit 8b068bdb10
1021 changed files with 332816 additions and 0 deletions

View File

@@ -0,0 +1,286 @@
package mysql
import (
"context"
"gorm.io/gorm"
"wucher/internal/domain/auth"
"wucher/internal/domain/user"
)
type userRoleJoinRow struct {
UserID []byte `gorm:"column:user_id"`
RoleID []byte `gorm:"column:role_id"`
RoleName string `gorm:"column:role_name"`
RoleDescription string `gorm:"column:role_description"`
}
func normalizeAssignedRoleIDs(primaryRoleID []byte, roleIDs [][]byte) [][]byte {
out := make([][]byte, 0, len(roleIDs)+1)
seen := make(map[string]struct{}, len(roleIDs)+1)
appendRole := func(roleID []byte) {
if len(roleID) != 16 {
return
}
key := string(roleID)
if _, exists := seen[key]; exists {
return
}
seen[key] = struct{}{}
out = append(out, append([]byte(nil), roleID...))
}
appendRole(primaryRoleID)
for i := range roleIDs {
appendRole(roleIDs[i])
}
return out
}
func replaceUserRolesTx(ctx context.Context, tx *gorm.DB, userID, primaryRoleID []byte, roleIDs [][]byte) error {
normalized := normalizeAssignedRoleIDs(primaryRoleID, roleIDs)
if len(userID) != 16 {
return nil
}
if err := tx.Where("user_id = ?", userID).Delete(&auth.UserRole{}).Error; err != nil {
return err
}
if len(normalized) == 0 {
return nil
}
actor := actorUserIDFromContext(ctx)
items := make([]auth.UserRole, 0, len(normalized))
for i := range normalized {
item := auth.UserRole{
UserID: userID,
RoleID: normalized[i],
}
if len(actor) > 0 {
item.CreatedBy = actor
item.UpdatedBy = actor
}
items = append(items, item)
}
return tx.Create(&items).Error
}
func listUserRoleIDsTx(tx *gorm.DB, userID []byte) ([][]byte, error) {
var rows []struct {
RoleID []byte `gorm:"column:role_id"`
}
if err := tx.Table("user_roles").
Select("role_id").
Where("user_id = ?", userID).
Find(&rows).Error; err != nil {
return nil, err
}
roleIDs := make([][]byte, 0, len(rows))
for i := range rows {
if len(rows[i].RoleID) != 16 {
continue
}
roleIDs = append(roleIDs, append([]byte(nil), rows[i].RoleID...))
}
return roleIDs, nil
}
func replacePrimaryUserRoleTx(ctx context.Context, tx *gorm.DB, userID, currentPrimaryRoleID, newPrimaryRoleID []byte) error {
roleIDs, err := listUserRoleIDsTx(tx, userID)
if err != nil {
return err
}
filtered := make([][]byte, 0, len(roleIDs))
currentPrimaryKey := string(currentPrimaryRoleID)
newPrimaryKey := string(newPrimaryRoleID)
for i := range roleIDs {
key := string(roleIDs[i])
if key == currentPrimaryKey || key == newPrimaryKey {
continue
}
filtered = append(filtered, roleIDs[i])
}
return replaceUserRolesTx(ctx, tx, userID, newPrimaryRoleID, filtered)
}
func loadUserRoleRows(ctx context.Context, db *gorm.DB, userIDs [][]byte) (map[string][]userRoleJoinRow, error) {
rowsByUserID := make(map[string][]userRoleJoinRow, len(userIDs))
if len(userIDs) == 0 {
return rowsByUserID, nil
}
var rows []userRoleJoinRow
if err := db.WithContext(ctx).
Table("user_roles ur").
Select("ur.user_id, roles.id AS role_id, roles.name AS role_name, roles.description AS role_description").
Joins("JOIN roles ON roles.id = ur.role_id").
Where("ur.user_id IN ?", userIDs).
Order("roles.name ASC").
Find(&rows).Error; err != nil {
return nil, err
}
for i := range rows {
key := string(rows[i].UserID)
rowsByUserID[key] = append(rowsByUserID[key], userRoleJoinRow{
UserID: append([]byte(nil), rows[i].UserID...),
RoleID: append([]byte(nil), rows[i].RoleID...),
RoleName: rows[i].RoleName,
RoleDescription: rows[i].RoleDescription,
})
}
return rowsByUserID, nil
}
func orderUserRoleRows(primaryRoleID []byte, rows []userRoleJoinRow) []userRoleJoinRow {
if len(rows) < 2 || len(primaryRoleID) != 16 {
return rows
}
primaryKey := string(primaryRoleID)
ordered := make([]userRoleJoinRow, 0, len(rows))
for i := range rows {
if string(rows[i].RoleID) == primaryKey {
ordered = append(ordered, rows[i])
break
}
}
for i := range rows {
if string(rows[i].RoleID) == primaryKey {
continue
}
ordered = append(ordered, rows[i])
}
return ordered
}
func loadRoleRowsByID(ctx context.Context, db *gorm.DB, roleIDs [][]byte) (map[string]userRoleJoinRow, error) {
out := make(map[string]userRoleJoinRow, len(roleIDs))
if len(roleIDs) == 0 {
return out, nil
}
var rows []struct {
ID []byte `gorm:"column:id"`
Name string `gorm:"column:name"`
Description string `gorm:"column:description"`
}
if err := db.WithContext(ctx).
Table("roles").
Select("id, name, description").
Where("id IN ?", roleIDs).
Find(&rows).Error; err != nil {
return nil, err
}
for i := range rows {
out[string(rows[i].ID)] = userRoleJoinRow{
RoleID: append([]byte(nil), rows[i].ID...),
RoleName: rows[i].Name,
RoleDescription: rows[i].Description,
}
}
return out, nil
}
func attachAuthUserRoles(ctx context.Context, db *gorm.DB, users ...*auth.User) error {
userIDs := make([][]byte, 0, len(users))
missingPrimaryRoleIDs := make([][]byte, 0, len(users))
for i := range users {
if users[i] == nil || len(users[i].ID) != 16 {
continue
}
userIDs = append(userIDs, users[i].ID)
if len(users[i].RoleID) == 16 {
missingPrimaryRoleIDs = append(missingPrimaryRoleIDs, users[i].RoleID)
}
}
rowsByUserID, err := loadUserRoleRows(ctx, db, userIDs)
if err != nil {
return err
}
roleRowsByID, err := loadRoleRowsByID(ctx, db, missingPrimaryRoleIDs)
if err != nil {
return err
}
for i := range users {
if users[i] == nil {
continue
}
rows := orderUserRoleRows(users[i].RoleID, rowsByUserID[string(users[i].ID)])
if len(rows) == 0 && len(users[i].RoleID) == 16 {
if roleRow, ok := roleRowsByID[string(users[i].RoleID)]; ok {
rows = []userRoleJoinRow{roleRow}
}
}
users[i].RoleIDs = make([][]byte, 0, len(rows))
users[i].Roles = make([]auth.Role, 0, len(rows))
for j := range rows {
users[i].RoleIDs = append(users[i].RoleIDs, append([]byte(nil), rows[j].RoleID...))
users[i].Roles = append(users[i].Roles, auth.Role{
ID: append([]byte(nil), rows[j].RoleID...),
Name: rows[j].RoleName,
Description: rows[j].RoleDescription,
})
}
}
return nil
}
func attachUserDomainRoles(ctx context.Context, db *gorm.DB, users ...*user.User) error {
userIDs := make([][]byte, 0, len(users))
for i := range users {
if users[i] == nil || len(users[i].ID) != 16 {
continue
}
userIDs = append(userIDs, users[i].ID)
}
rowsByUserID, err := loadUserRoleRows(ctx, db, userIDs)
if err != nil {
return err
}
for i := range users {
if users[i] == nil {
continue
}
rows := orderUserRoleRows(users[i].RoleID, rowsByUserID[string(users[i].ID)])
if len(rows) == 0 && len(users[i].RoleID) == 16 {
rows = []userRoleJoinRow{{
RoleID: append([]byte(nil), users[i].RoleID...),
RoleName: users[i].RoleName,
RoleDescription: users[i].RoleDescription,
}}
}
users[i].RoleIDs = make([][]byte, 0, len(rows))
users[i].Roles = make([]user.RoleSummary, 0, len(rows))
for j := range rows {
users[i].RoleIDs = append(users[i].RoleIDs, append([]byte(nil), rows[j].RoleID...))
users[i].Roles = append(users[i].Roles, user.RoleSummary{
ID: append([]byte(nil), rows[j].RoleID...),
Name: rows[j].RoleName,
Description: rows[j].RoleDescription,
})
}
}
return nil
}