feat: add superadmin assets and notifications

This commit is contained in:
2026-01-15 15:28:41 +08:00
parent c683fa5cf3
commit b896d0fa00
22 changed files with 4852 additions and 260 deletions

View File

@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"errors"
"path/filepath"
"strconv"
"strings"
"time"
@@ -2127,6 +2128,784 @@ func (s *super) ContentStatistics(ctx context.Context, filter *super_dto.SuperCo
}, nil
}
func (s *super) ListAssets(ctx context.Context, filter *super_dto.SuperAssetListFilter) (*requests.Pager, error) {
if filter == nil {
filter = &super_dto.SuperAssetListFilter{}
}
tbl, q := models.MediaAssetQuery.QueryContext(ctx)
if filter.ID != nil && *filter.ID > 0 {
q = q.Where(tbl.ID.Eq(*filter.ID))
}
if filter.TenantID != nil && *filter.TenantID > 0 {
q = q.Where(tbl.TenantID.Eq(*filter.TenantID))
}
if filter.UserID != nil && *filter.UserID > 0 {
q = q.Where(tbl.UserID.Eq(*filter.UserID))
}
if filter.Type != nil && *filter.Type != "" {
q = q.Where(tbl.Type.Eq(*filter.Type))
}
if filter.Status != nil && *filter.Status != "" {
q = q.Where(tbl.Status.Eq(*filter.Status))
}
if filter.Provider != nil && strings.TrimSpace(*filter.Provider) != "" {
q = q.Where(tbl.Provider.Eq(strings.TrimSpace(*filter.Provider)))
}
if filter.ObjectKey != nil && strings.TrimSpace(*filter.ObjectKey) != "" {
keyword := "%" + strings.TrimSpace(*filter.ObjectKey) + "%"
q = q.Where(tbl.ObjectKey.Like(keyword))
}
tenantIDs, tenantFilter, err := s.lookupTenantIDs(ctx, filter.TenantCode, filter.TenantName)
if err != nil {
return nil, err
}
if tenantFilter {
if len(tenantIDs) == 0 {
q = q.Where(tbl.ID.Eq(-1))
} else {
q = q.Where(tbl.TenantID.In(tenantIDs...))
}
}
userIDs, userFilter, err := s.lookupUserIDs(ctx, filter.Username)
if err != nil {
return nil, err
}
if userFilter {
if len(userIDs) == 0 {
q = q.Where(tbl.ID.Eq(-1))
} else {
q = q.Where(tbl.UserID.In(userIDs...))
}
}
if filter.CreatedAtFrom != nil {
from, err := s.parseFilterTime(filter.CreatedAtFrom)
if err != nil {
return nil, err
}
if from != nil {
q = q.Where(tbl.CreatedAt.Gte(*from))
}
}
if filter.CreatedAtTo != nil {
to, err := s.parseFilterTime(filter.CreatedAtTo)
if err != nil {
return nil, err
}
if to != nil {
q = q.Where(tbl.CreatedAt.Lte(*to))
}
}
if filter.SizeMin != nil {
// JSONB 元信息内的 size 需要使用原生表达式过滤。
q = q.Where(field.NewUnsafeFieldRaw("coalesce((meta->>'size')::bigint,0) >= ?", *filter.SizeMin))
}
if filter.SizeMax != nil {
// JSONB 元信息内的 size 需要使用原生表达式过滤。
q = q.Where(field.NewUnsafeFieldRaw("coalesce((meta->>'size')::bigint,0) <= ?", *filter.SizeMax))
}
orderApplied := false
if filter.Desc != nil && strings.TrimSpace(*filter.Desc) != "" {
switch strings.TrimSpace(*filter.Desc) {
case "id":
q = q.Order(tbl.ID.Desc())
case "created_at":
q = q.Order(tbl.CreatedAt.Desc())
}
orderApplied = true
} else if filter.Asc != nil && strings.TrimSpace(*filter.Asc) != "" {
switch strings.TrimSpace(*filter.Asc) {
case "id":
q = q.Order(tbl.ID)
case "created_at":
q = q.Order(tbl.CreatedAt)
}
orderApplied = true
}
if !orderApplied {
q = q.Order(tbl.CreatedAt.Desc())
}
filter.Pagination.Format()
total, err := q.Count()
if err != nil {
return nil, errorx.ErrDatabaseError.WithCause(err)
}
list, err := q.Offset(int(filter.Pagination.Offset())).Limit(int(filter.Pagination.Limit)).Find()
if err != nil {
return nil, errorx.ErrDatabaseError.WithCause(err)
}
if len(list) == 0 {
return &requests.Pager{
Pagination: filter.Pagination,
Total: total,
Items: []super_dto.SuperAssetItem{},
}, nil
}
tenantSet := make(map[int64]struct{})
userSet := make(map[int64]struct{})
assetIDs := make([]int64, 0, len(list))
for _, asset := range list {
assetIDs = append(assetIDs, asset.ID)
if asset.TenantID > 0 {
tenantSet[asset.TenantID] = struct{}{}
}
if asset.UserID > 0 {
userSet[asset.UserID] = struct{}{}
}
}
tenantMap := make(map[int64]*models.Tenant, len(tenantSet))
if len(tenantSet) > 0 {
ids := make([]int64, 0, len(tenantSet))
for id := range tenantSet {
ids = append(ids, id)
}
tenantTbl, tenantQuery := models.TenantQuery.QueryContext(ctx)
tenants, err := tenantQuery.Where(tenantTbl.ID.In(ids...)).Find()
if err != nil {
return nil, errorx.ErrDatabaseError.WithCause(err)
}
for _, tenant := range tenants {
tenantMap[tenant.ID] = tenant
}
}
userMap := make(map[int64]*models.User, len(userSet))
if len(userSet) > 0 {
ids := make([]int64, 0, len(userSet))
for id := range userSet {
ids = append(ids, id)
}
userTbl, userQuery := models.UserQuery.QueryContext(ctx)
users, err := userQuery.Where(userTbl.ID.In(ids...)).Find()
if err != nil {
return nil, errorx.ErrDatabaseError.WithCause(err)
}
for _, user := range users {
userMap[user.ID] = user
}
}
usedCountMap := make(map[int64]int64)
if len(assetIDs) > 0 {
type assetUseRow struct {
AssetID int64 `gorm:"column:asset_id"`
Count int64 `gorm:"column:count"`
}
rows := make([]assetUseRow, 0)
query := models.ContentAssetQuery.WithContext(ctx).
UnderlyingDB().
Model(&models.ContentAsset{}).
Select("asset_id, count(*) as count").
Where("asset_id in ?", assetIDs).
Group("asset_id")
if err := query.Scan(&rows).Error; err != nil {
return nil, errorx.ErrDatabaseError.WithCause(err)
}
for _, row := range rows {
usedCountMap[row.AssetID] = row.Count
}
}
items := make([]super_dto.SuperAssetItem, 0, len(list))
for _, asset := range list {
meta := asset.Meta.Data()
filename := strings.TrimSpace(meta.Filename)
if filename == "" {
filename = filepath.Base(asset.ObjectKey)
}
url := ""
if Common != nil {
url = Common.GetAssetURL(asset.ObjectKey)
}
item := super_dto.SuperAssetItem{
ID: asset.ID,
TenantID: asset.TenantID,
UserID: asset.UserID,
Type: asset.Type,
Status: asset.Status,
Provider: asset.Provider,
Bucket: asset.Bucket,
ObjectKey: asset.ObjectKey,
URL: url,
Filename: filename,
Size: meta.Size,
Hash: asset.Hash,
Variant: asset.Variant,
SourceAssetID: asset.SourceAssetID,
UsedCount: usedCountMap[asset.ID],
CreatedAt: s.formatTime(asset.CreatedAt),
UpdatedAt: s.formatTime(asset.UpdatedAt),
}
if tenant := tenantMap[asset.TenantID]; tenant != nil {
item.TenantCode = tenant.Code
item.TenantName = tenant.Name
}
if user := userMap[asset.UserID]; user != nil {
item.Username = user.Username
} else if asset.UserID > 0 {
item.Username = "ID:" + strconv.FormatInt(asset.UserID, 10)
}
items = append(items, item)
}
return &requests.Pager{
Pagination: filter.Pagination,
Total: total,
Items: items,
}, nil
}
func (s *super) AssetUsage(ctx context.Context, filter *super_dto.SuperAssetUsageFilter) (*super_dto.SuperAssetUsageResponse, error) {
tenantID := int64(0)
if filter != nil && filter.TenantID != nil {
tenantID = *filter.TenantID
}
tbl, q := models.MediaAssetQuery.QueryContext(ctx)
if tenantID > 0 {
q = q.Where(tbl.TenantID.Eq(tenantID))
}
total, err := q.Count()
if err != nil {
return nil, errorx.ErrDatabaseError.WithCause(err)
}
var totalSize int64
sizeQuery := models.MediaAssetQuery.WithContext(ctx).
UnderlyingDB().
Model(&models.MediaAsset{}).
Select("coalesce(sum((meta->>'size')::bigint), 0) as size")
if tenantID > 0 {
sizeQuery = sizeQuery.Where("tenant_id = ?", tenantID)
}
if err := sizeQuery.Scan(&totalSize).Error; err != nil {
return nil, errorx.ErrDatabaseError.WithCause(err)
}
type usageRow struct {
Type string `gorm:"column:type"`
Count int64 `gorm:"column:count"`
Size int64 `gorm:"column:size"`
}
rows := make([]usageRow, 0)
typeQuery := models.MediaAssetQuery.WithContext(ctx).
UnderlyingDB().
Model(&models.MediaAsset{}).
Select("type, count(*) as count, coalesce(sum((meta->>'size')::bigint), 0) as size").
Group("type")
if tenantID > 0 {
typeQuery = typeQuery.Where("tenant_id = ?", tenantID)
}
if err := typeQuery.Scan(&rows).Error; err != nil {
return nil, errorx.ErrDatabaseError.WithCause(err)
}
byType := make([]super_dto.SuperAssetUsageItem, 0, len(rows))
for _, row := range rows {
byType = append(byType, super_dto.SuperAssetUsageItem{
Type: consts.MediaAssetType(row.Type),
Count: row.Count,
TotalSize: row.Size,
})
}
return &super_dto.SuperAssetUsageResponse{
TotalCount: total,
TotalSize: totalSize,
ByType: byType,
}, nil
}
func (s *super) DeleteAsset(ctx context.Context, assetID int64, force bool) error {
if assetID <= 0 {
return errorx.ErrBadRequest.WithMsg("资产ID不能为空")
}
tbl, q := models.MediaAssetQuery.QueryContext(ctx)
asset, err := q.Where(tbl.ID.Eq(assetID)).First()
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return errorx.ErrRecordNotFound.WithMsg("资产不存在")
}
return errorx.ErrDatabaseError.WithCause(err)
}
useTbl, useQuery := models.ContentAssetQuery.QueryContext(ctx)
usedCount, err := useQuery.Where(useTbl.AssetID.Eq(assetID)).Count()
if err != nil {
return errorx.ErrDatabaseError.WithCause(err)
}
if usedCount > 0 && !force {
return errorx.ErrStatusConflict.WithMsg("资产已被内容引用,无法删除")
}
if usedCount > 0 && force {
// 强制删除时先清理引用关系,避免残留无效关联。
if _, err := useQuery.Where(useTbl.AssetID.Eq(assetID)).Delete(); err != nil {
return errorx.ErrDatabaseError.WithCause(err)
}
}
if _, err := q.Where(tbl.ID.Eq(assetID)).Delete(); err != nil {
return errorx.ErrDatabaseError.WithCause(err)
}
if Common != nil && asset.ObjectKey != "" {
count, err := models.MediaAssetQuery.WithContext(ctx).
Where(models.MediaAssetQuery.ObjectKey.Eq(asset.ObjectKey)).
Count()
if err != nil {
return errorx.ErrDatabaseError.WithCause(err)
}
if count == 0 && Common.storage != nil {
_ = Common.storage.Delete(asset.ObjectKey)
}
}
return nil
}
func (s *super) ListNotifications(ctx context.Context, filter *super_dto.SuperNotificationListFilter) (*requests.Pager, error) {
if filter == nil {
filter = &super_dto.SuperNotificationListFilter{}
}
tbl, q := models.NotificationQuery.QueryContext(ctx)
if filter.ID != nil && *filter.ID > 0 {
q = q.Where(tbl.ID.Eq(*filter.ID))
}
if filter.TenantID != nil && *filter.TenantID > 0 {
q = q.Where(tbl.TenantID.Eq(*filter.TenantID))
}
if filter.UserID != nil && *filter.UserID > 0 {
q = q.Where(tbl.UserID.Eq(*filter.UserID))
}
if filter.Type != nil && *filter.Type != "" {
q = q.Where(tbl.Type.Eq(filter.Type.String()))
}
if filter.IsRead != nil {
q = q.Where(tbl.IsRead.Is(*filter.IsRead))
}
if filter.Keyword != nil && strings.TrimSpace(*filter.Keyword) != "" {
keyword := "%" + strings.TrimSpace(*filter.Keyword) + "%"
q = q.Where(field.Or(tbl.Title.Like(keyword), tbl.Content.Like(keyword)))
}
tenantIDs, tenantFilter, err := s.lookupTenantIDs(ctx, filter.TenantCode, filter.TenantName)
if err != nil {
return nil, err
}
if tenantFilter {
if len(tenantIDs) == 0 {
q = q.Where(tbl.ID.Eq(-1))
} else {
q = q.Where(tbl.TenantID.In(tenantIDs...))
}
}
userIDs, userFilter, err := s.lookupUserIDs(ctx, filter.Username)
if err != nil {
return nil, err
}
if userFilter {
if len(userIDs) == 0 {
q = q.Where(tbl.ID.Eq(-1))
} else {
q = q.Where(tbl.UserID.In(userIDs...))
}
}
if filter.CreatedAtFrom != nil {
from, err := s.parseFilterTime(filter.CreatedAtFrom)
if err != nil {
return nil, err
}
if from != nil {
q = q.Where(tbl.CreatedAt.Gte(*from))
}
}
if filter.CreatedAtTo != nil {
to, err := s.parseFilterTime(filter.CreatedAtTo)
if err != nil {
return nil, err
}
if to != nil {
q = q.Where(tbl.CreatedAt.Lte(*to))
}
}
orderApplied := false
if filter.Desc != nil && strings.TrimSpace(*filter.Desc) != "" {
switch strings.TrimSpace(*filter.Desc) {
case "id":
q = q.Order(tbl.ID.Desc())
case "created_at":
q = q.Order(tbl.CreatedAt.Desc())
}
orderApplied = true
} else if filter.Asc != nil && strings.TrimSpace(*filter.Asc) != "" {
switch strings.TrimSpace(*filter.Asc) {
case "id":
q = q.Order(tbl.ID)
case "created_at":
q = q.Order(tbl.CreatedAt)
}
orderApplied = true
}
if !orderApplied {
q = q.Order(tbl.CreatedAt.Desc())
}
filter.Pagination.Format()
total, err := q.Count()
if err != nil {
return nil, errorx.ErrDatabaseError.WithCause(err)
}
list, err := q.Offset(int(filter.Pagination.Offset())).Limit(int(filter.Pagination.Limit)).Find()
if err != nil {
return nil, errorx.ErrDatabaseError.WithCause(err)
}
if len(list) == 0 {
return &requests.Pager{
Pagination: filter.Pagination,
Total: total,
Items: []super_dto.SuperNotificationItem{},
}, nil
}
tenantSet := make(map[int64]struct{})
userSet := make(map[int64]struct{})
for _, n := range list {
if n.TenantID > 0 {
tenantSet[n.TenantID] = struct{}{}
}
if n.UserID > 0 {
userSet[n.UserID] = struct{}{}
}
}
tenantMap := make(map[int64]*models.Tenant, len(tenantSet))
if len(tenantSet) > 0 {
ids := make([]int64, 0, len(tenantSet))
for id := range tenantSet {
ids = append(ids, id)
}
tenantTbl, tenantQuery := models.TenantQuery.QueryContext(ctx)
tenants, err := tenantQuery.Where(tenantTbl.ID.In(ids...)).Find()
if err != nil {
return nil, errorx.ErrDatabaseError.WithCause(err)
}
for _, tenant := range tenants {
tenantMap[tenant.ID] = tenant
}
}
userMap := make(map[int64]*models.User, len(userSet))
if len(userSet) > 0 {
ids := make([]int64, 0, len(userSet))
for id := range userSet {
ids = append(ids, id)
}
userTbl, userQuery := models.UserQuery.QueryContext(ctx)
users, err := userQuery.Where(userTbl.ID.In(ids...)).Find()
if err != nil {
return nil, errorx.ErrDatabaseError.WithCause(err)
}
for _, user := range users {
userMap[user.ID] = user
}
}
items := make([]super_dto.SuperNotificationItem, 0, len(list))
for _, n := range list {
item := super_dto.SuperNotificationItem{
ID: n.ID,
TenantID: n.TenantID,
UserID: n.UserID,
Type: consts.NotificationType(n.Type),
Title: n.Title,
Content: n.Content,
IsRead: n.IsRead,
CreatedAt: s.formatTime(n.CreatedAt),
}
if tenant := tenantMap[n.TenantID]; tenant != nil {
item.TenantCode = tenant.Code
item.TenantName = tenant.Name
}
if user := userMap[n.UserID]; user != nil {
item.Username = user.Username
} else if n.UserID > 0 {
item.Username = "ID:" + strconv.FormatInt(n.UserID, 10)
}
items = append(items, item)
}
return &requests.Pager{
Pagination: filter.Pagination,
Total: total,
Items: items,
}, nil
}
func (s *super) BroadcastNotifications(ctx context.Context, form *super_dto.SuperNotificationBroadcastForm) error {
if form == nil {
return errorx.ErrBadRequest.WithMsg("群发参数不能为空")
}
title := strings.TrimSpace(form.Title)
content := strings.TrimSpace(form.Content)
if title == "" || content == "" {
return errorx.ErrBadRequest.WithMsg("通知标题和内容不能为空")
}
if !form.Type.IsValid() {
return errorx.ErrBadRequest.WithMsg("通知类型非法")
}
tenantID := int64(0)
if form.TenantID != nil {
tenantID = *form.TenantID
}
userSet := make(map[int64]struct{})
for _, id := range form.UserIDs {
if id <= 0 {
continue
}
userSet[id] = struct{}{}
}
if len(userSet) == 0 && tenantID <= 0 {
return errorx.ErrBadRequest.WithMsg("请指定租户或用户")
}
if len(userSet) == 0 && tenantID > 0 {
// 仅向该租户的已验证成员发送。
tbl, q := models.TenantUserQuery.QueryContext(ctx)
list, err := q.Where(tbl.TenantID.Eq(tenantID), tbl.Status.Eq(consts.UserStatusVerified)).Find()
if err != nil {
return errorx.ErrDatabaseError.WithCause(err)
}
for _, tu := range list {
if tu.UserID > 0 {
userSet[tu.UserID] = struct{}{}
}
}
}
if len(userSet) == 0 {
return errorx.ErrRecordNotFound.WithMsg("未找到可通知的用户")
}
if Notification == nil {
return errorx.ErrInternalError.WithMsg("通知服务不可用")
}
typ := form.Type.String()
for userID := range userSet {
if err := Notification.Send(ctx, tenantID, userID, typ, title, content); err != nil {
return err
}
}
return nil
}
func (s *super) ListNotificationTemplates(ctx context.Context, filter *super_dto.SuperNotificationTemplateListFilter) (*requests.Pager, error) {
if filter == nil {
filter = &super_dto.SuperNotificationTemplateListFilter{}
}
tbl, q := models.NotificationTemplateQuery.QueryContext(ctx)
if filter.TenantID != nil && *filter.TenantID >= 0 {
q = q.Where(tbl.TenantID.Eq(*filter.TenantID))
}
if filter.Type != nil && *filter.Type != "" {
q = q.Where(tbl.Type.Eq(*filter.Type))
}
if filter.IsActive != nil {
q = q.Where(tbl.IsActive.Is(*filter.IsActive))
}
if filter.Keyword != nil && strings.TrimSpace(*filter.Keyword) != "" {
keyword := "%" + strings.TrimSpace(*filter.Keyword) + "%"
q = q.Where(field.Or(tbl.Name.Like(keyword), tbl.Title.Like(keyword)))
}
if filter.CreatedAtFrom != nil {
from, err := s.parseFilterTime(filter.CreatedAtFrom)
if err != nil {
return nil, err
}
if from != nil {
q = q.Where(tbl.CreatedAt.Gte(*from))
}
}
if filter.CreatedAtTo != nil {
to, err := s.parseFilterTime(filter.CreatedAtTo)
if err != nil {
return nil, err
}
if to != nil {
q = q.Where(tbl.CreatedAt.Lte(*to))
}
}
orderApplied := false
if filter.Desc != nil && strings.TrimSpace(*filter.Desc) != "" {
switch strings.TrimSpace(*filter.Desc) {
case "id":
q = q.Order(tbl.ID.Desc())
case "created_at":
q = q.Order(tbl.CreatedAt.Desc())
}
orderApplied = true
} else if filter.Asc != nil && strings.TrimSpace(*filter.Asc) != "" {
switch strings.TrimSpace(*filter.Asc) {
case "id":
q = q.Order(tbl.ID)
case "created_at":
q = q.Order(tbl.CreatedAt)
}
orderApplied = true
}
if !orderApplied {
q = q.Order(tbl.CreatedAt.Desc())
}
filter.Pagination.Format()
total, err := q.Count()
if err != nil {
return nil, errorx.ErrDatabaseError.WithCause(err)
}
list, err := q.Offset(int(filter.Pagination.Offset())).Limit(int(filter.Pagination.Limit)).Find()
if err != nil {
return nil, errorx.ErrDatabaseError.WithCause(err)
}
if len(list) == 0 {
return &requests.Pager{
Pagination: filter.Pagination,
Total: total,
Items: []super_dto.SuperNotificationTemplateItem{},
}, nil
}
tenantSet := make(map[int64]struct{})
for _, tmpl := range list {
if tmpl.TenantID > 0 {
tenantSet[tmpl.TenantID] = struct{}{}
}
}
tenantMap := make(map[int64]*models.Tenant, len(tenantSet))
if len(tenantSet) > 0 {
ids := make([]int64, 0, len(tenantSet))
for id := range tenantSet {
ids = append(ids, id)
}
tenantTbl, tenantQuery := models.TenantQuery.QueryContext(ctx)
tenants, err := tenantQuery.Where(tenantTbl.ID.In(ids...)).Find()
if err != nil {
return nil, errorx.ErrDatabaseError.WithCause(err)
}
for _, tenant := range tenants {
tenantMap[tenant.ID] = tenant
}
}
items := make([]super_dto.SuperNotificationTemplateItem, 0, len(list))
for _, tmpl := range list {
item := super_dto.SuperNotificationTemplateItem{
ID: tmpl.ID,
TenantID: tmpl.TenantID,
Name: tmpl.Name,
Type: tmpl.Type,
Title: tmpl.Title,
Content: tmpl.Content,
IsActive: tmpl.IsActive,
CreatedAt: s.formatTime(tmpl.CreatedAt),
UpdatedAt: s.formatTime(tmpl.UpdatedAt),
}
if tenant := tenantMap[tmpl.TenantID]; tenant != nil {
item.TenantCode = tenant.Code
item.TenantName = tenant.Name
}
items = append(items, item)
}
return &requests.Pager{
Pagination: filter.Pagination,
Total: total,
Items: items,
}, nil
}
func (s *super) CreateNotificationTemplate(ctx context.Context, form *super_dto.SuperNotificationTemplateCreateForm) (*super_dto.SuperNotificationTemplateItem, error) {
if form == nil {
return nil, errorx.ErrBadRequest.WithMsg("模板参数不能为空")
}
name := strings.TrimSpace(form.Name)
title := strings.TrimSpace(form.Title)
content := strings.TrimSpace(form.Content)
if name == "" || title == "" || content == "" {
return nil, errorx.ErrBadRequest.WithMsg("模板名称、标题和内容不能为空")
}
if !form.Type.IsValid() {
return nil, errorx.ErrBadRequest.WithMsg("通知类型非法")
}
tenantID := int64(0)
if form.TenantID != nil {
tenantID = *form.TenantID
}
isActive := true
if form.IsActive != nil {
isActive = *form.IsActive
}
tmpl := &models.NotificationTemplate{
TenantID: tenantID,
Name: name,
Type: form.Type,
Title: title,
Content: content,
IsActive: isActive,
}
if err := models.NotificationTemplateQuery.WithContext(ctx).Create(tmpl); err != nil {
return nil, errorx.ErrDatabaseError.WithCause(err)
}
item := &super_dto.SuperNotificationTemplateItem{
ID: tmpl.ID,
TenantID: tmpl.TenantID,
Name: tmpl.Name,
Type: tmpl.Type,
Title: tmpl.Title,
Content: tmpl.Content,
IsActive: tmpl.IsActive,
CreatedAt: s.formatTime(tmpl.CreatedAt),
UpdatedAt: s.formatTime(tmpl.UpdatedAt),
}
if tmpl.TenantID > 0 {
tenantTbl, tenantQuery := models.TenantQuery.QueryContext(ctx)
tenant, err := tenantQuery.Where(tenantTbl.ID.Eq(tmpl.TenantID)).First()
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errorx.ErrDatabaseError.WithCause(err)
}
if tenant != nil {
item.TenantCode = tenant.Code
item.TenantName = tenant.Name
}
}
return item, nil
}
func (s *super) ListOrders(ctx context.Context, filter *super_dto.SuperOrderListFilter) (*requests.Pager, error) {
tbl, q := models.OrderQuery.QueryContext(ctx)