feat: 多租户账号体系 + 前台收藏按钮
- 新增 users 表(user_id 数据隔离,bcrypt 密码) - 认证: 注册/登录(用户名+密码)/会话绑定用户, 首个用户成为管理员并接管旧数据 - 数据隔离: 笔记/分类/标签/回收站/版本/草稿/图谱/FTS 全部按用户隔离 - 前台: 登录/注册弹窗, 登录后★收藏自己的笔记, 游客只读公开笔记 - 后台: 用户名+密码登录, 每人管理自己的工作区, 越权访问返回404 - 冒烟测试重构+新增多租户隔离用例(78/78)
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"note-manager/model"
|
||||
"note-manager/repository"
|
||||
)
|
||||
|
||||
// UserService 用户/账号业务逻辑(多租户认证)
|
||||
type UserService struct {
|
||||
userRepo *repository.UserRepository
|
||||
}
|
||||
|
||||
// NewUserService 创建用户服务
|
||||
func NewUserService(userRepo *repository.UserRepository) *UserService {
|
||||
return &UserService{userRepo: userRepo}
|
||||
}
|
||||
|
||||
// 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 _, 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
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
Reference in New Issue
Block a user