feat: 实现 Admin 采购员账号管理 (#51)
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
// Admin 用户管理业务:列表、新增采购员、启停和重置密码。
|
||||
package service
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"cmautobuy/admin/model"
|
||||
"cmautobuy/admin/repository"
|
||||
)
|
||||
|
||||
var ErrAdminRequired = errors.New("只有管理员可以管理用户")
|
||||
|
||||
// UserListResult 是用户管理页的分页结果。
|
||||
type UserListResult struct {
|
||||
Rows []model.User
|
||||
Keyword string
|
||||
Status model.UserStatus
|
||||
Page int
|
||||
Total int
|
||||
TotalPages int
|
||||
}
|
||||
|
||||
// ListUsers 搜索并分页。用户页沿用全站统一的每页 20 条规则。
|
||||
func ListUsers(db *sql.DB, actor *model.User, keyword string, status model.UserStatus, requestedPage int) (*UserListResult, error) {
|
||||
if actor == nil || !actor.IsAdmin() {
|
||||
return nil, ErrAdminRequired
|
||||
}
|
||||
keyword = strings.TrimSpace(keyword)
|
||||
if status != model.UserActive && status != model.UserDisabled {
|
||||
status = ""
|
||||
}
|
||||
page := requestedPage
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
rows, total, err := repository.ListUsers(db, keyword, status, PageSize, (page-1)*PageSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
totalPages := TotalPages(total)
|
||||
clampedPage := ClampPage(page, totalPages)
|
||||
if clampedPage != page {
|
||||
page = clampedPage
|
||||
rows, _, err = repository.ListUsers(db, keyword, status, PageSize, (page-1)*PageSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &UserListResult{
|
||||
Rows: rows, Keyword: keyword, Status: status,
|
||||
Page: page, Total: total, TotalPages: totalPages,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreatePurchaser 创建固定 purchaser 角色的账号,不提供第二管理员入口。
|
||||
func CreatePurchaser(db *sql.DB, actor *model.User, username, password, confirmation string, now time.Time) error {
|
||||
if actor == nil || !actor.IsAdmin() {
|
||||
return ErrAdminRequired
|
||||
}
|
||||
username = strings.TrimSpace(username)
|
||||
if err := validateUsername(username); err != nil {
|
||||
return err
|
||||
}
|
||||
hash, err := hashConfirmedPassword(password, confirmation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userID, err := randomID("USR-", 16)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
at := now.UTC().Format(model.TimeLayout)
|
||||
return repository.CreateUser(db, model.User{
|
||||
UserID: userID, Username: username, PasswordHash: string(hash),
|
||||
Role: model.RolePurchaser, Status: model.UserActive,
|
||||
PasswordChangedAt: at, CreatedAt: at, UpdatedAt: at,
|
||||
})
|
||||
}
|
||||
|
||||
// ChangeUserStatus 启用或禁用账号。数据库事务负责最后管理员保护和 Session 撤销。
|
||||
func ChangeUserStatus(db *sql.DB, actor *model.User, userID string, status model.UserStatus, now time.Time) error {
|
||||
if actor == nil || !actor.IsAdmin() {
|
||||
return ErrAdminRequired
|
||||
}
|
||||
if status != model.UserActive && status != model.UserDisabled {
|
||||
return invalidInput("账号状态无效")
|
||||
}
|
||||
if strings.TrimSpace(userID) == "" {
|
||||
return invalidInput("用户编号不能为空")
|
||||
}
|
||||
return repository.SetUserStatusAndRevokeSessions(
|
||||
db, userID, status, now.UTC().Format(model.TimeLayout))
|
||||
}
|
||||
|
||||
// ResetUserPassword 更新密码哈希,并在同一事务中撤销目标用户全部 Session。
|
||||
func ResetUserPassword(db *sql.DB, actor *model.User, userID, password, confirmation string, now time.Time) error {
|
||||
if actor == nil || !actor.IsAdmin() {
|
||||
return ErrAdminRequired
|
||||
}
|
||||
if strings.TrimSpace(userID) == "" {
|
||||
return invalidInput("用户编号不能为空")
|
||||
}
|
||||
hash, err := hashConfirmedPassword(password, confirmation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return repository.ResetUserPasswordAndRevokeSessions(
|
||||
db, userID, string(hash), now.UTC().Format(model.TimeLayout))
|
||||
}
|
||||
Reference in New Issue
Block a user