606 lines
20 KiB
Go
606 lines
20 KiB
Go
package services
|
|
|
|
import (
|
|
"errors"
|
|
"math"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"server/models"
|
|
)
|
|
|
|
// DefaultTenantUserQuota 租户默认用户数上限(未配置套餐/套餐未设置时兜底)
|
|
const DefaultTenantUserQuota = 20
|
|
|
|
// DefaultPackageDurationDays 套餐默认有效时长(天):按年计费
|
|
const DefaultPackageDurationDays = 365
|
|
|
|
// DefaultTenantExtraUserPrice 单个用户增购默认单价(元/人)
|
|
const DefaultTenantExtraUserPrice = 200.00
|
|
|
|
// ErrTenantQuotaExceeded 用户数已达上限
|
|
var ErrTenantQuotaExceeded = errors.New("tenant user quota exceeded")
|
|
|
|
// CountTenantUsers 统计租户有效用户数(未软删)
|
|
func CountTenantUsers(tid uint64) (int, error) {
|
|
if tid == 0 {
|
|
return 0, nil
|
|
}
|
|
cnt, err := models.Orm.QueryTable(new(models.SystemTenantUser)).
|
|
Filter("tid", tid).
|
|
Filter("delete_time__isnull", true).
|
|
Count()
|
|
return int(cnt), err
|
|
}
|
|
|
|
// PackageDurationDays 套餐有效时长(天),未配置时按 365(年付)
|
|
func PackageDurationDays(pkg *models.SystemTenantPackage) int {
|
|
if pkg == nil || pkg.DurationDays <= 0 {
|
|
return DefaultPackageDurationDays
|
|
}
|
|
return pkg.DurationDays
|
|
}
|
|
|
|
// PackageExpireFromNow 从当前时间起算套餐到期时间
|
|
func PackageExpireFromNow(pkg *models.SystemTenantPackage) time.Time {
|
|
return time.Now().AddDate(0, 0, PackageDurationDays(pkg))
|
|
}
|
|
|
|
// tenantPackageExpired 租户套餐是否已超过时效(未设置到期时间视为永久有效)
|
|
func tenantPackageExpired(tenant *models.SystemTenant) bool {
|
|
return tenant.PackageExpireTime != nil && tenant.PackageExpireTime.Before(time.Now())
|
|
}
|
|
|
|
// GetTenantPackage 获取租户生效套餐:优先取绑定套餐,未绑定/已删除时回退到默认套餐。
|
|
// 绑定套餐已超过时效时,自动切换回基础套餐并落库(到期时间清空),返回基础套餐。
|
|
func GetTenantPackage(tid uint64) (*models.SystemTenantPackage, error) {
|
|
if tid == 0 {
|
|
return nil, errors.New("tid 不能为空")
|
|
}
|
|
var tenant models.SystemTenant
|
|
if err := models.Orm.QueryTable(new(models.SystemTenant)).Filter("id", tid).One(&tenant); err != nil {
|
|
return nil, err
|
|
}
|
|
if tenant.PackageID > 0 {
|
|
// 套餐到期:自动切换基础套餐(降级落库,保证只执行一次)
|
|
if tenantPackageExpired(&tenant) {
|
|
return downgradeExpiredTenantPackage(tid)
|
|
}
|
|
var pkg models.SystemTenantPackage
|
|
if err := models.Orm.QueryTable(new(models.SystemTenantPackage)).
|
|
Filter("id", tenant.PackageID).
|
|
Filter("delete_time__isnull", true).
|
|
One(&pkg); err == nil {
|
|
return &pkg, nil
|
|
}
|
|
}
|
|
return GetDefaultTenantPackage()
|
|
}
|
|
|
|
// GetBasicTenantPackage 获取基础套餐(code=basic;不存在时回退默认套餐)
|
|
func GetBasicTenantPackage() (*models.SystemTenantPackage, error) {
|
|
var pkg models.SystemTenantPackage
|
|
err := models.Orm.QueryTable(new(models.SystemTenantPackage)).
|
|
Filter("code", "basic").
|
|
Filter("delete_time__isnull", true).
|
|
Filter("status", 1).
|
|
One(&pkg)
|
|
if err == nil {
|
|
return &pkg, nil
|
|
}
|
|
return GetDefaultTenantPackage()
|
|
}
|
|
|
|
// downgradeExpiredTenantPackage 套餐到期:切换基础套餐并清除到期时间,返回基础套餐
|
|
func downgradeExpiredTenantPackage(tid uint64) (*models.SystemTenantPackage, error) {
|
|
basic, err := GetBasicTenantPackage()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
_, _ = models.Orm.QueryTable(new(models.SystemTenant)).
|
|
Filter("id", tid).
|
|
Update(map[string]interface{}{
|
|
"package_id": basic.ID,
|
|
"package_expire_time": nil,
|
|
})
|
|
return basic, nil
|
|
}
|
|
|
|
// GetDefaultTenantPackage 获取默认套餐(is_default=1 优先,其次按排序取第一个启用套餐)
|
|
func GetDefaultTenantPackage() (*models.SystemTenantPackage, error) {
|
|
var pkg models.SystemTenantPackage
|
|
err := models.Orm.QueryTable(new(models.SystemTenantPackage)).
|
|
Filter("delete_time__isnull", true).
|
|
Filter("status", 1).
|
|
Filter("is_default", 1).
|
|
OrderBy("sort", "id").
|
|
One(&pkg)
|
|
if err == nil {
|
|
return &pkg, nil
|
|
}
|
|
var fallback models.SystemTenantPackage
|
|
if err2 := models.Orm.QueryTable(new(models.SystemTenantPackage)).
|
|
Filter("delete_time__isnull", true).
|
|
Filter("status", 1).
|
|
OrderBy("sort", "id").
|
|
One(&fallback); err2 != nil {
|
|
return nil, err2
|
|
}
|
|
return &fallback, nil
|
|
}
|
|
|
|
// GetTenantPackageModules 获取租户套餐包含的功能模块(按套餐未绑定则取默认套餐)
|
|
func GetTenantPackageModules(tid uint64) ([]models.SystemTenantPackageModule, error) {
|
|
pkg, err := GetTenantPackage(tid)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var rows []models.SystemTenantPackageModule
|
|
_, err = models.Orm.QueryTable(new(models.SystemTenantPackageModule)).
|
|
Filter("package_id", pkg.ID).
|
|
OrderBy("id").
|
|
All(&rows)
|
|
return rows, err
|
|
}
|
|
|
|
// NormalizeModuleCode 规范化模块编码:去空格 + 统一小写。
|
|
// 模块、套餐模块、产品关联模块来自不同配置入口,编码大小写可能不一致
|
|
// (如模块 code=oa、产品关联功能填 OA),统一规范化后再比对。
|
|
func NormalizeModuleCode(code string) string {
|
|
return strings.ToLower(strings.TrimSpace(code))
|
|
}
|
|
|
|
// DayStart 取时间所在自然日的 0 点(有效期按「日」计算,不精确到秒)
|
|
func DayStart(t time.Time) time.Time {
|
|
return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
|
|
}
|
|
|
|
// ProductOrderExpireAt 单品订单有效期截止时间(精确到日):
|
|
// - 优先取落库的 expire_time(按日规范化到当天 0 点);
|
|
// - 历史订单未落库时按「开通日 0 点 + 默认有效期(1 年)」兜底计算,保证老订单同样按 1 年到期;
|
|
// - 两者都无(尚未开通)返回 nil,视为无到期时间。
|
|
func ProductOrderExpireAt(order *models.PlatformProductOrder) *time.Time {
|
|
if order == nil {
|
|
return nil
|
|
}
|
|
if order.ExpireTime != nil {
|
|
t := DayStart(*order.ExpireTime)
|
|
return &t
|
|
}
|
|
if order.ActivateTime != nil {
|
|
t := DayStart(*order.ActivateTime).AddDate(0, 0, models.ProductOrderDurationDays)
|
|
return &t
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ProductOrderExpired 单品订单是否已过有效期:
|
|
// 到期日当天 0 点起即视为过期(到期后权益自动失效,需重新购买续费)。
|
|
func ProductOrderExpired(order *models.PlatformProductOrder, now time.Time) bool {
|
|
expire := ProductOrderExpireAt(order)
|
|
return expire != nil && !now.Before(*expire)
|
|
}
|
|
|
|
// GetTenantModuleCodes 获取租户已开通的功能模块编码集合(如 erp / oa / crm)。
|
|
//
|
|
// 权益由两部分组成:
|
|
// - 当前生效套餐包含的模块;
|
|
// - 已开通单品订单关联产品的模块。
|
|
//
|
|
// 单品购买不会替换租户当前套餐,因此必须与套餐模块取并集;否则产品订单即使
|
|
// 已显示“已开通”,首页仍会把该模块判定为未购买。历史订单同样按产品当前的
|
|
// module_code 识别,保证已存在的已开通订单可立即恢复对应模块权限。
|
|
// 返回的 key 均为 NormalizeModuleCode 规范化后的编码。
|
|
func GetTenantModuleCodes(tid uint64) map[string]bool {
|
|
codes := map[string]bool{}
|
|
mark := func(code string) {
|
|
if c := NormalizeModuleCode(code); c != "" {
|
|
codes[c] = true
|
|
}
|
|
}
|
|
rows, err := GetTenantPackageModules(tid)
|
|
if err == nil {
|
|
for _, r := range rows {
|
|
mark(r.ModuleCode)
|
|
}
|
|
}
|
|
|
|
if tid == 0 {
|
|
return codes
|
|
}
|
|
|
|
var orders []models.PlatformProductOrder
|
|
if _, err := models.Orm.QueryTable(new(models.PlatformProductOrder)).
|
|
Filter("tid", tid).
|
|
Filter("status", models.ProductOrderStatusActivated).
|
|
Filter("product_id__gt", 0).
|
|
Filter("delete_time__isnull", true).
|
|
All(&orders); err != nil || len(orders) == 0 {
|
|
return codes
|
|
}
|
|
|
|
// 只统计仍在有效期内的订单:到期后权益自动失效,需重新购买续费
|
|
now := time.Now()
|
|
validOrders := make([]models.PlatformProductOrder, 0, len(orders))
|
|
for _, order := range orders {
|
|
if ProductOrderExpired(&order, now) {
|
|
continue
|
|
}
|
|
validOrders = append(validOrders, order)
|
|
}
|
|
if len(validOrders) == 0 {
|
|
return codes
|
|
}
|
|
|
|
productIDs := make([]uint64, 0, len(validOrders))
|
|
for _, order := range validOrders {
|
|
productIDs = append(productIDs, order.ProductID)
|
|
}
|
|
var products []models.PlatformProduct
|
|
if _, err := models.Orm.QueryTable(new(models.PlatformProduct)).
|
|
Filter("id__in", productIDs).
|
|
All(&products); err != nil {
|
|
return codes
|
|
}
|
|
for _, product := range products {
|
|
code := product.ModuleCode
|
|
if strings.TrimSpace(code) == "" {
|
|
// 产品未显式关联功能模块时,按产品编码兜底匹配(产品编码通常即模块编码,如 oa)
|
|
code = product.Code
|
|
}
|
|
mark(code)
|
|
}
|
|
return codes
|
|
}
|
|
|
|
// TenantQuotaCheck 用户数配额校验结果
|
|
type TenantQuotaCheck struct {
|
|
Quota int `json:"quota"`
|
|
Used int `json:"used"`
|
|
Allowed bool `json:"allowed"`
|
|
}
|
|
|
|
// CheckTenantUserQuota 校验租户是否还能新增用户。
|
|
// uid > 0 且该用户已绑定到本租户时视为「编辑/复用已有绑定」,不占用新增名额。
|
|
func CheckTenantUserQuota(tid, uid uint64) (*TenantQuotaCheck, error) {
|
|
result := &TenantQuotaCheck{Quota: DefaultTenantUserQuota, Allowed: true}
|
|
if tid == 0 {
|
|
return result, nil
|
|
}
|
|
|
|
quota := DefaultTenantUserQuota
|
|
var tenant models.SystemTenant
|
|
if err := models.Orm.QueryTable(new(models.SystemTenant)).Filter("id", tid).One(&tenant); err == nil && tenant.UserQuota > 0 {
|
|
quota = tenant.UserQuota
|
|
}
|
|
result.Quota = quota
|
|
|
|
if uid > 0 {
|
|
exist, err := models.Orm.QueryTable(new(models.SystemTenantUser)).
|
|
Filter("tid", tid).
|
|
Filter("uid", uid).
|
|
Filter("delete_time__isnull", true).
|
|
Count()
|
|
if err == nil && exist > 0 {
|
|
result.Allowed = true
|
|
return result, nil
|
|
}
|
|
}
|
|
|
|
used, err := CountTenantUsers(tid)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result.Used = used
|
|
result.Allowed = used < quota
|
|
return result, nil
|
|
}
|
|
|
|
// EnsureTenantUserQuota 用户数已满时返回 ErrTenantQuotaExceeded
|
|
func EnsureTenantUserQuota(tid, uid uint64) error {
|
|
check, err := CheckTenantUserQuota(tid, uid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !check.Allowed {
|
|
return ErrTenantQuotaExceeded
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetTenantQuotaInfo 租户用户数使用情况
|
|
type TenantQuotaInfo struct {
|
|
Tid uint64 `json:"tid"`
|
|
PackageID uint64 `json:"package_id"`
|
|
PackageName string `json:"package_name"`
|
|
Quota int `json:"quota"`
|
|
Used int `json:"used"`
|
|
Remaining int `json:"remaining"`
|
|
ExtraUserPrice float64 `json:"extra_user_price"`
|
|
EffectivePackageID uint64 `json:"effective_package_id"`
|
|
DurationDays int `json:"duration_days"`
|
|
PackageExpireTime *time.Time `json:"package_expire_time"`
|
|
DaysRemaining int `json:"days_remaining"`
|
|
}
|
|
|
|
// GetTenantQuotaInfo 查询租户用户数使用情况(含生效套餐信息与计费到期时间)
|
|
func GetTenantQuotaInfo(tid uint64) (*TenantQuotaInfo, error) {
|
|
info := &TenantQuotaInfo{Tid: tid, Quota: DefaultTenantUserQuota, ExtraUserPrice: DefaultTenantExtraUserPrice}
|
|
var tenant models.SystemTenant
|
|
if err := models.Orm.QueryTable(new(models.SystemTenant)).Filter("id", tid).One(&tenant); err != nil {
|
|
return nil, err
|
|
}
|
|
if tenant.UserQuota > 0 {
|
|
info.Quota = tenant.UserQuota
|
|
}
|
|
used, err := CountTenantUsers(tid)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
info.Used = used
|
|
info.Remaining = info.Quota - used
|
|
if info.Remaining < 0 {
|
|
info.Remaining = 0
|
|
}
|
|
// 生效套餐(到期会自动降级基础套餐并落库)
|
|
if pkg, err := GetTenantPackage(tid); err == nil && pkg != nil {
|
|
info.EffectivePackageID = pkg.ID
|
|
info.PackageName = pkg.Name
|
|
info.DurationDays = PackageDurationDays(pkg)
|
|
if pkg.ExtraUserPrice > 0 {
|
|
info.ExtraUserPrice = pkg.ExtraUserPrice
|
|
}
|
|
}
|
|
// 重新读取租户绑定信息(GetTenantPackage 可能已触发到期降级落库)
|
|
var fresh models.SystemTenant
|
|
if e := models.Orm.QueryTable(new(models.SystemTenant)).Filter("id", tid).One(&fresh); e == nil {
|
|
tenant = fresh
|
|
}
|
|
info.PackageID = tenant.PackageID
|
|
info.PackageExpireTime = tenant.PackageExpireTime
|
|
if tenant.PackageExpireTime != nil {
|
|
remain := int(math.Ceil(time.Until(*tenant.PackageExpireTime).Hours() / 24))
|
|
if remain < 0 {
|
|
remain = 0
|
|
}
|
|
info.DaysRemaining = remain
|
|
}
|
|
return info, nil
|
|
}
|
|
|
|
// IncreaseTenantQuota 增加租户用户数上限,返回(变更前上限、变更后上限)
|
|
func IncreaseTenantQuota(tid uint64, count int) (int, int, error) {
|
|
if tid == 0 || count <= 0 {
|
|
return 0, 0, errors.New("参数错误")
|
|
}
|
|
var tenant models.SystemTenant
|
|
if err := models.Orm.QueryTable(new(models.SystemTenant)).Filter("id", tid).One(&tenant); err != nil {
|
|
return 0, 0, errors.New("租户不存在")
|
|
}
|
|
before := tenant.UserQuota
|
|
if before <= 0 {
|
|
before = DefaultTenantUserQuota
|
|
}
|
|
after := before + count
|
|
_, err := models.Orm.QueryTable(new(models.SystemTenant)).
|
|
Filter("id", tid).
|
|
Update(map[string]interface{}{"user_quota": after})
|
|
if err != nil {
|
|
return before, after, err
|
|
}
|
|
return before, after, nil
|
|
}
|
|
|
|
// ApplyPackageToTenant 为租户绑定/续费套餐:
|
|
// - 同步套餐包含的用户数(不低于当前已开用户数,且不降低已有上限,syncQuota=true 时)
|
|
// - 按套餐有效时长(duration_days,默认 365 天 = 年付)设置计费到期时间;
|
|
// 同套餐未到期时再次绑定视为续费,在原到期时间上顺延
|
|
func ApplyPackageToTenant(tid, packageID uint64, syncQuota bool) (int, error) {
|
|
if tid == 0 || packageID == 0 {
|
|
return 0, errors.New("参数错误")
|
|
}
|
|
var pkg models.SystemTenantPackage
|
|
if err := models.Orm.QueryTable(new(models.SystemTenantPackage)).
|
|
Filter("id", packageID).
|
|
Filter("delete_time__isnull", true).
|
|
One(&pkg); err != nil {
|
|
return 0, errors.New("套餐不存在")
|
|
}
|
|
var tenant models.SystemTenant
|
|
if err := models.Orm.QueryTable(new(models.SystemTenant)).Filter("id", tid).One(&tenant); err != nil {
|
|
return 0, errors.New("租户不存在")
|
|
}
|
|
|
|
update := map[string]interface{}{"package_id": packageID}
|
|
newQuota := tenant.UserQuota
|
|
if syncQuota {
|
|
current := tenant.UserQuota
|
|
if current <= 0 {
|
|
current = DefaultTenantUserQuota
|
|
}
|
|
newQuota = pkg.UserQuota
|
|
if newQuota <= 0 {
|
|
newQuota = DefaultTenantUserQuota
|
|
}
|
|
// 不低于当前上限:换套餐不回收已购用户数
|
|
if newQuota < current {
|
|
newQuota = current
|
|
}
|
|
update["user_quota"] = newQuota
|
|
}
|
|
|
|
// 计费时效:同套餐未到期续费 → 从原到期时间顺延;换套餐/已到期 → 从当前时间起算
|
|
base := time.Now()
|
|
if tenant.PackageID == packageID && tenant.PackageExpireTime != nil && tenant.PackageExpireTime.After(base) {
|
|
base = *tenant.PackageExpireTime
|
|
}
|
|
update["package_expire_time"] = base.AddDate(0, 0, PackageDurationDays(&pkg))
|
|
|
|
if _, err := models.Orm.QueryTable(new(models.SystemTenant)).
|
|
Filter("id", tid).
|
|
Update(update); err != nil {
|
|
return 0, err
|
|
}
|
|
return newQuota, nil
|
|
}
|
|
|
|
// sortModulesByPathDesc 模块按 path 长度倒序:长路径优先匹配,避免 /apps 误吞 /apps/erp
|
|
func sortModulesByPathDesc(modules []models.SystemModules) []models.SystemModules {
|
|
list := make([]models.SystemModules, 0, len(modules))
|
|
for _, m := range modules {
|
|
if strings.TrimSpace(m.Path) != "" {
|
|
list = append(list, m)
|
|
}
|
|
}
|
|
sort.SliceStable(list, func(i, j int) bool {
|
|
return len(list[i].Path) > len(list[j].Path)
|
|
})
|
|
return list
|
|
}
|
|
|
|
// ownerModuleCode 返回菜单路径归属的模块编码(按模块 path 前缀匹配)。
|
|
// 不属于任何模块时 ok=false —— 视为公共菜单,不参与套餐过滤。
|
|
func ownerModuleCode(sortedModules []models.SystemModules, path string) (string, bool) {
|
|
p := strings.TrimSuffix(strings.TrimSpace(path), "/")
|
|
if p == "" {
|
|
return "", false
|
|
}
|
|
for _, m := range sortedModules {
|
|
mp := strings.TrimSuffix(strings.TrimSpace(m.Path), "/")
|
|
if mp == "" {
|
|
continue
|
|
}
|
|
if p == mp || strings.HasPrefix(p, mp+"/") {
|
|
return m.Code, true
|
|
}
|
|
}
|
|
return "", false
|
|
}
|
|
|
|
// FilterMenusByTenantModules 按租户套餐包含的功能模块过滤菜单:
|
|
// - 菜单不属于任何模块(公共/基础菜单)→ 保留;
|
|
// - 菜单归属某模块 → 仅当该模块已被租户套餐开通时保留。
|
|
func FilterMenusByTenantModules(menus []models.SystemMenu, allModules []models.SystemModules, allowedCodes map[string]bool) []models.SystemMenu {
|
|
if len(menus) == 0 || len(allModules) == 0 {
|
|
return menus
|
|
}
|
|
sorted := sortModulesByPathDesc(allModules)
|
|
out := make([]models.SystemMenu, 0, len(menus))
|
|
for _, m := range menus {
|
|
path := ""
|
|
if m.Path != nil {
|
|
path = *m.Path
|
|
}
|
|
code, ok := ownerModuleCode(sorted, path)
|
|
if !ok {
|
|
out = append(out, m)
|
|
continue
|
|
}
|
|
if allowedCodes[NormalizeModuleCode(code)] {
|
|
out = append(out, m)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// MarkMenusLockedByTenantModules 按租户套餐标记未开通模块的菜单:菜单保留,返回锁定菜单ID集合。
|
|
// lockedIDs 包含未开通模块对应的菜单及其全部后代菜单ID,供前端展示锁定态(而非直接剔除导致 404)。
|
|
func MarkMenusLockedByTenantModules(menus []models.SystemMenu, allModules []models.SystemModules, allowedCodes map[string]bool) ([]models.SystemMenu, map[uint64]bool) {
|
|
lockedIDs := map[uint64]bool{}
|
|
if len(menus) == 0 || len(allModules) == 0 {
|
|
return menus, lockedIDs
|
|
}
|
|
sorted := sortModulesByPathDesc(allModules)
|
|
kept := make([]models.SystemMenu, 0, len(menus))
|
|
for _, m := range menus {
|
|
path := ""
|
|
if m.Path != nil {
|
|
path = *m.Path
|
|
}
|
|
code, ok := ownerModuleCode(sorted, path)
|
|
if ok && !allowedCodes[NormalizeModuleCode(code)] {
|
|
lockedIDs[m.ID] = true
|
|
}
|
|
kept = append(kept, m)
|
|
}
|
|
if len(lockedIDs) == 0 {
|
|
return kept, lockedIDs
|
|
}
|
|
// 锁定传递给所有后代菜单
|
|
childrenOf := map[uint64][]uint64{}
|
|
for _, m := range kept {
|
|
if m.Pid > 0 {
|
|
pid := uint64(m.Pid)
|
|
childrenOf[pid] = append(childrenOf[pid], m.ID)
|
|
}
|
|
}
|
|
queue := make([]uint64, 0, len(lockedIDs))
|
|
for id := range lockedIDs {
|
|
queue = append(queue, id)
|
|
}
|
|
for len(queue) > 0 {
|
|
id := queue[0]
|
|
queue = queue[1:]
|
|
for _, child := range childrenOf[id] {
|
|
if !lockedIDs[child] {
|
|
lockedIDs[child] = true
|
|
queue = append(queue, child)
|
|
}
|
|
}
|
|
}
|
|
return kept, lockedIDs
|
|
}
|
|
|
|
// PackagePurchaseURLCode 平台通用配置(yz_platform_normal_setting)中「套餐购买链接」的配置 code
|
|
const PackagePurchaseURLCode = "package_purchase_url"
|
|
|
|
// GetPackagePurchaseURL 读取平台配置的套餐购买链接。
|
|
// 租户端 home 页未开通(锁定)功能卡片的「购买」按钮跳转到该地址;未配置时返回空串。
|
|
func GetPackagePurchaseURL() string {
|
|
var s models.PlatformNormalSetting
|
|
if err := models.Orm.QueryTable(new(models.PlatformNormalSetting)).
|
|
Filter("code", PackagePurchaseURLCode).
|
|
Filter("delete_time__isnull", true).
|
|
One(&s); err != nil {
|
|
return ""
|
|
}
|
|
return strings.TrimSpace(s.Value)
|
|
}
|
|
|
|
// SavePackagePurchaseURL 保存平台配置的套餐购买链接(不存在则创建)
|
|
func SavePackagePurchaseURL(url string) error {
|
|
url = strings.TrimSpace(url)
|
|
var s models.PlatformNormalSetting
|
|
err := models.Orm.QueryTable(new(models.PlatformNormalSetting)).
|
|
Filter("code", PackagePurchaseURLCode).
|
|
Filter("delete_time__isnull", true).
|
|
One(&s)
|
|
if err != nil {
|
|
s = models.PlatformNormalSetting{
|
|
Name: "套餐购买链接",
|
|
Code: PackagePurchaseURLCode,
|
|
Value: url,
|
|
Remark: "租户端未开通功能卡片「购买」按钮跳转地址",
|
|
}
|
|
_, err = models.Orm.Insert(&s)
|
|
return err
|
|
}
|
|
now := time.Now()
|
|
s.Value = url
|
|
s.UpdateTime = &now
|
|
_, err = models.Orm.Update(&s)
|
|
return err
|
|
}
|
|
|
|
// ListEnabledModules 获取全部启用且未删除的模块(用于菜单归属判定)
|
|
func ListEnabledModules() ([]models.SystemModules, error) {
|
|
var rows []models.SystemModules
|
|
_, err := models.Orm.QueryTable(new(models.SystemModules)).
|
|
Filter("delete_time__isnull", true).
|
|
Filter("status", 1).
|
|
OrderBy("sort", "id").
|
|
All(&rows)
|
|
return rows, err
|
|
}
|