1
0
forked from baron/baron-sso

조직현황 구조변경. 총괄센터삼안 실 조직 삽입확인

This commit is contained in:
2026-05-11 20:13:54 +09:00
parent d3853fac2a
commit 3063450ee0
59 changed files with 5086 additions and 549 deletions

View File

@@ -391,7 +391,12 @@ func (h *TenantHandler) ImportTenantsCSV(c *fiber.Ctx) error {
}
}
tenant, err := h.createTenantCSVRecord(c, record, creatorID)
recordCreatorID := creatorID
if record.Type == domain.TenantTypeOrganization {
recordCreatorID = ""
}
tenant, err := h.createTenantCSVRecord(c, record, recordCreatorID)
if err != nil {
result.Failed++
result.Errors = append(result.Errors, fmt.Sprintf("row %d: %s", rowNumber, err.Error()))
@@ -632,11 +637,142 @@ func normalizeTenantConfig(config map[string]any) (domain.JSONMap, error) {
normalized[key] = fields
continue
}
if key == "visibility" {
visibility, ok := value.(string)
if !ok {
return nil, fmt.Errorf("visibility must be public, internal, or private")
}
visibility = strings.TrimSpace(strings.ToLower(visibility))
if visibility == "" || visibility == "public" {
normalized[key] = "public"
continue
}
if visibility != "internal" && visibility != "private" {
return nil, fmt.Errorf("visibility must be public, internal, or private")
}
normalized[key] = visibility
continue
}
if key == "orgUnitType" {
orgUnitType, ok := value.(string)
if !ok {
return nil, fmt.Errorf("orgUnitType must be one of 실, 팀, 디비전, 셀, 본부, 지역본부, 부")
}
orgUnitType = strings.TrimSpace(orgUnitType)
if orgUnitType == "" {
continue
}
if !isAllowedOrgUnitType(orgUnitType) {
return nil, fmt.Errorf("orgUnitType must be one of 실, 팀, 디비전, 셀, 본부, 지역본부, 부")
}
normalized[key] = orgUnitType
continue
}
normalized[key] = value
}
return normalized, nil
}
func isAllowedOrgUnitType(value string) bool {
switch value {
case "실", "팀", "디비전", "셀", "본부", "지역본부", "부":
return true
default:
return false
}
}
func hasTenantOrgConfig(config domain.JSONMap) bool {
if config == nil {
return false
}
_, hasVisibility := config["visibility"]
_, hasOrgUnitType := config["orgUnitType"]
return hasVisibility || hasOrgUnitType
}
func isHanmacFamilyDescendantTenant(tenant domain.Tenant, tenants []domain.Tenant) bool {
if strings.EqualFold(tenant.Slug, "hanmac-family") {
return false
}
byID := make(map[string]domain.Tenant, len(tenants)+1)
for _, item := range tenants {
byID[item.ID] = item
}
byID[tenant.ID] = tenant
parentID := tenant.ParentID
visited := make(map[string]bool)
for parentID != nil && *parentID != "" {
if visited[*parentID] {
return false
}
visited[*parentID] = true
parent, ok := byID[*parentID]
if !ok {
return false
}
if strings.EqualFold(parent.Slug, "hanmac-family") {
return true
}
parentID = parent.ParentID
}
return false
}
func validateTenantOrgConfigScope(tenant domain.Tenant, tenants []domain.Tenant, config domain.JSONMap) error {
if !hasTenantOrgConfig(config) {
return nil
}
if isHanmacFamilyDescendantTenant(tenant, tenants) {
return nil
}
return fmt.Errorf("tenant org config is allowed only hanmac-family descendants")
}
func tenantVisibility(config domain.JSONMap) string {
visibility, _ := config["visibility"].(string)
switch strings.ToLower(strings.TrimSpace(visibility)) {
case "internal":
return "internal"
case "private":
return "private"
default:
return "public"
}
}
func filterPublicTenants(tenants []domain.Tenant) []domain.Tenant {
excludedIDs := make(map[string]bool)
for _, tenant := range tenants {
visibility := tenantVisibility(tenant.Config)
if visibility == "internal" || visibility == "private" {
excludedIDs[tenant.ID] = true
}
}
changed := true
for changed {
changed = false
for _, tenant := range tenants {
if tenant.ParentID != nil && excludedIDs[*tenant.ParentID] && !excludedIDs[tenant.ID] {
excludedIDs[tenant.ID] = true
changed = true
}
}
}
filtered := make([]domain.Tenant, 0, len(tenants))
for _, tenant := range tenants {
if !excludedIDs[tenant.ID] {
filtered = append(filtered, tenant)
}
}
return filtered
}
func normalizeTenantUserSchema(value any) ([]any, error) {
if value == nil {
return nil, nil
@@ -1023,6 +1159,15 @@ func (h *TenantHandler) CreateTenant(c *fiber.Ctx) error {
if err != nil {
return errorJSON(c, fiber.StatusBadRequest, err.Error())
}
var tenants []domain.Tenant
if hasTenantOrgConfig(config) {
if err := h.DB.Find(&tenants).Error; err != nil {
return errorJSON(c, fiber.StatusInternalServerError, err.Error())
}
if err := validateTenantOrgConfigScope(*tenant, tenants, config); err != nil {
return errorJSON(c, fiber.StatusBadRequest, err.Error())
}
}
tenant.Config = config
h.DB.Save(tenant)
summary.Config = tenant.Config
@@ -1162,6 +1307,15 @@ func (h *TenantHandler) UpdateTenant(c *fiber.Ctx) error {
if err != nil {
return errorJSON(c, fiber.StatusBadRequest, err.Error())
}
var tenants []domain.Tenant
if hasTenantOrgConfig(config) {
if err := h.DB.Find(&tenants).Error; err != nil {
return errorJSON(c, fiber.StatusInternalServerError, err.Error())
}
if err := validateTenantOrgConfigScope(tenant, tenants, config); err != nil {
return errorJSON(c, fiber.StatusBadRequest, err.Error())
}
}
tenant.Config = config
}
@@ -1696,10 +1850,13 @@ func (h *TenantHandler) GetPublicOrgChart(c *fiber.Ctx) error {
for _, t := range allTenants {
if findRoot(t.ID) == sharedRootID {
filteredTenants = append(filteredTenants, t)
tenantIDs = append(tenantIDs, t.ID)
slugs = append(slugs, t.Slug)
}
}
filteredTenants = filterPublicTenants(filteredTenants)
for _, t := range filteredTenants {
tenantIDs = append(tenantIDs, t.ID)
slugs = append(slugs, t.Slug)
}
type publicUserSummary struct {
ID string `json:"id"`