2026-05-12 09:53:20 +08:00

184 lines
4.6 KiB
Go

package adminuser
import (
"bytes"
"encoding/csv"
"fmt"
"strings"
"time"
"hyapp-admin-server/internal/config"
"hyapp-admin-server/internal/model"
"hyapp-admin-server/internal/modules/shared"
"hyapp-admin-server/internal/repository"
"hyapp-admin-server/internal/security"
)
const defaultInitialPassword = "Hyapp@123456"
type AdminUserService struct {
store *repository.Store
cfg config.Config
}
type CreateUserInput struct {
Username string
Name string
Team string
Status string
Password string
MFAEnabled bool
RoleIDs []uint
}
type UpdateUserInput struct {
Name *string
Team *string
Status *string
MFAEnabled *bool
RoleIDs *[]uint
}
type UserStatusInput struct {
Status string
}
type BatchUserStatusInput struct {
IDs []uint
Status string
}
type PasswordResult struct {
Password string
}
type UserExport struct {
FileName string
Content []byte
Count int
}
func NewService(store *repository.Store, cfg config.Config) *AdminUserService {
return &AdminUserService{store: store, cfg: cfg}
}
func (s *AdminUserService) ListUsers(actor shared.Actor, options repository.ListOptions) ([]model.User, int64, error) {
access, err := s.store.DataAccessForUser(actor.UserID)
if err != nil {
return nil, 0, err
}
return s.store.ListUsersWithAccess(options, access)
}
func (s *AdminUserService) CreateUser(input CreateUserInput) (*model.User, PasswordResult, error) {
password := strings.TrimSpace(input.Password)
if password == "" {
password = defaultInitialPassword
}
hash, err := security.HashPassword(password)
if err != nil {
return nil, PasswordResult{}, err
}
user := model.User{
Username: strings.TrimSpace(input.Username),
Name: strings.TrimSpace(input.Name),
PasswordHash: hash,
Team: strings.TrimSpace(input.Team),
Status: shared.DefaultString(input.Status, model.UserStatusActive),
MFAEnabled: input.MFAEnabled,
}
if err := s.store.CreateUser(&user, input.RoleIDs); err != nil {
return nil, PasswordResult{}, err
}
created, err := s.store.FindUserByID(user.ID)
if err != nil {
return nil, PasswordResult{}, err
}
return created, PasswordResult{Password: password}, nil
}
func (s *AdminUserService) GetUser(id uint) (*model.User, error) {
return s.store.FindUserByID(id)
}
func (s *AdminUserService) UpdateUser(id uint, input UpdateUserInput) (*model.User, error) {
updates := map[string]any{}
if input.Name != nil {
updates["name"] = strings.TrimSpace(*input.Name)
}
if input.Team != nil {
updates["team"] = strings.TrimSpace(*input.Team)
}
if input.Status != nil {
updates["status"] = strings.TrimSpace(*input.Status)
}
if input.MFAEnabled != nil {
updates["mfa_enabled"] = *input.MFAEnabled
}
return s.store.UpdateUser(id, updates, input.RoleIDs)
}
func (s *AdminUserService) UpdateUserStatus(id uint, input UserStatusInput) (*model.User, error) {
return s.store.UpdateUserStatus(id, input.Status)
}
func (s *AdminUserService) ResetUserPassword(id uint, password string) (PasswordResult, error) {
value := strings.TrimSpace(password)
if value == "" {
value = defaultInitialPassword
}
if err := s.store.ResetUserPassword(id, value); err != nil {
return PasswordResult{}, err
}
return PasswordResult{Password: value}, nil
}
func (s *AdminUserService) BatchUpdateUserStatus(input BatchUserStatusInput) error {
return s.store.BatchUpdateUserStatus(input.IDs, input.Status)
}
func (s *AdminUserService) ExportUsers(actor shared.Actor, options repository.ListOptions) (UserExport, error) {
access, err := s.store.DataAccessForUser(actor.UserID)
if err != nil {
return UserExport{}, err
}
users, err := s.store.ExportUsersWithAccess(options, access)
if err != nil {
return UserExport{}, err
}
var buf bytes.Buffer
writer := csv.NewWriter(&buf)
_ = writer.Write([]string{"ID", "账号", "姓名", "角色", "团队", "状态", "MFA", "最后登录"})
for _, user := range users {
lastLogin := ""
if user.LastLoginAtMS != nil {
lastLogin = time.UnixMilli(*user.LastLoginAtMS).UTC().Format("2006-01-02 15:04:05")
}
_ = writer.Write([]string{
fmt.Sprintf("%d", user.ID),
user.Username,
user.Name,
strings.Join(roleNames(user.Roles), "、"),
user.Team,
user.Status,
shared.BoolText(user.MFAEnabled),
lastLogin,
})
}
writer.Flush()
if err := writer.Error(); err != nil {
return UserExport{}, err
}
return UserExport{FileName: "hyapp-users.csv", Content: buf.Bytes(), Count: len(users)}, nil
}
func roleNames(roles []model.Role) []string {
names := make([]string, 0, len(roles))
for _, role := range roles {
names = append(names, role.Name)
}
return names
}