feat: 优化web
This commit is contained in:
@@ -0,0 +1,261 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"giter.top/smart/internal/iam/entity"
|
||||
"giter.top/smart/internal/iam/repository"
|
||||
"giter.top/smart/pkg/utils/id"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// TenantService 租户
|
||||
type TenantService interface {
|
||||
Create(ctx context.Context, req *CreateTenantRequest) (*entity.Tenant, error)
|
||||
Update(ctx context.Context, id string, req *UpdateTenantRequest) (*entity.Tenant, error)
|
||||
Delete(ctx context.Context, ids []string) error
|
||||
Get(ctx context.Context, id string) (*entity.Tenant, error)
|
||||
List(ctx context.Context, name, code string, status *int16, page, pageSize int) (*TenantListResponse, error)
|
||||
}
|
||||
|
||||
type CreateTenantRequest struct {
|
||||
TenantCode string `json:"tenant_code" binding:"required,max=64"`
|
||||
TenantName string `json:"tenant_name" binding:"required,max=128"`
|
||||
AdminUserName string `json:"admin_user_name" binding:"required,max=64"`
|
||||
AdminPassword string `json:"admin_password" binding:"required,min=6,max=64"`
|
||||
AdminRealName string `json:"admin_real_name" binding:"max=64"`
|
||||
}
|
||||
|
||||
type UpdateTenantRequest struct {
|
||||
TenantName *string `json:"tenant_name"`
|
||||
TenantCode *string `json:"tenant_code" binding:"omitempty,max=64"`
|
||||
Status *int16 `json:"status"`
|
||||
ExpireTime *string `json:"expire_time"` // RFC3339
|
||||
}
|
||||
|
||||
type TenantListItem struct {
|
||||
entity.Tenant
|
||||
UserCount int64 `json:"user_count"`
|
||||
DeptCount int64 `json:"dept_count"`
|
||||
}
|
||||
|
||||
type TenantListResponse struct {
|
||||
Items []TenantListItem `json:"items"`
|
||||
Total int64 `json:"total"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"page_size"`
|
||||
TotalPages int `json:"total_pages"`
|
||||
}
|
||||
|
||||
type tenantService struct {
|
||||
db *gorm.DB
|
||||
tenants repository.TenantRepository
|
||||
depts repository.DeptRepository
|
||||
users repository.UserRepository
|
||||
roles repository.RoleRepository
|
||||
menus repository.MenuRepository
|
||||
}
|
||||
|
||||
func NewTenantService(
|
||||
db *gorm.DB,
|
||||
tenants repository.TenantRepository,
|
||||
depts repository.DeptRepository,
|
||||
users repository.UserRepository,
|
||||
roles repository.RoleRepository,
|
||||
menus repository.MenuRepository,
|
||||
) TenantService {
|
||||
return &tenantService{db: db, tenants: tenants, depts: depts, users: users, roles: roles, menus: menus}
|
||||
}
|
||||
|
||||
func (s *tenantService) Create(ctx context.Context, req *CreateTenantRequest) (*entity.Tenant, error) {
|
||||
ok, err := s.tenants.ExistsCode(ctx, req.TenantCode, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ok {
|
||||
return nil, fmt.Errorf("租户编码已存在")
|
||||
}
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(req.AdminPassword), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out *entity.Tenant
|
||||
err = s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
t := &entity.Tenant{
|
||||
ID: id.New(),
|
||||
TenantCode: req.TenantCode,
|
||||
TenantName: req.TenantName,
|
||||
Status: 1,
|
||||
}
|
||||
if err := tx.Create(t).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
var ucount int64
|
||||
if err := tx.Model(&entity.User{}).Where("tenant_id = ? AND user_name = ?", t.ID, req.AdminUserName).Count(&ucount).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if ucount > 0 {
|
||||
return fmt.Errorf("管理员账号已存在")
|
||||
}
|
||||
root := &entity.Dept{
|
||||
ID: id.New(),
|
||||
TenantID: t.ID,
|
||||
ParentID: "",
|
||||
DeptName: req.TenantName,
|
||||
SortOrder: 0,
|
||||
Status: 1,
|
||||
}
|
||||
if err := tx.Create(root).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
path := fmt.Sprintf("/%s/", root.ID)
|
||||
if err := tx.Model(&entity.Dept{}).Where("id = ?", root.ID).Update("dept_path", path).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
admin := &entity.User{
|
||||
ID: id.New(),
|
||||
TenantID: t.ID,
|
||||
DeptID: &root.ID,
|
||||
UserName: req.AdminUserName,
|
||||
RealName: req.AdminRealName,
|
||||
PasswordHash: string(hash),
|
||||
Status: 1,
|
||||
}
|
||||
if err := tx.Create(admin).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&entity.UserDept{ID: id.New(), UserID: admin.ID, DeptID: root.ID, IsPrimary: true}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
role := &entity.Role{
|
||||
ID: id.New(),
|
||||
TenantID: t.ID,
|
||||
RoleCode: DefaultTenantAdminRoleCode,
|
||||
RoleName: "超级管理员",
|
||||
DataScope: entity.DataScopeAll,
|
||||
Description: "租户初始化角色",
|
||||
IsBuiltin: true,
|
||||
Status: 1,
|
||||
}
|
||||
if err := tx.Create(role).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
var allMenus []entity.Menu
|
||||
if err := tx.Find(&allMenus).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
for _, m := range allMenus {
|
||||
if err := tx.Create(&entity.RoleMenu{ID: id.New(), RoleID: role.ID, MenuID: m.ID}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := tx.Create(&entity.UserRole{ID: id.New(), UserID: admin.ID, RoleID: role.ID}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
aid := admin.ID
|
||||
if err := tx.Model(t).Update("admin_user_id", aid).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
t.AdminUserID = &aid
|
||||
out = t
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s *tenantService) Update(ctx context.Context, id string, req *UpdateTenantRequest) (*entity.Tenant, error) {
|
||||
t, err := s.tenants.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
if errors.Is(err, repository.ErrNotFound) {
|
||||
return nil, fmt.Errorf("租户不存在")
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if req.TenantName != nil && *req.TenantName != "" {
|
||||
t.TenantName = *req.TenantName
|
||||
if root, err := s.depts.FindRoot(ctx, t.ID); err == nil {
|
||||
root.DeptName = *req.TenantName
|
||||
_ = s.depts.Update(ctx, root)
|
||||
}
|
||||
}
|
||||
if req.TenantCode != nil && *req.TenantCode != "" {
|
||||
ok, err := s.tenants.ExistsCode(ctx, *req.TenantCode, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ok {
|
||||
return nil, fmt.Errorf("租户编码已存在")
|
||||
}
|
||||
t.TenantCode = *req.TenantCode
|
||||
}
|
||||
if req.Status != nil {
|
||||
t.Status = *req.Status
|
||||
}
|
||||
if req.ExpireTime != nil && *req.ExpireTime != "" {
|
||||
et, err := time.Parse(time.RFC3339, *req.ExpireTime)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("到期时间格式无效: %w", err)
|
||||
}
|
||||
t.ExpireTime = &et
|
||||
if et.Before(time.Now()) {
|
||||
t.Status = 0
|
||||
}
|
||||
}
|
||||
if err := s.tenants.Update(ctx, t); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (s *tenantService) Delete(ctx context.Context, ids []string) error {
|
||||
for _, tid := range ids {
|
||||
n, err := s.tenants.CountUsers(ctx, tid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n > 0 {
|
||||
return fmt.Errorf("租户 %s 仍存在用户,无法删除", tid)
|
||||
}
|
||||
}
|
||||
for _, tid := range ids {
|
||||
if err := s.db.WithContext(ctx).Delete(&entity.Tenant{}, "id = ?", tid).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *tenantService) Get(ctx context.Context, id string) (*entity.Tenant, error) {
|
||||
return s.tenants.GetByID(ctx, id)
|
||||
}
|
||||
|
||||
func (s *tenantService) List(ctx context.Context, name, code string, status *int16, page, pageSize int) (*TenantListResponse, error) {
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 {
|
||||
pageSize = 10
|
||||
}
|
||||
rows, total, err := s.tenants.List(ctx, name, code, status, page, pageSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items := make([]TenantListItem, 0, len(rows))
|
||||
for _, t := range rows {
|
||||
uc, _ := s.tenants.CountUsers(ctx, t.ID)
|
||||
dc, _ := s.tenants.CountDepts(ctx, t.ID)
|
||||
items = append(items, TenantListItem{Tenant: t, UserCount: uc, DeptCount: dc})
|
||||
}
|
||||
tp := int(total) / pageSize
|
||||
if int(total)%pageSize != 0 {
|
||||
tp++
|
||||
}
|
||||
return &TenantListResponse{Items: items, Total: total, Page: page, PageSize: pageSize, TotalPages: tp}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user