499 lines
15 KiB
Go
499 lines
15 KiB
Go
package controllers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"math"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"server/models"
|
|
"server/services"
|
|
|
|
beego "github.com/beego/beego/v2/server/web"
|
|
)
|
|
|
|
// PlatformTenantController ĺšłĺ°çŤŻç§ćˇçŽĄç?
|
|
type PlatformTenantController struct {
|
|
beego.Controller
|
|
}
|
|
|
|
type tenantDTO struct {
|
|
ID uint64 `json:"id"`
|
|
TenantCode string `json:"tenant_code"`
|
|
TenantName string `json:"tenant_name"`
|
|
ContactPerson string `json:"contact_person"`
|
|
ContactPhone string `json:"contact_phone"`
|
|
ContactEmail string `json:"contact_email"`
|
|
Address string `json:"address"`
|
|
Worktime string `json:"worktime"`
|
|
Status int8 `json:"status"`
|
|
// 套餐与用户数配额
|
|
PackageID uint64 `json:"package_id"`
|
|
PackageName string `json:"package_name"`
|
|
// PackageExpireTime 套餐计费到期时间(NULL-永久有效);DaysRemaining 剩余天数
|
|
PackageExpireTime *time.Time `json:"package_expire_time"`
|
|
DaysRemaining int `json:"days_remaining"`
|
|
UserQuota int `json:"user_quota"`
|
|
UserUsed int `json:"user_used"`
|
|
Remark string `json:"remark"`
|
|
CreateTime *time.Time `json:"create_time,omitempty"`
|
|
UpdateTime *time.Time `json:"update_time,omitempty"`
|
|
DeleteTime *time.Time `json:"delete_time,omitempty"`
|
|
}
|
|
|
|
// tenantPackageNameMap 套餐ID -> 套餐名称
|
|
func tenantPackageNameMap() map[uint64]string {
|
|
result := map[uint64]string{}
|
|
var rows []models.SystemTenantPackage
|
|
if _, err := models.Orm.QueryTable(new(models.SystemTenantPackage)).
|
|
Filter("delete_time__isnull", true).
|
|
All(&rows, "ID", "Name"); err == nil {
|
|
for _, r := range rows {
|
|
result[r.ID] = r.Name
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// tenantUserCountMap 租户ID -> 有效用户数
|
|
func tenantUserCountMap() map[uint64]int {
|
|
result := map[uint64]int{}
|
|
var rows []models.SystemTenantUser
|
|
if _, err := models.Orm.QueryTable(new(models.SystemTenantUser)).
|
|
Filter("delete_time__isnull", true).
|
|
All(&rows, "ID", "Tid"); err == nil {
|
|
for _, r := range rows {
|
|
result[r.Tid]++
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func stringValue(s *string) string {
|
|
if s == nil {
|
|
return ""
|
|
}
|
|
return *s
|
|
}
|
|
|
|
func stringPtr(s string) *string {
|
|
return &s
|
|
}
|
|
|
|
func toTenantDTO(t models.SystemTenant) tenantDTO {
|
|
ct := t.CreateTime
|
|
ut := t.UpdateTime
|
|
daysRemaining := 0
|
|
if t.PackageExpireTime != nil {
|
|
daysRemaining = int(math.Ceil(time.Until(*t.PackageExpireTime).Hours() / 24))
|
|
if daysRemaining < 0 {
|
|
daysRemaining = 0
|
|
}
|
|
}
|
|
return tenantDTO{
|
|
ID: t.ID,
|
|
TenantCode: t.TenantCode,
|
|
TenantName: t.TenantName,
|
|
ContactPerson: stringValue(t.ContactPerson),
|
|
ContactPhone: stringValue(t.ContactPhone),
|
|
ContactEmail: stringValue(t.ContactEmail),
|
|
Address: stringValue(t.Address),
|
|
Worktime: stringValue(t.Worktime),
|
|
Status: t.Status,
|
|
PackageID: t.PackageID,
|
|
PackageExpireTime: t.PackageExpireTime,
|
|
DaysRemaining: daysRemaining,
|
|
UserQuota: t.UserQuota,
|
|
Remark: stringValue(t.Remark),
|
|
CreateTime: &ct,
|
|
UpdateTime: &ut,
|
|
DeleteTime: t.DeleteTime,
|
|
}
|
|
}
|
|
|
|
// GetTenant čˇĺç§ćˇĺ襨
|
|
// GET /platform/tenant/getTenant?page=1&pageSize=10&tenant_name=...&tenant_code=...&contact_person=...&contact_phone=...
|
|
func (c *PlatformTenantController) GetTenant() {
|
|
page, _ := c.GetInt("page", 1)
|
|
pageSize, _ := c.GetInt("pageSize", 10)
|
|
if page < 1 {
|
|
page = 1
|
|
}
|
|
if pageSize < 1 {
|
|
pageSize = 10
|
|
}
|
|
|
|
tenantName := strings.TrimSpace(c.GetString("tenant_name"))
|
|
tenantCode := strings.TrimSpace(c.GetString("tenant_code"))
|
|
contactPerson := strings.TrimSpace(c.GetString("contact_person"))
|
|
contactPhone := strings.TrimSpace(c.GetString("contact_phone"))
|
|
|
|
qs := models.Orm.QueryTable(new(models.SystemTenant))
|
|
if tenantName != "" {
|
|
qs = qs.Filter("tenant_name__icontains", tenantName)
|
|
}
|
|
if tenantCode != "" {
|
|
qs = qs.Filter("tenant_code__icontains", tenantCode)
|
|
}
|
|
if contactPerson != "" {
|
|
qs = qs.Filter("contact_person__icontains", contactPerson)
|
|
}
|
|
if contactPhone != "" {
|
|
qs = qs.Filter("contact_phone__icontains", contactPhone)
|
|
}
|
|
|
|
total, err := qs.Count()
|
|
if err != nil {
|
|
c.Data["json"] = map[string]interface{}{"code": 500, "msg": "čˇĺç§ćˇĺ¤ąč´Ľ: " + err.Error()}
|
|
_ = c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
var rows []models.SystemTenant
|
|
_, err = qs.OrderBy("-id").Limit(pageSize, (page-1)*pageSize).All(&rows)
|
|
if err != nil {
|
|
c.Data["json"] = map[string]interface{}{"code": 500, "msg": "čˇĺç§ćˇĺ¤ąč´Ľ: " + err.Error()}
|
|
_ = c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
packageNames := tenantPackageNameMap()
|
|
userCounts := tenantUserCountMap()
|
|
list := make([]tenantDTO, 0, len(rows))
|
|
for _, t := range rows {
|
|
dto := toTenantDTO(t)
|
|
dto.PackageName = packageNames[t.PackageID]
|
|
dto.UserUsed = userCounts[t.ID]
|
|
list = append(list, dto)
|
|
}
|
|
|
|
c.Data["json"] = map[string]interface{}{
|
|
"code": 200,
|
|
"msg": "success",
|
|
"data": map[string]interface{}{
|
|
"list": list,
|
|
"total": total,
|
|
},
|
|
}
|
|
_ = c.ServeJSON()
|
|
}
|
|
|
|
// GetTenantDetail čˇĺç§ćˇčŻŚć
|
|
// GET /platform/tenant/getTenantDetail/:id
|
|
func (c *PlatformTenantController) GetTenantDetail() {
|
|
id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64)
|
|
if err != nil || id == 0 {
|
|
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "ć ćID"}
|
|
_ = c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
var t models.SystemTenant
|
|
err = models.Orm.QueryTable(new(models.SystemTenant)).Filter("id", id).One(&t)
|
|
if err != nil {
|
|
c.Data["json"] = map[string]interface{}{"code": 404, "msg": "租户不存在"}
|
|
_ = c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
dto := toTenantDTO(t)
|
|
if pkg, err := services.GetTenantPackage(t.ID); err == nil && pkg != nil {
|
|
if pkg.ID != t.PackageID {
|
|
// 套餐已因到期自动降级,原到期时间不再有效
|
|
dto.PackageExpireTime = nil
|
|
dto.DaysRemaining = 0
|
|
}
|
|
dto.PackageID = pkg.ID
|
|
dto.PackageName = pkg.Name
|
|
}
|
|
if used, err := services.CountTenantUsers(t.ID); err == nil {
|
|
dto.UserUsed = used
|
|
}
|
|
|
|
c.Data["json"] = map[string]interface{}{
|
|
"code": 200,
|
|
"msg": "success",
|
|
"data": dto,
|
|
}
|
|
_ = c.ServeJSON()
|
|
}
|
|
|
|
type tenantPayload struct {
|
|
TenantCode string `json:"tenant_code"`
|
|
TenantName string `json:"tenant_name"`
|
|
ContactPerson string `json:"contact_person"`
|
|
ContactPhone string `json:"contact_phone"`
|
|
ContactEmail string `json:"contact_email"`
|
|
Address string `json:"address"`
|
|
Worktime string `json:"worktime"`
|
|
Status *int8 `json:"status"`
|
|
// 套餐与用户数上限(可选)
|
|
PackageID *uint64 `json:"package_id"`
|
|
UserQuota *int `json:"user_quota"`
|
|
Remark string `json:"remark"`
|
|
}
|
|
|
|
func (c *PlatformTenantController) parseTenantPayload() (tenantPayload, error) {
|
|
// äźĺ
äťčĄ¨ĺ话ĺďźcreateTenant ä˝żç¨ multipart/form-dataďź?
|
|
p := tenantPayload{
|
|
TenantCode: strings.TrimSpace(c.GetString("tenant_code")),
|
|
TenantName: strings.TrimSpace(c.GetString("tenant_name")),
|
|
ContactPerson: strings.TrimSpace(c.GetString("contact_person")),
|
|
ContactPhone: strings.TrimSpace(c.GetString("contact_phone")),
|
|
ContactEmail: strings.TrimSpace(c.GetString("contact_email")),
|
|
Address: strings.TrimSpace(c.GetString("address")),
|
|
Worktime: strings.TrimSpace(c.GetString("worktime")),
|
|
Remark: strings.TrimSpace(c.GetString("remark")),
|
|
}
|
|
if s := strings.TrimSpace(c.GetString("status")); s != "" {
|
|
if v, err := strconv.ParseInt(s, 10, 8); err == nil {
|
|
tmp := int8(v)
|
|
p.Status = &tmp
|
|
}
|
|
}
|
|
if s := strings.TrimSpace(c.GetString("package_id")); s != "" {
|
|
if v, err := strconv.ParseUint(s, 10, 64); err == nil {
|
|
p.PackageID = &v
|
|
}
|
|
}
|
|
if s := strings.TrimSpace(c.GetString("user_quota")); s != "" {
|
|
if v, err := strconv.Atoi(s); err == nil {
|
|
p.UserQuota = &v
|
|
}
|
|
}
|
|
|
|
// ĺŚćĺ
łéŽĺ掾为犺ďźĺ°čŻäť JSON body č§ŁćďźeditTenant éťčޤ JSONďź?
|
|
if p.TenantName == "" && p.TenantCode == "" {
|
|
raw, _ := io.ReadAll(c.Ctx.Request.Body)
|
|
if len(raw) > 0 {
|
|
_ = json.Unmarshal(raw, &p)
|
|
}
|
|
}
|
|
return p, nil
|
|
}
|
|
|
|
// CreateTenant ĺĺťşç§ćˇ
|
|
// POST /platform/tenant/createTenant
|
|
func (c *PlatformTenantController) CreateTenant() {
|
|
p, _ := c.parseTenantPayload()
|
|
if strings.TrimSpace(p.TenantName) == "" {
|
|
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "ç§ćˇĺç§°ä¸č˝ä¸şçŠş"}
|
|
_ = c.ServeJSON()
|
|
return
|
|
}
|
|
if strings.TrimSpace(p.TenantCode) == "" {
|
|
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "ç§ćˇçźç ä¸č˝ä¸şçŠş"}
|
|
_ = c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
// ć ĄéŞçźç ĺŻä¸
|
|
cnt, err := models.Orm.QueryTable(new(models.SystemTenant)).Filter("tenant_code", p.TenantCode).Count()
|
|
if err != nil {
|
|
c.Data["json"] = map[string]interface{}{"code": 500, "msg": "ĺ坺夹贼: " + err.Error()}
|
|
_ = c.ServeJSON()
|
|
return
|
|
}
|
|
if cnt > 0 {
|
|
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "租户编码已存在"}
|
|
_ = c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
status := int8(1)
|
|
if p.Status != nil {
|
|
status = *p.Status
|
|
}
|
|
|
|
// 套餐与用户数上限:未指定套餐时使用默认套餐,用户数取其包含用户数(兜底 20)
|
|
packageID := uint64(0)
|
|
userQuota := services.DefaultTenantUserQuota
|
|
// 显式选择的套餐按套餐有效时长计费;未指定而兜底绑定默认套餐的不设置到期时间
|
|
explicitPackage := p.PackageID != nil && *p.PackageID > 0
|
|
var packageExpire *time.Time
|
|
if explicitPackage {
|
|
packageID = *p.PackageID
|
|
}
|
|
if packageID == 0 {
|
|
if pkg, err := services.GetDefaultTenantPackage(); err == nil && pkg != nil {
|
|
packageID = pkg.ID
|
|
if pkg.UserQuota > 0 {
|
|
userQuota = pkg.UserQuota
|
|
}
|
|
}
|
|
}
|
|
if packageID > 0 {
|
|
var pkg models.SystemTenantPackage
|
|
if err := models.Orm.QueryTable(new(models.SystemTenantPackage)).
|
|
Filter("id", packageID).
|
|
Filter("delete_time__isnull", true).
|
|
One(&pkg); err == nil {
|
|
if pkg.UserQuota > 0 {
|
|
userQuota = pkg.UserQuota
|
|
}
|
|
if explicitPackage {
|
|
e := services.PackageExpireFromNow(&pkg)
|
|
packageExpire = &e
|
|
}
|
|
}
|
|
}
|
|
if p.UserQuota != nil && *p.UserQuota > 0 {
|
|
userQuota = *p.UserQuota
|
|
}
|
|
if userQuota <= 0 {
|
|
userQuota = services.DefaultTenantUserQuota
|
|
}
|
|
|
|
t := models.SystemTenant{
|
|
TenantCode: p.TenantCode,
|
|
TenantName: p.TenantName,
|
|
ContactPerson: stringPtr(p.ContactPerson),
|
|
ContactPhone: stringPtr(p.ContactPhone),
|
|
ContactEmail: stringPtr(p.ContactEmail),
|
|
Address: stringPtr(p.Address),
|
|
Worktime: stringPtr(p.Worktime),
|
|
Status: status,
|
|
PackageID: packageID,
|
|
PackageExpireTime: packageExpire,
|
|
UserQuota: userQuota,
|
|
Remark: stringPtr(p.Remark),
|
|
}
|
|
|
|
id, err := models.Orm.Insert(&t)
|
|
if err != nil {
|
|
c.Data["json"] = map[string]interface{}{"code": 500, "msg": "ĺ坺夹贼: " + err.Error()}
|
|
_ = c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
c.Data["json"] = map[string]interface{}{
|
|
"code": 200,
|
|
"msg": "success",
|
|
"data": map[string]interface{}{"id": id},
|
|
}
|
|
_ = c.ServeJSON()
|
|
}
|
|
|
|
// EditTenant çźčžç§ćˇ
|
|
// POST /platform/tenant/editTenant/:id
|
|
func (c *PlatformTenantController) EditTenant() {
|
|
id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64)
|
|
if err != nil || id == 0 {
|
|
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "ć ćID"}
|
|
_ = c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
p, _ := c.parseTenantPayload()
|
|
if strings.TrimSpace(p.TenantName) == "" {
|
|
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "ç§ćˇĺç§°ä¸č˝ä¸şçŠş"}
|
|
_ = c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
update := map[string]interface{}{
|
|
"tenant_name": p.TenantName,
|
|
"contact_person": p.ContactPerson,
|
|
"contact_phone": p.ContactPhone,
|
|
"contact_email": p.ContactEmail,
|
|
"address": p.Address,
|
|
"worktime": p.Worktime,
|
|
"remark": p.Remark,
|
|
}
|
|
if p.Status != nil {
|
|
update["status"] = *p.Status
|
|
}
|
|
if p.PackageID != nil {
|
|
pkgID := *p.PackageID
|
|
if pkgID > 0 {
|
|
// 套餐切换/续费:按套餐有效时长重新计费(同套餐未到期在原到期时间上顺延),不动用户数上限
|
|
if _, err := services.ApplyPackageToTenant(id, pkgID, false); err != nil {
|
|
c.Data["json"] = map[string]interface{}{"code": 400, "msg": err.Error()}
|
|
_ = c.ServeJSON()
|
|
return
|
|
}
|
|
} else {
|
|
// 解绑套餐:回退未绑定状态(视为默认套餐),清除计费到期时间
|
|
update["package_id"] = 0
|
|
update["package_expire_time"] = nil
|
|
}
|
|
}
|
|
if p.UserQuota != nil {
|
|
// 用户数上限只增不减:不允许调低到当前已开用户数以下
|
|
used, _ := services.CountTenantUsers(id)
|
|
quota := *p.UserQuota
|
|
if quota > 0 && quota < used {
|
|
c.Data["json"] = map[string]interface{}{
|
|
"code": 400,
|
|
"msg": fmt.Sprintf("用户数上限不能小于该租户已开用户数(%d)", used),
|
|
}
|
|
_ = c.ServeJSON()
|
|
return
|
|
}
|
|
if quota > 0 {
|
|
update["user_quota"] = quota
|
|
}
|
|
}
|
|
|
|
_, err = models.Orm.QueryTable(new(models.SystemTenant)).Filter("id", id).Update(update)
|
|
if err != nil {
|
|
c.Data["json"] = map[string]interface{}{"code": 500, "msg": "ć´ć°ĺ¤ąč´Ľ: " + err.Error()}
|
|
_ = c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success"}
|
|
_ = c.ServeJSON()
|
|
}
|
|
|
|
// DeleteTenant ĺ é¤ç§ćˇ
|
|
// DELETE /platform/tenant/deleteTenant/:id
|
|
func (c *PlatformTenantController) DeleteTenant() {
|
|
id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64)
|
|
if err != nil || id == 0 {
|
|
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "ć ćID"}
|
|
_ = c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
_, err = models.Orm.QueryTable(new(models.SystemTenant)).Filter("id", id).Delete()
|
|
if err != nil {
|
|
c.Data["json"] = map[string]interface{}{"code": 500, "msg": "ĺ é¤ĺ¤ąč´Ľ: " + err.Error()}
|
|
_ = c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success"}
|
|
_ = c.ServeJSON()
|
|
}
|
|
|
|
// FindTenantCode ć ĄéŞç§ćˇçźç ćŻĺŚéĺ¤
|
|
// GET /platform/tenant/findTenantCode?tenant_code=xxxxxx
|
|
// čżĺ code=200 襨示ĺŻç¨ďźé200襨示éĺ¤/ä¸ĺŻç¨ďźĺ獯äźčŞĺ¨éć°çćďź
|
|
func (c *PlatformTenantController) FindTenantCode() {
|
|
code := strings.TrimSpace(c.GetString("tenant_code"))
|
|
if code == "" {
|
|
c.Data["json"] = map[string]interface{}{"code": 400, "msg": "tenant_code ä¸č˝ä¸şçŠş"}
|
|
_ = c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
cnt, err := models.Orm.QueryTable(new(models.SystemTenant)).Filter("tenant_code", code).Count()
|
|
if err != nil {
|
|
c.Data["json"] = map[string]interface{}{"code": 500, "msg": "ć ĄéŞĺ¤ąč´Ľ: " + err.Error()}
|
|
_ = c.ServeJSON()
|
|
return
|
|
}
|
|
if cnt > 0 {
|
|
c.Data["json"] = map[string]interface{}{"code": 409, "msg": "租户编码已存在"}
|
|
_ = c.ServeJSON()
|
|
return
|
|
}
|
|
|
|
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "ok"}
|
|
_ = c.ServeJSON()
|
|
}
|