package auth import ( "context" "errors" "fmt" "strings" "sync" "time" "chat-agent-system/internal/config" ) // ErrUsernameExists 用户名或 account_id 唯一约束冲突。调用方应返回 400,而不是基础设施 500。 var ErrUsernameExists = errors.New("用户名已存在") // AuthStore 用户与认证配置存储接口 type AuthStore interface { GetUserByUsername(ctx context.Context, username string) (*UserAccount, error) GetUserByID(ctx context.Context, id int64) (*UserAccount, error) CreateUser(ctx context.Context, user *UserAccount) (*UserAccount, error) UpdateUser(ctx context.Context, user *UserAccount, actorID int64) (*UserAccount, error) UpdateUserRole(ctx context.Context, id int64, roleID, position string) (*UserAccount, error) UpdateUserStatus(ctx context.Context, id int64, status AccountStatus) (*UserAccount, error) UpdateUserPassword(ctx context.Context, id int64, passwordHash string, mustChangePassword bool) (*UserAccount, error) RecordPasswordResetAudit(ctx context.Context, targetUserID, actorUserID int64, requestID string) error ListUsers(ctx context.Context) ([]*UserAccount, error) ListActiveUserDirectory(ctx context.Context) ([]UserDirectoryEntry, error) GetConfig(ctx context.Context) (SystemAuthConfig, error) UpdateConfig(ctx context.Context, cfg SystemAuthConfig) error } // MemoryStore 内存与并发安全存储实现 type MemoryStore struct { mu sync.RWMutex users map[int64]*UserAccount nextID int64 cfg SystemAuthConfig } // NewMemoryStore 初始化并载入初始系统管理员与基线用户 func NewMemoryStore(cfg *config.Config) *MemoryStore { store := &MemoryStore{ users: make(map[int64]*UserAccount), nextID: 1, cfg: SystemAuthConfig{ AllowRegistration: true, RequireApproval: false, }, } if cfg != nil { store.cfg.AllowRegistration = cfg.AuthRegistrationEnabled store.cfg.RequireApproval = cfg.AuthRequireApproval } now := time.Now().Format("2006-01-02 15:04:05") // 1. 初始化 .env 系统超级管理员 adminUser := "admin" adminPass := "admin-secret-password-123" if cfg != nil && cfg.AdminUsername != "" { adminUser = cfg.AdminUsername } if cfg != nil && cfg.AdminPassword != "" { adminPass = cfg.AdminPassword } adminHash, _ := HashPassword(adminPass) store.users[1] = &UserAccount{ ID: 1, Username: adminUser, Name: "系统管理员", PasswordHash: adminHash, Status: StatusActive, MustChangePassword: false, RoleID: "role-admin", Position: "系统管理员", Department: "系统运维部", EmployeeID: "SYS-000", CreatedAt: now, UpdatedAt: now, } store.nextID = 2 // 2. 标准测试用户仅 dev/test 环境种子化;生产不得携带固定密码演示账号 env := "" if cfg != nil { env = cfg.Env } if env == "dev" || env == "test" || env == "" { defaultPassHash, _ := HashPassword("123456") seedUsers := []struct { id int64 username string name string roleID string position string dept string employeeID string }{ {2, "zhanggong", "张工", "role-tech-lead", "技术主管", "骨科耗材研发组", "RD-001"}, {3, "lizhuguan", "李主管", "role-tech-lead", "技术主管", "骨科耗材研发组", "RD-002"}, {4, "wangzong", "王总监", "role-director", "研发总监", "研发中心", "RD-003"}, {5, "guest", "访客", "role-guest", "待指派", "外协中心", "EXT-999"}, } for _, su := range seedUsers { store.users[su.id] = &UserAccount{ ID: su.id, Username: su.username, Name: su.name, PasswordHash: defaultPassHash, Status: StatusActive, MustChangePassword: false, RoleID: su.roleID, Position: su.position, Department: su.dept, EmployeeID: su.employeeID, CreatedAt: now, UpdatedAt: now, } if su.id >= store.nextID { store.nextID = su.id + 1 } } } return store } // GetUserByUsername 根据用户名查询 func (s *MemoryStore) GetUserByUsername(ctx context.Context, username string) (*UserAccount, error) { s.mu.RLock() defer s.mu.RUnlock() for _, u := range s.users { if strings.EqualFold(u.Username, username) { copy := *u return ©, nil } } return nil, fmt.Errorf("user not found: %s", username) } // GetUserByID 根据用户ID查询 func (s *MemoryStore) GetUserByID(ctx context.Context, id int64) (*UserAccount, error) { s.mu.RLock() defer s.mu.RUnlock() u, exists := s.users[id] if !exists { return nil, fmt.Errorf("user not found: %d", id) } copy := *u return ©, nil } // CreateUser 创建新用户账号 func (s *MemoryStore) CreateUser(ctx context.Context, user *UserAccount) (*UserAccount, error) { s.mu.Lock() defer s.mu.Unlock() if user == nil || strings.TrimSpace(user.PasswordHash) == "" { return nil, fmt.Errorf("password hash required") } for _, existing := range s.users { if strings.EqualFold(existing.Username, user.Username) { return nil, ErrUsernameExists } } user.ID = s.nextID s.nextID++ now := time.Now().Format("2006-01-02 15:04:05") if user.CreatedAt == "" { user.CreatedAt = now } user.UpdatedAt = now copied := *user s.users[user.ID] = &copied result := *user return &result, nil } // RecordPasswordResetAudit records an admin password reset in memory (no persistence). func (s *MemoryStore) RecordPasswordResetAudit(ctx context.Context, targetUserID, actorUserID int64, requestID string) error { return nil } // UpdateUser 更新用户基础资料;actorID==user.ID 时清除管理员代改标记。 func (s *MemoryStore) UpdateUser(ctx context.Context, user *UserAccount, actorID int64) (*UserAccount, error) { s.mu.Lock() defer s.mu.Unlock() existing, ok := s.users[user.ID] if !ok { return nil, fmt.Errorf("user not found: %d", user.ID) } existing.Name = user.Name existing.Department = user.Department existing.EmployeeID = user.EmployeeID existing.Email = user.Email existing.Avatar = user.Avatar existing.Locale = user.Locale existing.Timezone = user.Timezone now := time.Now().Format("2006-01-02 15:04:05") existing.UpdatedAt = now if actorID > 0 && actorID != user.ID { existing.ProfileUpdatedBy = actorID existing.ProfileUpdatedAt = now } else { existing.ProfileUpdatedBy = 0 existing.ProfileUpdatedAt = "" } copy := *existing return ©, nil } // UpdateUserRole 更新用户职位与全局角色 func (s *MemoryStore) UpdateUserRole(ctx context.Context, id int64, roleID, position string) (*UserAccount, error) { s.mu.Lock() defer s.mu.Unlock() existing, ok := s.users[id] if !ok { return nil, fmt.Errorf("user not found: %d", id) } existing.RoleID = roleID existing.Position = position existing.UpdatedAt = time.Now().Format("2006-01-02 15:04:05") copy := *existing return ©, nil } // UpdateUserStatus 更新用户账号状态 func (s *MemoryStore) UpdateUserStatus(ctx context.Context, id int64, status AccountStatus) (*UserAccount, error) { s.mu.Lock() defer s.mu.Unlock() existing, ok := s.users[id] if !ok { return nil, fmt.Errorf("user not found: %d", id) } existing.Status = status existing.UpdatedAt = time.Now().Format("2006-01-02 15:04:05") copy := *existing return ©, nil } // UpdateUserPassword 更新用户密码并置位/复位首次强制改密 func (s *MemoryStore) UpdateUserPassword(ctx context.Context, id int64, passwordHash string, mustChangePassword bool) (*UserAccount, error) { s.mu.Lock() defer s.mu.Unlock() existing, ok := s.users[id] if !ok { return nil, fmt.Errorf("user not found: %d", id) } existing.PasswordHash = passwordHash existing.MustChangePassword = mustChangePassword existing.UpdatedAt = time.Now().Format("2006-01-02 15:04:05") copy := *existing return ©, nil } // ListActiveUserDirectory 返回 active 用户的轻量目录。 func (s *MemoryStore) ListActiveUserDirectory(ctx context.Context) ([]UserDirectoryEntry, error) { s.mu.RLock() defer s.mu.RUnlock() out := make([]UserDirectoryEntry, 0, len(s.users)) for _, u := range s.users { if u.Status != StatusActive { continue } out = append(out, UserDirectoryEntry{ ID: u.ID, Username: u.Username, Name: u.Name, Department: u.Department, Position: u.Position, }) } // 稳定排序:按姓名再按 id for i := 0; i < len(out); i++ { for j := i + 1; j < len(out); j++ { if out[j].Name < out[i].Name || (out[j].Name == out[i].Name && out[j].ID < out[i].ID) { out[i], out[j] = out[j], out[i] } } } return out, nil } // ListUsers 列出所有用户 func (s *MemoryStore) ListUsers(ctx context.Context) ([]*UserAccount, error) { s.mu.RLock() defer s.mu.RUnlock() list := make([]*UserAccount, 0, len(s.users)) for _, u := range s.users { copy := *u // 脱敏密码哈希 copy.PasswordHash = "" list = append(list, ©) } return list, nil } // GetConfig 获取准入配置 func (s *MemoryStore) GetConfig(ctx context.Context) (SystemAuthConfig, error) { s.mu.RLock() defer s.mu.RUnlock() return s.cfg, nil } // UpdateConfig 更新准入配置 func (s *MemoryStore) UpdateConfig(ctx context.Context, cfg SystemAuthConfig) error { s.mu.Lock() defer s.mu.Unlock() s.cfg = cfg return nil }