266 lines
6.8 KiB
Go
266 lines
6.8 KiB
Go
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)
|
|
}
|