tenant: add member management APIs
This commit is contained in:
26
backend/app/http/tenant/dto/tenant_user_admin.go
Normal file
26
backend/app/http/tenant/dto/tenant_user_admin.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"quyun/v2/app/requests"
|
||||
"quyun/v2/database/models"
|
||||
)
|
||||
|
||||
// AdminTenantUserJoinResponse 返回租户管理员添加成员后的结果。
|
||||
type AdminTenantUserJoinResponse struct {
|
||||
// TenantUser 租户成员关系记录。
|
||||
TenantUser *models.TenantUser `json:"tenant_user,omitempty"`
|
||||
}
|
||||
|
||||
// AdminTenantUserRoleUpdateForm 租户成员角色更新表单。
|
||||
type AdminTenantUserRoleUpdateForm struct {
|
||||
// Role 角色:member/tenant_admin。
|
||||
Role string `json:"role,omitempty"`
|
||||
}
|
||||
|
||||
// AdminTenantUserListFilter 租户管理员查询成员列表的过滤条件。
|
||||
type AdminTenantUserListFilter struct {
|
||||
// Pagination 分页参数(page/limit)。
|
||||
requests.Pagination `json:",inline" query:",inline"`
|
||||
// UserID 按用户ID过滤(可选)。
|
||||
UserID *int64 `json:"user_id,omitempty" query:"user_id"`
|
||||
}
|
||||
@@ -60,15 +60,17 @@ func Provide(opts ...opt.Option) error {
|
||||
order *order,
|
||||
orderAdmin *orderAdmin,
|
||||
orderMe *orderMe,
|
||||
tenantUserAdmin *tenantUserAdmin,
|
||||
) (contracts.HttpRoute, error) {
|
||||
obj := &Routes{
|
||||
content: content,
|
||||
contentAdmin: contentAdmin,
|
||||
me: me,
|
||||
middlewares: middlewares,
|
||||
order: order,
|
||||
orderAdmin: orderAdmin,
|
||||
orderMe: orderMe,
|
||||
content: content,
|
||||
contentAdmin: contentAdmin,
|
||||
me: me,
|
||||
middlewares: middlewares,
|
||||
order: order,
|
||||
orderAdmin: orderAdmin,
|
||||
orderMe: orderMe,
|
||||
tenantUserAdmin: tenantUserAdmin,
|
||||
}
|
||||
if err := obj.Prepare(); err != nil {
|
||||
return nil, err
|
||||
@@ -78,5 +80,12 @@ func Provide(opts ...opt.Option) error {
|
||||
}, atom.GroupRoutes); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := container.Container.Provide(func() (*tenantUserAdmin, error) {
|
||||
obj := &tenantUserAdmin{}
|
||||
|
||||
return obj, nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -24,12 +24,13 @@ type Routes struct {
|
||||
log *log.Entry `inject:"false"`
|
||||
middlewares *middlewares.Middlewares
|
||||
// Controller instances
|
||||
content *content
|
||||
contentAdmin *contentAdmin
|
||||
me *me
|
||||
order *order
|
||||
orderAdmin *orderAdmin
|
||||
orderMe *orderMe
|
||||
content *content
|
||||
contentAdmin *contentAdmin
|
||||
me *me
|
||||
order *order
|
||||
orderAdmin *orderAdmin
|
||||
orderMe *orderMe
|
||||
tenantUserAdmin *tenantUserAdmin
|
||||
}
|
||||
|
||||
// Prepare initializes the routes provider with logging configuration.
|
||||
@@ -184,6 +185,29 @@ func (r *Routes) Register(router fiber.Router) {
|
||||
Local[*models.User]("user"),
|
||||
PathParam[int64]("orderID"),
|
||||
))
|
||||
// Register routes for controller: tenantUserAdmin
|
||||
r.log.Debugf("Registering route: Get /t/:tenantCode/v1/admin/users -> tenantUserAdmin.adminTenantUsers")
|
||||
router.Get("/t/:tenantCode/v1/admin/users"[len(r.Path()):], DataFunc3(
|
||||
r.tenantUserAdmin.adminTenantUsers,
|
||||
Local[*models.Tenant]("tenant"),
|
||||
Local[*models.TenantUser]("tenant_user"),
|
||||
Query[dto.AdminTenantUserListFilter]("filter"),
|
||||
))
|
||||
r.log.Debugf("Registering route: Patch /t/:tenantCode/v1/admin/users/:userID/role -> tenantUserAdmin.adminSetUserRole")
|
||||
router.Patch("/t/:tenantCode/v1/admin/users/:userID/role"[len(r.Path()):], DataFunc4(
|
||||
r.tenantUserAdmin.adminSetUserRole,
|
||||
Local[*models.Tenant]("tenant"),
|
||||
Local[*models.TenantUser]("tenant_user"),
|
||||
PathParam[int64]("userID"),
|
||||
Body[dto.AdminTenantUserRoleUpdateForm]("form"),
|
||||
))
|
||||
r.log.Debugf("Registering route: Post /t/:tenantCode/v1/admin/users/:userID/join -> tenantUserAdmin.adminJoinUser")
|
||||
router.Post("/t/:tenantCode/v1/admin/users/:userID/join"[len(r.Path()):], DataFunc3(
|
||||
r.tenantUserAdmin.adminJoinUser,
|
||||
Local[*models.Tenant]("tenant"),
|
||||
Local[*models.TenantUser]("tenant_user"),
|
||||
PathParam[int64]("userID"),
|
||||
))
|
||||
|
||||
r.log.Info("Successfully registered all routes")
|
||||
}
|
||||
|
||||
183
backend/app/http/tenant/tenant_user_admin.go
Normal file
183
backend/app/http/tenant/tenant_user_admin.go
Normal file
@@ -0,0 +1,183 @@
|
||||
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"
|
||||
"go.ipao.vip/gen"
|
||||
)
|
||||
|
||||
// tenantUserAdmin provides tenant-admin member management endpoints.
|
||||
//
|
||||
// @provider
|
||||
type tenantUserAdmin struct{}
|
||||
|
||||
// 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/admin/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/admin/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=models.TenantUser}
|
||||
//
|
||||
// @Router /t/:tenantCode/v1/admin/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
|
||||
}
|
||||
if filter == nil {
|
||||
filter = &dto.AdminTenantUserListFilter{}
|
||||
}
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"tenant_id": tenant.ID,
|
||||
"user_id": tenantUser.UserID,
|
||||
"query_uid": filter.UserID,
|
||||
}).Info("tenant.admin.users.list")
|
||||
|
||||
filter.Pagination.Format()
|
||||
|
||||
tbl, query := models.TenantUserQuery.QueryContext(ctx.Context())
|
||||
conds := []gen.Condition{tbl.TenantID.Eq(tenant.ID)}
|
||||
if filter.UserID != nil && *filter.UserID > 0 {
|
||||
conds = append(conds, tbl.UserID.Eq(*filter.UserID))
|
||||
}
|
||||
|
||||
items, total, err := query.Where(conds...).Order(tbl.ID.Desc()).FindByPage(int(filter.Offset()), int(filter.Limit))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &requests.Pager{
|
||||
Pagination: filter.Pagination,
|
||||
Total: total,
|
||||
Items: items,
|
||||
}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user