13c53fea0a
- 管理员可管理任意用户笔记(读取/修改/删除/回收站/标签/图谱/FTS 全平台) - 普通用户仍数据隔离, 越权返回404 - 新增注册开关: 管理员后台⚙设置可开/关, 支持REGISTRATION_ENABLED环境变量 - 注册关闭时前台/登录页隐藏注册入口, 注册接口返回400 - 冒烟测试扩展到91用例全过
136 lines
3.5 KiB
Go
136 lines
3.5 KiB
Go
package service
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
|
|
"note-manager/model"
|
|
"note-manager/repository"
|
|
)
|
|
|
|
// UserService 用户/账号业务逻辑(多租户认证)
|
|
type UserService struct {
|
|
userRepo *repository.UserRepository
|
|
// registrationEnabled 以 env 为准的注册默认开关;DB 设置项可覆盖(运行时切换)
|
|
registrationDefault string
|
|
}
|
|
|
|
// NewUserService 创建用户服务
|
|
func NewUserService(userRepo *repository.UserRepository) *UserService {
|
|
return &UserService{userRepo: userRepo, registrationDefault: "true"}
|
|
}
|
|
|
|
// SetRegistrationDefault 设置注册开关的默认值(来自环境变量)
|
|
func (s *UserService) SetRegistrationDefault(v string) {
|
|
s.registrationDefault = v
|
|
}
|
|
|
|
// RegistrationEnabled 当前注册开关是否开启
|
|
func (s *UserService) RegistrationEnabled() bool {
|
|
val := s.userRepo.GetSetting("registration_enabled", s.registrationDefault)
|
|
return val == "1" || val == "true"
|
|
}
|
|
|
|
// SetRegistrationEnabled 切换注册开关(持久化到 DB)
|
|
func (s *UserService) SetRegistrationEnabled(on bool) error {
|
|
v := "false"
|
|
if on {
|
|
v = "true"
|
|
}
|
|
return s.userRepo.SetSetting("registration_enabled", v)
|
|
}
|
|
|
|
// Register 注册新用户
|
|
// 说明:首个注册的用户自动成为 admin(拥有平台管理权限);其余为普通 user。
|
|
// 同时把历史遗留(user_id=0)的笔记迁移给首位注册用户。
|
|
func (s *UserService) Register(username, password, displayName string) (*model.User, error) {
|
|
username = strings.TrimSpace(strings.ToLower(username))
|
|
displayName = strings.TrimSpace(displayName)
|
|
if username == "" {
|
|
return nil, errors.New("用户名不能为空")
|
|
}
|
|
if len(password) < 6 {
|
|
return nil, errors.New("密码至少 6 位")
|
|
}
|
|
if !s.RegistrationEnabled() {
|
|
return nil, errors.New("注册功能已关闭")
|
|
}
|
|
if _, err := s.userRepo.GetByUsername(username); err == nil {
|
|
return nil, errors.New("用户名已存在")
|
|
}
|
|
|
|
count, err := s.userRepo.Count()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
role := "user"
|
|
if count == 0 {
|
|
role = "admin"
|
|
}
|
|
if displayName == "" {
|
|
displayName = username
|
|
}
|
|
|
|
u := &model.User{
|
|
Username: username,
|
|
PasswordHash: model.HashPassword(password),
|
|
DisplayName: displayName,
|
|
Role: role,
|
|
}
|
|
if err := s.userRepo.Create(u); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 首位用户:接管历史遗留(user_id=0)的笔记
|
|
if role == "admin" {
|
|
if err := s.userRepo.GetDB().Model(&model.Note{}).
|
|
Where("user_id = ?", 0).Update("user_id", u.ID).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return u, nil
|
|
}
|
|
|
|
// IsAdmin 判断用户是否为管理员
|
|
func (s *UserService) IsAdmin(userID uint) bool {
|
|
if userID == 0 {
|
|
return false
|
|
}
|
|
u, err := s.userRepo.GetByID(userID)
|
|
if err != nil || u == nil {
|
|
return false
|
|
}
|
|
return u.Role == "admin"
|
|
}
|
|
|
|
// GetRole 返回用户角色(admin/user/空)
|
|
func (s *UserService) GetRole(userID uint) string {
|
|
if userID == 0 {
|
|
return ""
|
|
}
|
|
u, err := s.userRepo.GetByID(userID)
|
|
if err != nil || u == nil {
|
|
return ""
|
|
}
|
|
return u.Role
|
|
}
|
|
|
|
// Login 校验用户名密码,返回用户
|
|
func (s *UserService) Login(username, password string) (*model.User, error) {
|
|
username = strings.TrimSpace(strings.ToLower(username))
|
|
u, err := s.userRepo.GetByUsername(username)
|
|
if err != nil {
|
|
return nil, errors.New("用户名或密码错误")
|
|
}
|
|
ok, _ := model.CheckPassword(password, u.PasswordHash)
|
|
if !ok {
|
|
return nil, errors.New("用户名或密码错误")
|
|
}
|
|
return u, nil
|
|
}
|
|
|
|
// GetByID 获取用户信息
|
|
func (s *UserService) GetByID(id uint) (*model.User, error) {
|
|
return s.userRepo.GetByID(id)
|
|
}
|