148 lines
4.6 KiB
Go
148 lines
4.6 KiB
Go
package web
|
||
|
||
import (
|
||
"errors"
|
||
"regexp"
|
||
"strings"
|
||
|
||
"quyun/v2/app/errorx"
|
||
"quyun/v2/app/http/web/dto"
|
||
"quyun/v2/app/services"
|
||
"quyun/v2/database/models"
|
||
"quyun/v2/pkg/consts"
|
||
"quyun/v2/providers/jwt"
|
||
|
||
"github.com/gofiber/fiber/v3"
|
||
"go.ipao.vip/gen/types"
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
// @provider
|
||
type tenantApply struct{}
|
||
|
||
var reTenantCode = regexp.MustCompile(`^[a-z0-9][a-z0-9_-]{2,63}$`)
|
||
|
||
// Application 获取当前用户的租户申请信息(申请创作者)。
|
||
//
|
||
// @Summary 获取租户申请信息(申请创作者)
|
||
// @Tags Web
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Success 200 {object} dto.TenantApplicationResponse "成功"
|
||
// @Router /v1/tenant/application [get]
|
||
func (ctl *tenantApply) application(ctx fiber.Ctx) (*dto.TenantApplicationResponse, error) {
|
||
claims, ok := ctx.Locals(consts.CtxKeyClaims).(*jwt.Claims)
|
||
if !ok || claims == nil || claims.UserID <= 0 {
|
||
return nil, errorx.ErrTokenInvalid
|
||
}
|
||
|
||
m, err := services.Tenant.FindOwnedByUserID(ctx, claims.UserID)
|
||
if err != nil {
|
||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||
return &dto.TenantApplicationResponse{HasApplication: false}, nil
|
||
}
|
||
return nil, errorx.Wrap(err).WithMsg("查询申请信息失败,请稍后再试")
|
||
}
|
||
|
||
return &dto.TenantApplicationResponse{
|
||
HasApplication: true,
|
||
TenantID: m.ID,
|
||
TenantCode: m.Code,
|
||
TenantName: m.Name,
|
||
Status: m.Status,
|
||
StatusDescription: m.Status.Description(),
|
||
CreatedAt: m.CreatedAt,
|
||
}, nil
|
||
}
|
||
|
||
// Apply 申请创作者(创建租户申请)。
|
||
//
|
||
// @Summary 提交租户申请(申请创作者)
|
||
// @Tags Web
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param form body dto.TenantApplyForm true "form"
|
||
// @Success 200 {object} dto.TenantApplicationResponse "成功"
|
||
// @Router /v1/tenant/apply [post]
|
||
// @Bind form body
|
||
func (ctl *tenantApply) apply(ctx fiber.Ctx, form *dto.TenantApplyForm) (*dto.TenantApplicationResponse, error) {
|
||
claims, ok := ctx.Locals(consts.CtxKeyClaims).(*jwt.Claims)
|
||
if !ok || claims == nil || claims.UserID <= 0 {
|
||
return nil, errorx.ErrTokenInvalid
|
||
}
|
||
|
||
code := strings.ToLower(strings.TrimSpace(form.Code))
|
||
if code == "" {
|
||
return nil, errorx.ErrMissingParameter.WithMsg("请填写租户 ID")
|
||
}
|
||
if !reTenantCode.MatchString(code) {
|
||
return nil, errorx.ErrInvalidParameter.WithMsg("租户 ID 需为 3-64 位小写字母/数字/下划线/短横线,且以字母或数字开头")
|
||
}
|
||
name := strings.TrimSpace(form.Name)
|
||
if name == "" {
|
||
return nil, errorx.ErrMissingParameter.WithMsg("请填写租户名称")
|
||
}
|
||
|
||
// 一个用户仅可申请为一个租户创作者:若已存在 owned tenant,直接返回当前申请信息。
|
||
existing, err := services.Tenant.FindOwnedByUserID(ctx, claims.UserID)
|
||
if err == nil && existing != nil && existing.ID > 0 {
|
||
return &dto.TenantApplicationResponse{
|
||
HasApplication: true,
|
||
TenantID: existing.ID,
|
||
TenantCode: existing.Code,
|
||
TenantName: existing.Name,
|
||
Status: existing.Status,
|
||
StatusDescription: existing.Status.Description(),
|
||
CreatedAt: existing.CreatedAt,
|
||
}, nil
|
||
}
|
||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||
return nil, errorx.Wrap(err).WithMsg("申请校验失败,请稍后再试")
|
||
}
|
||
|
||
tenant := &models.Tenant{
|
||
UserID: claims.UserID,
|
||
Code: code,
|
||
UUID: types.NewUUIDv4(),
|
||
Name: name,
|
||
Status: consts.TenantStatusPendingVerify,
|
||
Config: types.JSON([]byte(`{}`)),
|
||
}
|
||
|
||
// NOTE: 使用全新 Session,避免复用 gen.DO 内部带 Model/Statement 的 *gorm.DB 导致 schema 与 dest 不一致而触发 GORM 反射 panic。
|
||
db := models.Q.Tenant.WithContext(ctx).UnderlyingDB().Session(&gorm.Session{})
|
||
err = db.Transaction(func(tx *gorm.DB) error {
|
||
if err := tx.Omit("Users").Create(tenant).Error; err != nil {
|
||
return err
|
||
}
|
||
|
||
tenantUser := &models.TenantUser{
|
||
TenantID: tenant.ID,
|
||
UserID: claims.UserID,
|
||
Role: types.NewArray([]consts.TenantUserRole{consts.TenantUserRoleTenantAdmin}),
|
||
Status: consts.UserStatusVerified,
|
||
}
|
||
if err := tx.Create(tenantUser).Error; err != nil {
|
||
return err
|
||
}
|
||
|
||
return tx.First(tenant, tenant.ID).Error
|
||
})
|
||
if err != nil {
|
||
if errors.Is(err, gorm.ErrDuplicatedKey) {
|
||
return nil, errorx.ErrRecordDuplicated.WithMsg("租户 ID 已被占用,请换一个试试")
|
||
}
|
||
return nil, errorx.Wrap(err).WithMsg("提交申请失败,请稍后再试")
|
||
}
|
||
|
||
return &dto.TenantApplicationResponse{
|
||
HasApplication: true,
|
||
TenantID: tenant.ID,
|
||
TenantCode: tenant.Code,
|
||
TenantName: tenant.Name,
|
||
Status: tenant.Status,
|
||
StatusDescription: tenant.Status.Description(),
|
||
CreatedAt: tenant.CreatedAt,
|
||
}, nil
|
||
}
|