feat: 方案100%匹配 — RBAC动态权限+Grafana监控+目录库独立微服务+全国目录中心+标识同步
This commit is contained in:
@@ -0,0 +1,265 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/tcs-iptv/tcs/internal/model"
|
||||
)
|
||||
|
||||
// AdminService 用户管理/组织管理服务(系统管理模块补足)。
|
||||
// MVP 使用内存存储,生产环境可替换为 PostgreSQL 实现。
|
||||
type AdminService struct {
|
||||
mu sync.RWMutex
|
||||
users map[string]*model.User // userID -> User
|
||||
orgs map[string]*model.Organization // orgID -> Organization
|
||||
keys map[string]*model.User // apiKey -> User(快速鉴权查找)
|
||||
names map[string]bool // username 唯一性校验
|
||||
}
|
||||
|
||||
// NewAdminService 创建用户/组织管理服务。
|
||||
func NewAdminService() *AdminService {
|
||||
return &AdminService{
|
||||
users: make(map[string]*model.User),
|
||||
orgs: make(map[string]*model.Organization),
|
||||
keys: make(map[string]*model.User),
|
||||
names: make(map[string]bool),
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 组织管理 ----
|
||||
|
||||
// CreateOrg 创建组织。
|
||||
func (a *AdminService) CreateOrg(org model.Organization) (model.Organization, error) {
|
||||
if org.Name == "" {
|
||||
return model.Organization{}, fmt.Errorf("admin: 组织名称不能为空")
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
org.ID = a.nextID("org")
|
||||
org.Status = model.OrgStatusActive
|
||||
if org.CreatedAt.IsZero() {
|
||||
org.CreatedAt = time.Now()
|
||||
}
|
||||
org.UpdatedAt = time.Now()
|
||||
a.orgs[org.ID] = &org
|
||||
return org, nil
|
||||
}
|
||||
|
||||
// UpdateOrg 更新组织信息。
|
||||
func (a *AdminService) UpdateOrg(orgID string, updates model.Organization) (model.Organization, error) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
org, ok := a.orgs[orgID]
|
||||
if !ok {
|
||||
return model.Organization{}, fmt.Errorf("admin: 组织 %s 不存在", orgID)
|
||||
}
|
||||
if updates.Name != "" {
|
||||
org.Name = updates.Name
|
||||
}
|
||||
if updates.OrgNode != "" {
|
||||
org.OrgNode = updates.OrgNode
|
||||
}
|
||||
if updates.Province != "" {
|
||||
org.Province = updates.Province
|
||||
}
|
||||
if updates.Type != "" {
|
||||
org.Type = updates.Type
|
||||
}
|
||||
if updates.Status != "" {
|
||||
org.Status = updates.Status
|
||||
}
|
||||
org.UpdatedAt = time.Now()
|
||||
return *org, nil
|
||||
}
|
||||
|
||||
// GetOrg 查询组织详情。
|
||||
func (a *AdminService) GetOrg(orgID string) (model.Organization, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
org, ok := a.orgs[orgID]
|
||||
if !ok {
|
||||
return model.Organization{}, fmt.Errorf("admin: 组织 %s 不存在", orgID)
|
||||
}
|
||||
return *org, nil
|
||||
}
|
||||
|
||||
// ListOrgs 列出全部组织(可按类型过滤)。
|
||||
func (a *AdminService) ListOrgs(orgType string) []model.Organization {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
var out []model.Organization
|
||||
for _, org := range a.orgs {
|
||||
if orgType == "" || org.Type == orgType {
|
||||
out = append(out, *org)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// DisableOrg 禁用组织。
|
||||
func (a *AdminService) DisableOrg(orgID string) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
org, ok := a.orgs[orgID]
|
||||
if !ok {
|
||||
return fmt.Errorf("admin: 组织 %s 不存在", orgID)
|
||||
}
|
||||
org.Status = model.OrgStatusDisabled
|
||||
org.UpdatedAt = time.Now()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ---- 用户管理 ----
|
||||
|
||||
// CreateUser 创建用户并自动生成 API Key/Secret。
|
||||
func (a *AdminService) CreateUser(user model.User) (model.User, error) {
|
||||
if user.Username == "" {
|
||||
return model.User{}, fmt.Errorf("admin: 用户名不能为空")
|
||||
}
|
||||
if user.OrgID == "" {
|
||||
return model.User{}, fmt.Errorf("admin: 所属组织不能为空")
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if a.names[user.Username] {
|
||||
return model.User{}, fmt.Errorf("admin: 用户名 %s 已存在", user.Username)
|
||||
}
|
||||
if _, ok := a.orgs[user.OrgID]; !ok {
|
||||
return model.User{}, fmt.Errorf("admin: 组织 %s 不存在", user.OrgID)
|
||||
}
|
||||
user.ID = a.nextID("user")
|
||||
user.APIKey = a.generateAPIKey()
|
||||
user.APISecret = a.generateAPISecret()
|
||||
user.Status = model.UserStatusActive
|
||||
if user.CreatedAt.IsZero() {
|
||||
user.CreatedAt = time.Now()
|
||||
}
|
||||
user.UpdatedAt = time.Now()
|
||||
a.users[user.ID] = &user
|
||||
a.keys[user.APIKey] = &user
|
||||
a.names[user.Username] = true
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// UpdateUser 更新用户信息(不支持修改用户名和 API Key)。
|
||||
func (a *AdminService) UpdateUser(userID string, updates model.User) (model.User, error) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
user, ok := a.users[userID]
|
||||
if !ok {
|
||||
return model.User{}, fmt.Errorf("admin: 用户 %s 不存在", userID)
|
||||
}
|
||||
if updates.FullName != "" {
|
||||
user.FullName = updates.FullName
|
||||
}
|
||||
if updates.OrgID != "" {
|
||||
if _, ok := a.orgs[updates.OrgID]; !ok {
|
||||
return model.User{}, fmt.Errorf("admin: 组织 %s 不存在", updates.OrgID)
|
||||
}
|
||||
user.OrgID = updates.OrgID
|
||||
}
|
||||
if updates.Role != "" {
|
||||
user.Role = updates.Role
|
||||
}
|
||||
if updates.Status != "" {
|
||||
user.Status = updates.Status
|
||||
}
|
||||
user.UpdatedAt = time.Now()
|
||||
return *user, nil
|
||||
}
|
||||
|
||||
// GetUser 查询用户详情。
|
||||
func (a *AdminService) GetUser(userID string) (model.User, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
user, ok := a.users[userID]
|
||||
if !ok {
|
||||
return model.User{}, fmt.Errorf("admin: 用户 %s 不存在", userID)
|
||||
}
|
||||
return *user, nil
|
||||
}
|
||||
|
||||
// ListUsers 列出全部用户(可按组织或角色过滤)。
|
||||
func (a *AdminService) ListUsers(orgID, role string) []model.User {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
var out []model.User
|
||||
for _, user := range a.users {
|
||||
if orgID != "" && user.OrgID != orgID {
|
||||
continue
|
||||
}
|
||||
if role != "" && user.Role != role {
|
||||
continue
|
||||
}
|
||||
out = append(out, *user)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// DisableUser 禁用用户。
|
||||
func (a *AdminService) DisableUser(userID string) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
user, ok := a.users[userID]
|
||||
if !ok {
|
||||
return fmt.Errorf("admin: 用户 %s 不存在", userID)
|
||||
}
|
||||
user.Status = model.UserStatusDisabled
|
||||
user.UpdatedAt = time.Now()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResetAPIKey 重置用户的 API Key 和 Secret。
|
||||
func (a *AdminService) ResetAPIKey(userID string) (model.User, error) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
user, ok := a.users[userID]
|
||||
if !ok {
|
||||
return model.User{}, fmt.Errorf("admin: 用户 %s 不存在", userID)
|
||||
}
|
||||
// 移除旧 key
|
||||
delete(a.keys, user.APIKey)
|
||||
// 生成新 key
|
||||
user.APIKey = a.generateAPIKey()
|
||||
user.APISecret = a.generateAPISecret()
|
||||
user.UpdatedAt = time.Now()
|
||||
a.keys[user.APIKey] = user
|
||||
return *user, nil
|
||||
}
|
||||
|
||||
// LookupByAPIKey 根据 API Key 查询用户(供 httpx.KeyStore 使用)。
|
||||
func (a *AdminService) LookupByAPIKey(apiKey string) (secret string, role string, ok bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
user, exists := a.keys[apiKey]
|
||||
if !exists || user.Status != model.UserStatusActive {
|
||||
return "", "", false
|
||||
}
|
||||
return user.APISecret, user.Role, true
|
||||
}
|
||||
|
||||
// ---- 辅助方法 ----
|
||||
|
||||
func (a *AdminService) nextID(prefix string) string {
|
||||
// 使用时间戳+随机数生成唯一 ID
|
||||
ts := time.Now().Format("20060102")
|
||||
b := make([]byte, 4)
|
||||
rand.Read(b)
|
||||
return fmt.Sprintf("%s-%s-%s", prefix, ts, hex.EncodeToString(b))
|
||||
}
|
||||
|
||||
func (a *AdminService) generateAPIKey() string {
|
||||
b := make([]byte, 16)
|
||||
rand.Read(b)
|
||||
return "tcs-" + hex.EncodeToString(b)
|
||||
}
|
||||
|
||||
func (a *AdminService) generateAPISecret() string {
|
||||
b := make([]byte, 32)
|
||||
rand.Read(b)
|
||||
return hex.EncodeToString(b)
|
||||
}
|
||||
Reference in New Issue
Block a user