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(`{}`)), } db := models.Q.Tenant.WithContext(ctx).UnderlyingDB() err = db.Transaction(func(tx *gorm.DB) error { if err := tx.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 }