205 lines
5.8 KiB
Go
205 lines
5.8 KiB
Go
package tenant
|
||
|
||
import (
|
||
"strings"
|
||
|
||
"quyun/v2/app/errorx"
|
||
"quyun/v2/app/http/tenant/dto"
|
||
"quyun/v2/app/requests"
|
||
"quyun/v2/app/services"
|
||
"quyun/v2/database/models"
|
||
"quyun/v2/pkg/consts"
|
||
|
||
"github.com/gofiber/fiber/v3"
|
||
log "github.com/sirupsen/logrus"
|
||
)
|
||
|
||
// tenantUserAdmin provides tenant-admin member management endpoints.
|
||
//
|
||
// @provider
|
||
type tenantUserAdmin struct{}
|
||
|
||
// adminRemoveUser
|
||
//
|
||
// @Summary 移除租户成员(租户管理)
|
||
// @Tags Tenant
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param tenantCode path string true "Tenant Code"
|
||
// @Param userID path int64 true "UserID"
|
||
// @Success 200 {object} requests.Pager
|
||
//
|
||
// @Router /t/:tenantCode/v1/management/users/:userID [delete]
|
||
// @Bind tenant local key(tenant)
|
||
// @Bind tenantUser local key(tenant_user)
|
||
// @Bind userID path
|
||
func (*tenantUserAdmin) adminRemoveUser(
|
||
ctx fiber.Ctx,
|
||
tenant *models.Tenant,
|
||
tenantUser *models.TenantUser,
|
||
userID int64,
|
||
) error {
|
||
if err := requireTenantAdmin(tenantUser); err != nil {
|
||
return err
|
||
}
|
||
if userID <= 0 {
|
||
return errorx.ErrInvalidParameter.WithMsg("user_id must be > 0")
|
||
}
|
||
|
||
log.WithFields(log.Fields{
|
||
"tenant_id": tenant.ID,
|
||
"operator_user_id": tenantUser.UserID,
|
||
"target_user_id": userID,
|
||
"operator_is_admin": true,
|
||
}).Info("tenant.admin.users.remove")
|
||
|
||
// 关键语义:删除成员接口幂等化;目标用户不属于租户时也返回成功,便于后台重试与批量操作。
|
||
return services.Tenant.RemoveUser(ctx.Context(), tenant.ID, userID)
|
||
}
|
||
|
||
// adminJoinUser
|
||
//
|
||
// @Summary 添加租户成员(租户管理)
|
||
// @Tags Tenant
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param tenantCode path string true "Tenant Code"
|
||
// @Param userID path int64 true "UserID"
|
||
// @Success 200 {object} dto.AdminTenantUserJoinResponse
|
||
//
|
||
// @Router /t/:tenantCode/v1/management/users/:userID/join [post]
|
||
// @Bind tenant local key(tenant)
|
||
// @Bind tenantUser local key(tenant_user)
|
||
// @Bind userID path
|
||
func (*tenantUserAdmin) adminJoinUser(
|
||
ctx fiber.Ctx,
|
||
tenant *models.Tenant,
|
||
tenantUser *models.TenantUser,
|
||
userID int64,
|
||
) (*dto.AdminTenantUserJoinResponse, error) {
|
||
if err := requireTenantAdmin(tenantUser); err != nil {
|
||
return nil, err
|
||
}
|
||
if userID <= 0 {
|
||
return nil, errorx.ErrInvalidParameter.WithMsg("user_id must be > 0")
|
||
}
|
||
|
||
log.WithFields(log.Fields{
|
||
"tenant_id": tenant.ID,
|
||
"operator_user_id": tenantUser.UserID,
|
||
"target_user_id": userID,
|
||
"operator_is_admin": true,
|
||
}).Info("tenant.admin.users.join")
|
||
|
||
// 关键逻辑:以 TenantUser 为准创建成员关系;服务层保证幂等(已存在则不重复创建)。
|
||
if err := services.Tenant.AddUser(ctx.Context(), tenant.ID, userID); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
m, err := services.Tenant.FindTenantUser(ctx.Context(), tenant.ID, userID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return &dto.AdminTenantUserJoinResponse{TenantUser: m}, nil
|
||
}
|
||
|
||
// adminSetUserRole
|
||
//
|
||
// @Summary 设置成员角色(租户管理)
|
||
// @Tags Tenant
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param tenantCode path string true "Tenant Code"
|
||
// @Param userID path int64 true "UserID"
|
||
// @Param form body dto.AdminTenantUserRoleUpdateForm true "Form"
|
||
// @Success 200 {object} dto.AdminTenantUserJoinResponse
|
||
//
|
||
// @Router /t/:tenantCode/v1/management/users/:userID/role [patch]
|
||
// @Bind tenant local key(tenant)
|
||
// @Bind tenantUser local key(tenant_user)
|
||
// @Bind userID path
|
||
// @Bind form body
|
||
func (*tenantUserAdmin) adminSetUserRole(
|
||
ctx fiber.Ctx,
|
||
tenant *models.Tenant,
|
||
tenantUser *models.TenantUser,
|
||
userID int64,
|
||
form *dto.AdminTenantUserRoleUpdateForm,
|
||
) (*dto.AdminTenantUserJoinResponse, error) {
|
||
if err := requireTenantAdmin(tenantUser); err != nil {
|
||
return nil, err
|
||
}
|
||
if userID <= 0 || form == nil {
|
||
return nil, errorx.ErrInvalidParameter
|
||
}
|
||
|
||
roleStr := strings.TrimSpace(form.Role)
|
||
if roleStr == "" {
|
||
return nil, errorx.ErrInvalidParameter.WithMsg("role is required")
|
||
}
|
||
|
||
var role consts.TenantUserRole
|
||
switch roleStr {
|
||
case string(consts.TenantUserRoleMember):
|
||
role = consts.TenantUserRoleMember
|
||
case string(consts.TenantUserRoleTenantAdmin):
|
||
role = consts.TenantUserRoleTenantAdmin
|
||
default:
|
||
return nil, errorx.ErrInvalidParameter.WithMsg("invalid role")
|
||
}
|
||
|
||
log.WithFields(log.Fields{
|
||
"tenant_id": tenant.ID,
|
||
"operator_user_id": tenantUser.UserID,
|
||
"target_user_id": userID,
|
||
"role": role,
|
||
}).Info("tenant.admin.users.set_role")
|
||
|
||
if err := services.Tenant.SetUserRole(ctx.Context(), tenant.ID, userID, role); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
m, err := services.Tenant.FindTenantUser(ctx.Context(), tenant.ID, userID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return &dto.AdminTenantUserJoinResponse{TenantUser: m}, nil
|
||
}
|
||
|
||
// adminTenantUsers
|
||
//
|
||
// @Summary 成员列表(租户管理)
|
||
// @Tags Tenant
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param tenantCode path string true "Tenant Code"
|
||
// @Param filter query dto.AdminTenantUserListFilter true "Filter"
|
||
// @Success 200 {object} requests.Pager{items=dto.AdminTenantUserItem}
|
||
//
|
||
// @Router /t/:tenantCode/v1/management/users [get]
|
||
// @Bind tenant local key(tenant)
|
||
// @Bind tenantUser local key(tenant_user)
|
||
// @Bind filter query
|
||
func (*tenantUserAdmin) adminTenantUsers(
|
||
ctx fiber.Ctx,
|
||
tenant *models.Tenant,
|
||
tenantUser *models.TenantUser,
|
||
filter *dto.AdminTenantUserListFilter,
|
||
) (*requests.Pager, error) {
|
||
if err := requireTenantAdmin(tenantUser); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
log.WithFields(log.Fields{
|
||
"tenant_id": tenant.ID,
|
||
"user_id": tenantUser.UserID,
|
||
"query_uid": filter.UserID,
|
||
"role": filter.Role,
|
||
"status": filter.Status,
|
||
"username": filter.UsernameTrimmed(),
|
||
}).Info("tenant.admin.users.list")
|
||
|
||
// 按 llm.txt 约束:HTTP 层不允许直接查询数据库,全部交由 services 层处理。
|
||
return services.Tenant.AdminTenantUsersPage(ctx.Context(), tenant.ID, filter)
|
||
}
|