feat: add audit logs and system configs
This commit is contained in:
28
backend/app/http/super/v1/audit_logs.go
Normal file
28
backend/app/http/super/v1/audit_logs.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
dto "quyun/v2/app/http/super/v1/dto"
|
||||
"quyun/v2/app/requests"
|
||||
"quyun/v2/app/services"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
)
|
||||
|
||||
// @provider
|
||||
type auditLogs struct{}
|
||||
|
||||
// List audit logs
|
||||
//
|
||||
// @Router /super/v1/audit-logs [get]
|
||||
// @Summary List audit logs
|
||||
// @Description List audit logs across tenants
|
||||
// @Tags Audit
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param page query int false "Page number"
|
||||
// @Param limit query int false "Page size"
|
||||
// @Success 200 {object} requests.Pager{items=[]dto.SuperAuditLogItem}
|
||||
// @Bind filter query
|
||||
func (c *auditLogs) List(ctx fiber.Ctx, filter *dto.SuperAuditLogListFilter) (*requests.Pager, error) {
|
||||
return services.Super.ListAuditLogs(ctx, filter)
|
||||
}
|
||||
117
backend/app/http/super/v1/dto/super_audit.go
Normal file
117
backend/app/http/super/v1/dto/super_audit.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"quyun/v2/app/requests"
|
||||
)
|
||||
|
||||
// SuperAuditLogListFilter 超管审计日志列表过滤条件。
|
||||
type SuperAuditLogListFilter struct {
|
||||
requests.Pagination
|
||||
// ID 审计日志ID,精确匹配。
|
||||
ID *int64 `query:"id"`
|
||||
// TenantID 租户ID过滤。
|
||||
TenantID *int64 `query:"tenant_id"`
|
||||
// TenantCode 租户编码,模糊匹配。
|
||||
TenantCode *string `query:"tenant_code"`
|
||||
// TenantName 租户名称,模糊匹配。
|
||||
TenantName *string `query:"tenant_name"`
|
||||
// OperatorID 操作者用户ID,精确匹配。
|
||||
OperatorID *int64 `query:"operator_id"`
|
||||
// OperatorName 操作者用户名/昵称,模糊匹配。
|
||||
OperatorName *string `query:"operator_name"`
|
||||
// Action 动作标识过滤,精确匹配。
|
||||
Action *string `query:"action"`
|
||||
// TargetID 目标ID过滤,精确匹配。
|
||||
TargetID *string `query:"target_id"`
|
||||
// Keyword 详情关键词,模糊匹配。
|
||||
Keyword *string `query:"keyword"`
|
||||
// CreatedAtFrom 创建时间起始(RFC3339)。
|
||||
CreatedAtFrom *string `query:"created_at_from"`
|
||||
// CreatedAtTo 创建时间结束(RFC3339)。
|
||||
CreatedAtTo *string `query:"created_at_to"`
|
||||
// Asc 升序字段(id/created_at)。
|
||||
Asc *string `query:"asc"`
|
||||
// Desc 降序字段(id/created_at)。
|
||||
Desc *string `query:"desc"`
|
||||
}
|
||||
|
||||
// SuperAuditLogItem 超管审计日志条目。
|
||||
type SuperAuditLogItem struct {
|
||||
// ID 审计日志ID。
|
||||
ID int64 `json:"id"`
|
||||
// TenantID 租户ID。
|
||||
TenantID int64 `json:"tenant_id"`
|
||||
// TenantCode 租户编码。
|
||||
TenantCode string `json:"tenant_code"`
|
||||
// TenantName 租户名称。
|
||||
TenantName string `json:"tenant_name"`
|
||||
// OperatorID 操作者用户ID。
|
||||
OperatorID int64 `json:"operator_id"`
|
||||
// OperatorName 操作者用户名/昵称。
|
||||
OperatorName string `json:"operator_name"`
|
||||
// Action 动作标识。
|
||||
Action string `json:"action"`
|
||||
// TargetID 目标ID。
|
||||
TargetID string `json:"target_id"`
|
||||
// Detail 操作详情。
|
||||
Detail string `json:"detail"`
|
||||
// CreatedAt 创建时间(RFC3339)。
|
||||
CreatedAt string `json:"created_at"`
|
||||
}
|
||||
|
||||
// SuperSystemConfigListFilter 超管系统配置列表过滤条件。
|
||||
type SuperSystemConfigListFilter struct {
|
||||
requests.Pagination
|
||||
// ConfigKey 配置Key,精确匹配。
|
||||
ConfigKey *string `query:"config_key"`
|
||||
// Keyword Key或描述关键词,模糊匹配。
|
||||
Keyword *string `query:"keyword"`
|
||||
// CreatedAtFrom 创建时间起始(RFC3339)。
|
||||
CreatedAtFrom *string `query:"created_at_from"`
|
||||
// CreatedAtTo 创建时间结束(RFC3339)。
|
||||
CreatedAtTo *string `query:"created_at_to"`
|
||||
// UpdatedAtFrom 更新时间起始(RFC3339)。
|
||||
UpdatedAtFrom *string `query:"updated_at_from"`
|
||||
// UpdatedAtTo 更新时间结束(RFC3339)。
|
||||
UpdatedAtTo *string `query:"updated_at_to"`
|
||||
// Asc 升序字段(id/config_key/updated_at)。
|
||||
Asc *string `query:"asc"`
|
||||
// Desc 降序字段(id/config_key/updated_at)。
|
||||
Desc *string `query:"desc"`
|
||||
}
|
||||
|
||||
// SuperSystemConfigCreateForm 超管系统配置创建参数。
|
||||
type SuperSystemConfigCreateForm struct {
|
||||
// ConfigKey 配置项Key(唯一)。
|
||||
ConfigKey string `json:"config_key"`
|
||||
// Value 配置值(JSON)。
|
||||
Value json.RawMessage `json:"value"`
|
||||
// Description 配置说明。
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// SuperSystemConfigUpdateForm 超管系统配置更新参数。
|
||||
type SuperSystemConfigUpdateForm struct {
|
||||
// Value 配置值(JSON,可选)。
|
||||
Value *json.RawMessage `json:"value"`
|
||||
// Description 配置说明(可选)。
|
||||
Description *string `json:"description"`
|
||||
}
|
||||
|
||||
// SuperSystemConfigItem 超管系统配置条目。
|
||||
type SuperSystemConfigItem struct {
|
||||
// ID 配置ID。
|
||||
ID int64 `json:"id"`
|
||||
// ConfigKey 配置项Key。
|
||||
ConfigKey string `json:"config_key"`
|
||||
// Value 配置值(JSON)。
|
||||
Value json.RawMessage `json:"value"`
|
||||
// Description 配置说明。
|
||||
Description string `json:"description"`
|
||||
// CreatedAt 创建时间(RFC3339)。
|
||||
CreatedAt string `json:"created_at"`
|
||||
// UpdatedAt 更新时间(RFC3339)。
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
@@ -17,6 +17,13 @@ func Provide(opts ...opt.Option) error {
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := container.Container.Provide(func() (*auditLogs, error) {
|
||||
obj := &auditLogs{}
|
||||
|
||||
return obj, nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := container.Container.Provide(func() (*contents, error) {
|
||||
obj := &contents{}
|
||||
|
||||
@@ -75,6 +82,7 @@ func Provide(opts ...opt.Option) error {
|
||||
}
|
||||
if err := container.Container.Provide(func(
|
||||
assets *assets,
|
||||
auditLogs *auditLogs,
|
||||
contents *contents,
|
||||
coupons *coupons,
|
||||
creatorApplications *creatorApplications,
|
||||
@@ -84,12 +92,14 @@ func Provide(opts ...opt.Option) error {
|
||||
orders *orders,
|
||||
payoutAccounts *payoutAccounts,
|
||||
reports *reports,
|
||||
systemConfigs *systemConfigs,
|
||||
tenants *tenants,
|
||||
users *users,
|
||||
withdrawals *withdrawals,
|
||||
) (contracts.HttpRoute, error) {
|
||||
obj := &Routes{
|
||||
assets: assets,
|
||||
auditLogs: auditLogs,
|
||||
contents: contents,
|
||||
coupons: coupons,
|
||||
creatorApplications: creatorApplications,
|
||||
@@ -99,6 +109,7 @@ func Provide(opts ...opt.Option) error {
|
||||
orders: orders,
|
||||
payoutAccounts: payoutAccounts,
|
||||
reports: reports,
|
||||
systemConfigs: systemConfigs,
|
||||
tenants: tenants,
|
||||
users: users,
|
||||
withdrawals: withdrawals,
|
||||
@@ -111,6 +122,13 @@ func Provide(opts ...opt.Option) error {
|
||||
}, atom.GroupRoutes); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := container.Container.Provide(func() (*systemConfigs, error) {
|
||||
obj := &systemConfigs{}
|
||||
|
||||
return obj, nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := container.Container.Provide(func() (*tenants, error) {
|
||||
obj := &tenants{}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ type Routes struct {
|
||||
middlewares *middlewares.Middlewares
|
||||
// Controller instances
|
||||
assets *assets
|
||||
auditLogs *auditLogs
|
||||
contents *contents
|
||||
coupons *coupons
|
||||
creatorApplications *creatorApplications
|
||||
@@ -34,6 +35,7 @@ type Routes struct {
|
||||
orders *orders
|
||||
payoutAccounts *payoutAccounts
|
||||
reports *reports
|
||||
systemConfigs *systemConfigs
|
||||
tenants *tenants
|
||||
users *users
|
||||
withdrawals *withdrawals
|
||||
@@ -71,6 +73,12 @@ func (r *Routes) Register(router fiber.Router) {
|
||||
r.assets.Usage,
|
||||
Query[dto.SuperAssetUsageFilter]("filter"),
|
||||
))
|
||||
// Register routes for controller: auditLogs
|
||||
r.log.Debugf("Registering route: Get /super/v1/audit-logs -> auditLogs.List")
|
||||
router.Get("/super/v1/audit-logs"[len(r.Path()):], DataFunc1(
|
||||
r.auditLogs.List,
|
||||
Query[dto.SuperAuditLogListFilter]("filter"),
|
||||
))
|
||||
// Register routes for controller: contents
|
||||
r.log.Debugf("Registering route: Get /super/v1/contents -> contents.List")
|
||||
router.Get("/super/v1/contents"[len(r.Path()):], DataFunc1(
|
||||
@@ -243,6 +251,25 @@ func (r *Routes) Register(router fiber.Router) {
|
||||
r.reports.Export,
|
||||
Body[dto.SuperReportExportForm]("form"),
|
||||
))
|
||||
// Register routes for controller: systemConfigs
|
||||
r.log.Debugf("Registering route: Get /super/v1/system-configs -> systemConfigs.List")
|
||||
router.Get("/super/v1/system-configs"[len(r.Path()):], DataFunc1(
|
||||
r.systemConfigs.List,
|
||||
Query[dto.SuperSystemConfigListFilter]("filter"),
|
||||
))
|
||||
r.log.Debugf("Registering route: Patch /super/v1/system-configs/:id<int> -> systemConfigs.Update")
|
||||
router.Patch("/super/v1/system-configs/:id<int>"[len(r.Path()):], DataFunc3(
|
||||
r.systemConfigs.Update,
|
||||
Local[*models.User]("__ctx_user"),
|
||||
PathParam[int64]("id"),
|
||||
Body[dto.SuperSystemConfigUpdateForm]("form"),
|
||||
))
|
||||
r.log.Debugf("Registering route: Post /super/v1/system-configs -> systemConfigs.Create")
|
||||
router.Post("/super/v1/system-configs"[len(r.Path()):], DataFunc2(
|
||||
r.systemConfigs.Create,
|
||||
Local[*models.User]("__ctx_user"),
|
||||
Body[dto.SuperSystemConfigCreateForm]("form"),
|
||||
))
|
||||
// Register routes for controller: tenants
|
||||
r.log.Debugf("Registering route: Get /super/v1/tenant-join-requests -> tenants.ListJoinRequests")
|
||||
router.Get("/super/v1/tenant-join-requests"[len(r.Path()):], DataFunc1(
|
||||
|
||||
63
backend/app/http/super/v1/system_configs.go
Normal file
63
backend/app/http/super/v1/system_configs.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
dto "quyun/v2/app/http/super/v1/dto"
|
||||
"quyun/v2/app/requests"
|
||||
"quyun/v2/app/services"
|
||||
"quyun/v2/database/models"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
)
|
||||
|
||||
// @provider
|
||||
type systemConfigs struct{}
|
||||
|
||||
// List system configs
|
||||
//
|
||||
// @Router /super/v1/system-configs [get]
|
||||
// @Summary List system configs
|
||||
// @Description List platform system configs
|
||||
// @Tags SystemConfig
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param page query int false "Page number"
|
||||
// @Param limit query int false "Page size"
|
||||
// @Success 200 {object} requests.Pager{items=[]dto.SuperSystemConfigItem}
|
||||
// @Bind filter query
|
||||
func (c *systemConfigs) List(ctx fiber.Ctx, filter *dto.SuperSystemConfigListFilter) (*requests.Pager, error) {
|
||||
return services.Super.ListSystemConfigs(ctx, filter)
|
||||
}
|
||||
|
||||
// Create system config
|
||||
//
|
||||
// @Router /super/v1/system-configs [post]
|
||||
// @Summary Create system config
|
||||
// @Description Create platform system config
|
||||
// @Tags SystemConfig
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param form body dto.SuperSystemConfigCreateForm true "Create form"
|
||||
// @Success 200 {object} dto.SuperSystemConfigItem
|
||||
// @Bind user local key(__ctx_user)
|
||||
// @Bind form body
|
||||
func (c *systemConfigs) Create(ctx fiber.Ctx, user *models.User, form *dto.SuperSystemConfigCreateForm) (*dto.SuperSystemConfigItem, error) {
|
||||
return services.Super.CreateSystemConfig(ctx, user.ID, form)
|
||||
}
|
||||
|
||||
// Update system config
|
||||
//
|
||||
// @Router /super/v1/system-configs/:id<int> [patch]
|
||||
// @Summary Update system config
|
||||
// @Description Update platform system config
|
||||
// @Tags SystemConfig
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int64 true "Config ID"
|
||||
// @Param form body dto.SuperSystemConfigUpdateForm true "Update form"
|
||||
// @Success 200 {object} dto.SuperSystemConfigItem
|
||||
// @Bind user local key(__ctx_user)
|
||||
// @Bind id path
|
||||
// @Bind form body
|
||||
func (c *systemConfigs) Update(ctx fiber.Ctx, user *models.User, id int64, form *dto.SuperSystemConfigUpdateForm) (*dto.SuperSystemConfigItem, error) {
|
||||
return services.Super.UpdateSystemConfig(ctx, user.ID, id, form)
|
||||
}
|
||||
@@ -3,18 +3,38 @@ package services
|
||||
import (
|
||||
"context"
|
||||
|
||||
"quyun/v2/database/models"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// @provider
|
||||
type audit struct{}
|
||||
|
||||
func (s *audit) Log(ctx context.Context, operatorID int64, action, targetID, detail string) {
|
||||
func (s *audit) Log(ctx context.Context, tenantID, operatorID int64, action, targetID, detail string) {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"audit": true,
|
||||
"tenant": tenantID,
|
||||
"operator": operatorID,
|
||||
"action": action,
|
||||
"target": targetID,
|
||||
"detail": detail,
|
||||
}).Info("Audit Log")
|
||||
|
||||
entry := &models.AuditLog{
|
||||
TenantID: tenantID,
|
||||
OperatorID: operatorID,
|
||||
Action: action,
|
||||
TargetID: targetID,
|
||||
Detail: detail,
|
||||
}
|
||||
if err := models.AuditLogQuery.WithContext(ctx).Create(entry); err != nil {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"audit": true,
|
||||
"tenant": tenantID,
|
||||
"operator": operatorID,
|
||||
"action": action,
|
||||
"target": targetID,
|
||||
}).WithError(err).Warn("Audit log persist failed")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1019,7 +1019,7 @@ func (s *super) ReviewCreatorApplication(ctx context.Context, operatorID, tenant
|
||||
if strings.TrimSpace(form.Reason) != "" {
|
||||
detail += ",原因:" + strings.TrimSpace(form.Reason)
|
||||
}
|
||||
Audit.Log(ctx, operatorID, "review_creator_application", cast.ToString(tenant.ID), detail)
|
||||
Audit.Log(ctx, tenant.ID, operatorID, "review_creator_application", cast.ToString(tenant.ID), detail)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -1317,7 +1317,7 @@ func (s *super) RemovePayoutAccount(ctx context.Context, operatorID, id int64) e
|
||||
}
|
||||
|
||||
if Audit != nil {
|
||||
Audit.Log(ctx, operatorID, "remove_payout_account", cast.ToString(account.ID), "Removed payout account")
|
||||
Audit.Log(ctx, account.TenantID, operatorID, "remove_payout_account", cast.ToString(account.ID), "Removed payout account")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -2376,7 +2376,7 @@ func (s *super) ReviewContent(ctx context.Context, operatorID, contentID int64,
|
||||
_ = Notification.Send(ctx, content.TenantID, content.UserID, string(consts.NotificationTypeAudit), title, detail)
|
||||
}
|
||||
if Audit != nil {
|
||||
Audit.Log(ctx, operatorID, "review_content", cast.ToString(contentID), detail)
|
||||
Audit.Log(ctx, content.TenantID, operatorID, "review_content", cast.ToString(contentID), detail)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -2465,7 +2465,7 @@ func (s *super) BatchReviewContents(ctx context.Context, operatorID int64, form
|
||||
_ = Notification.Send(ctx, content.TenantID, content.UserID, string(consts.NotificationTypeAudit), title, detail)
|
||||
}
|
||||
if Audit != nil {
|
||||
Audit.Log(ctx, operatorID, "review_content", cast.ToString(content.ID), detail)
|
||||
Audit.Log(ctx, content.TenantID, operatorID, "review_content", cast.ToString(content.ID), detail)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3318,6 +3318,431 @@ func (s *super) CreateNotificationTemplate(ctx context.Context, form *super_dto.
|
||||
return item, nil
|
||||
}
|
||||
|
||||
func (s *super) ListAuditLogs(ctx context.Context, filter *super_dto.SuperAuditLogListFilter) (*requests.Pager, error) {
|
||||
if filter == nil {
|
||||
filter = &super_dto.SuperAuditLogListFilter{}
|
||||
}
|
||||
|
||||
tbl, q := models.AuditLogQuery.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.OperatorID != nil && *filter.OperatorID > 0 {
|
||||
q = q.Where(tbl.OperatorID.Eq(*filter.OperatorID))
|
||||
}
|
||||
if filter.Action != nil && strings.TrimSpace(*filter.Action) != "" {
|
||||
q = q.Where(tbl.Action.Eq(strings.TrimSpace(*filter.Action)))
|
||||
}
|
||||
if filter.TargetID != nil && strings.TrimSpace(*filter.TargetID) != "" {
|
||||
q = q.Where(tbl.TargetID.Eq(strings.TrimSpace(*filter.TargetID)))
|
||||
}
|
||||
if filter.Keyword != nil && strings.TrimSpace(*filter.Keyword) != "" {
|
||||
keyword := "%" + strings.TrimSpace(*filter.Keyword) + "%"
|
||||
q = q.Where(field.Or(tbl.Detail.Like(keyword), tbl.Action.Like(keyword), tbl.TargetID.Like(keyword)))
|
||||
}
|
||||
|
||||
// 跨租户筛选:根据租户编码/名称定位租户ID。
|
||||
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...))
|
||||
}
|
||||
}
|
||||
|
||||
// 根据操作者昵称/用户名定位用户ID。
|
||||
operatorIDs, operatorFilter, err := s.lookupUserIDs(ctx, filter.OperatorName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if operatorFilter {
|
||||
if len(operatorIDs) == 0 {
|
||||
q = q.Where(tbl.ID.Eq(-1))
|
||||
} else {
|
||||
q = q.Where(tbl.OperatorID.In(operatorIDs...))
|
||||
}
|
||||
}
|
||||
|
||||
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.SuperAuditLogItem{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
tenantSet := make(map[int64]struct{})
|
||||
operatorSet := make(map[int64]struct{})
|
||||
for _, log := range list {
|
||||
if log.TenantID > 0 {
|
||||
tenantSet[log.TenantID] = struct{}{}
|
||||
}
|
||||
if log.OperatorID > 0 {
|
||||
operatorSet[log.OperatorID] = 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
|
||||
}
|
||||
}
|
||||
|
||||
operatorMap := make(map[int64]*models.User, len(operatorSet))
|
||||
if len(operatorSet) > 0 {
|
||||
ids := make([]int64, 0, len(operatorSet))
|
||||
for id := range operatorSet {
|
||||
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 {
|
||||
operatorMap[user.ID] = user
|
||||
}
|
||||
}
|
||||
|
||||
items := make([]super_dto.SuperAuditLogItem, 0, len(list))
|
||||
for _, log := range list {
|
||||
item := super_dto.SuperAuditLogItem{
|
||||
ID: log.ID,
|
||||
TenantID: log.TenantID,
|
||||
OperatorID: log.OperatorID,
|
||||
Action: log.Action,
|
||||
TargetID: log.TargetID,
|
||||
Detail: log.Detail,
|
||||
CreatedAt: s.formatTime(log.CreatedAt),
|
||||
}
|
||||
if tenant := tenantMap[log.TenantID]; tenant != nil {
|
||||
item.TenantCode = tenant.Code
|
||||
item.TenantName = tenant.Name
|
||||
}
|
||||
if operator := operatorMap[log.OperatorID]; operator != nil {
|
||||
item.OperatorName = operator.Username
|
||||
} else if log.OperatorID > 0 {
|
||||
item.OperatorName = "ID:" + strconv.FormatInt(log.OperatorID, 10)
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
|
||||
return &requests.Pager{
|
||||
Pagination: filter.Pagination,
|
||||
Total: total,
|
||||
Items: items,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *super) ListSystemConfigs(ctx context.Context, filter *super_dto.SuperSystemConfigListFilter) (*requests.Pager, error) {
|
||||
if filter == nil {
|
||||
filter = &super_dto.SuperSystemConfigListFilter{}
|
||||
}
|
||||
|
||||
tbl, q := models.SystemConfigQuery.QueryContext(ctx)
|
||||
|
||||
if filter.ConfigKey != nil && strings.TrimSpace(*filter.ConfigKey) != "" {
|
||||
q = q.Where(tbl.ConfigKey.Eq(strings.TrimSpace(*filter.ConfigKey)))
|
||||
}
|
||||
if filter.Keyword != nil && strings.TrimSpace(*filter.Keyword) != "" {
|
||||
keyword := "%" + strings.TrimSpace(*filter.Keyword) + "%"
|
||||
q = q.Where(field.Or(tbl.ConfigKey.Like(keyword), tbl.Description.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))
|
||||
}
|
||||
}
|
||||
if filter.UpdatedAtFrom != nil {
|
||||
from, err := s.parseFilterTime(filter.UpdatedAtFrom)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if from != nil {
|
||||
q = q.Where(tbl.UpdatedAt.Gte(*from))
|
||||
}
|
||||
}
|
||||
if filter.UpdatedAtTo != nil {
|
||||
to, err := s.parseFilterTime(filter.UpdatedAtTo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if to != nil {
|
||||
q = q.Where(tbl.UpdatedAt.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 "config_key":
|
||||
q = q.Order(tbl.ConfigKey.Desc())
|
||||
case "updated_at":
|
||||
q = q.Order(tbl.UpdatedAt.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 "config_key":
|
||||
q = q.Order(tbl.ConfigKey)
|
||||
case "updated_at":
|
||||
q = q.Order(tbl.UpdatedAt)
|
||||
case "created_at":
|
||||
q = q.Order(tbl.CreatedAt)
|
||||
}
|
||||
orderApplied = true
|
||||
}
|
||||
if !orderApplied {
|
||||
q = q.Order(tbl.UpdatedAt.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.SuperSystemConfigItem{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
items := make([]super_dto.SuperSystemConfigItem, 0, len(list))
|
||||
for _, cfg := range list {
|
||||
items = append(items, super_dto.SuperSystemConfigItem{
|
||||
ID: cfg.ID,
|
||||
ConfigKey: cfg.ConfigKey,
|
||||
Value: json.RawMessage(cfg.Value),
|
||||
Description: cfg.Description,
|
||||
CreatedAt: s.formatTime(cfg.CreatedAt),
|
||||
UpdatedAt: s.formatTime(cfg.UpdatedAt),
|
||||
})
|
||||
}
|
||||
|
||||
return &requests.Pager{
|
||||
Pagination: filter.Pagination,
|
||||
Total: total,
|
||||
Items: items,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *super) CreateSystemConfig(ctx context.Context, operatorID int64, form *super_dto.SuperSystemConfigCreateForm) (*super_dto.SuperSystemConfigItem, error) {
|
||||
if operatorID == 0 {
|
||||
return nil, errorx.ErrUnauthorized.WithMsg("缺少操作者信息")
|
||||
}
|
||||
if form == nil {
|
||||
return nil, errorx.ErrBadRequest.WithMsg("配置参数不能为空")
|
||||
}
|
||||
|
||||
key := strings.TrimSpace(form.ConfigKey)
|
||||
if key == "" {
|
||||
return nil, errorx.ErrBadRequest.WithMsg("配置Key不能为空")
|
||||
}
|
||||
if len(form.Value) == 0 || !json.Valid(form.Value) {
|
||||
return nil, errorx.ErrBadRequest.WithMsg("配置值必须是合法JSON")
|
||||
}
|
||||
desc := strings.TrimSpace(form.Description)
|
||||
if desc == "" {
|
||||
return nil, errorx.ErrBadRequest.WithMsg("配置说明不能为空")
|
||||
}
|
||||
|
||||
// 配置Key唯一,重复提交直接提示。
|
||||
tbl, q := models.SystemConfigQuery.QueryContext(ctx)
|
||||
_, err := q.Where(tbl.ConfigKey.Eq(key)).First()
|
||||
if err == nil {
|
||||
return nil, errorx.ErrRecordDuplicated.WithMsg("配置Key已存在")
|
||||
}
|
||||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
|
||||
cfg := &models.SystemConfig{
|
||||
ConfigKey: key,
|
||||
Value: types.JSON(form.Value),
|
||||
Description: desc,
|
||||
}
|
||||
if err := q.Create(cfg); err != nil {
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
|
||||
if Audit != nil {
|
||||
Audit.Log(ctx, 0, operatorID, "create_system_config", cfg.ConfigKey, "创建系统配置")
|
||||
}
|
||||
|
||||
return &super_dto.SuperSystemConfigItem{
|
||||
ID: cfg.ID,
|
||||
ConfigKey: cfg.ConfigKey,
|
||||
Value: json.RawMessage(cfg.Value),
|
||||
Description: cfg.Description,
|
||||
CreatedAt: s.formatTime(cfg.CreatedAt),
|
||||
UpdatedAt: s.formatTime(cfg.UpdatedAt),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *super) UpdateSystemConfig(ctx context.Context, operatorID, id int64, form *super_dto.SuperSystemConfigUpdateForm) (*super_dto.SuperSystemConfigItem, error) {
|
||||
if operatorID == 0 {
|
||||
return nil, errorx.ErrUnauthorized.WithMsg("缺少操作者信息")
|
||||
}
|
||||
if id == 0 {
|
||||
return nil, errorx.ErrBadRequest.WithMsg("配置ID不能为空")
|
||||
}
|
||||
if form == nil {
|
||||
return nil, errorx.ErrBadRequest.WithMsg("配置参数不能为空")
|
||||
}
|
||||
|
||||
tbl, q := models.SystemConfigQuery.QueryContext(ctx)
|
||||
cfg, err := q.Where(tbl.ID.Eq(id)).First()
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, errorx.ErrRecordNotFound.WithMsg("配置不存在")
|
||||
}
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
|
||||
updates := make(map[string]interface{}, 3)
|
||||
if form.Value != nil {
|
||||
if len(*form.Value) == 0 || !json.Valid(*form.Value) {
|
||||
return nil, errorx.ErrBadRequest.WithMsg("配置值必须是合法JSON")
|
||||
}
|
||||
updates["value"] = types.JSON(*form.Value)
|
||||
}
|
||||
if form.Description != nil {
|
||||
desc := strings.TrimSpace(*form.Description)
|
||||
if desc == "" {
|
||||
return nil, errorx.ErrBadRequest.WithMsg("配置说明不能为空")
|
||||
}
|
||||
updates["description"] = desc
|
||||
}
|
||||
if len(updates) == 0 {
|
||||
return nil, errorx.ErrBadRequest.WithMsg("请至少更新一项配置")
|
||||
}
|
||||
updates["updated_at"] = time.Now()
|
||||
|
||||
if _, err := q.Where(tbl.ID.Eq(id)).Updates(updates); err != nil {
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
|
||||
cfg, err = q.Where(tbl.ID.Eq(id)).First()
|
||||
if err != nil {
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
|
||||
if Audit != nil {
|
||||
details := make([]string, 0, 2)
|
||||
if form.Value != nil {
|
||||
details = append(details, "更新配置值")
|
||||
}
|
||||
if form.Description != nil {
|
||||
details = append(details, "更新配置说明")
|
||||
}
|
||||
detail := strings.Join(details, ",")
|
||||
Audit.Log(ctx, 0, operatorID, "update_system_config", cfg.ConfigKey, detail)
|
||||
}
|
||||
|
||||
return &super_dto.SuperSystemConfigItem{
|
||||
ID: cfg.ID,
|
||||
ConfigKey: cfg.ConfigKey,
|
||||
Value: json.RawMessage(cfg.Value),
|
||||
Description: cfg.Description,
|
||||
CreatedAt: s.formatTime(cfg.CreatedAt),
|
||||
UpdatedAt: s.formatTime(cfg.UpdatedAt),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *super) ListOrders(ctx context.Context, filter *super_dto.SuperOrderListFilter) (*requests.Pager, error) {
|
||||
tbl, q := models.OrderQuery.QueryContext(ctx)
|
||||
|
||||
@@ -5523,7 +5948,7 @@ func (s *super) UpdateCouponStatus(ctx context.Context, operatorID, couponID int
|
||||
}
|
||||
|
||||
if Audit != nil {
|
||||
Audit.Log(ctx, operatorID, "freeze_coupon", cast.ToString(coupon.ID), "Freeze coupon")
|
||||
Audit.Log(ctx, coupon.TenantID, operatorID, "freeze_coupon", cast.ToString(coupon.ID), "Freeze coupon")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -6077,7 +6502,7 @@ func (s *super) ApproveWithdrawal(ctx context.Context, operatorID, id int64) err
|
||||
UpdatedAt: time.Now(),
|
||||
})
|
||||
if err == nil && Audit != nil {
|
||||
Audit.Log(ctx, operatorID, "approve_withdrawal", cast.ToString(id), "Approved withdrawal")
|
||||
Audit.Log(ctx, o.TenantID, operatorID, "approve_withdrawal", cast.ToString(id), "Approved withdrawal")
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -6404,11 +6829,13 @@ func (s *super) RejectWithdrawal(ctx context.Context, operatorID, id int64, reas
|
||||
if operatorID == 0 {
|
||||
return errorx.ErrUnauthorized.WithMsg("缺少操作者信息")
|
||||
}
|
||||
tenantID := int64(0)
|
||||
err := models.Q.Transaction(func(tx *models.Query) error {
|
||||
o, err := tx.Order.WithContext(ctx).Where(tx.Order.ID.Eq(id)).First()
|
||||
if err != nil {
|
||||
return errorx.ErrRecordNotFound
|
||||
}
|
||||
tenantID = o.TenantID
|
||||
if o.Status != consts.OrderStatusCreated {
|
||||
return errorx.ErrStatusConflict.WithMsg("订单状态不正确")
|
||||
}
|
||||
@@ -6448,7 +6875,7 @@ func (s *super) RejectWithdrawal(ctx context.Context, operatorID, id int64, reas
|
||||
})
|
||||
|
||||
if err == nil && Audit != nil {
|
||||
Audit.Log(ctx, operatorID, "reject_withdrawal", cast.ToString(id), "Rejected: "+reason)
|
||||
Audit.Log(ctx, tenantID, operatorID, "reject_withdrawal", cast.ToString(id), "Rejected: "+reason)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
CREATE TABLE IF NOT EXISTS audit_logs (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0,
|
||||
operator_id BIGINT NOT NULL,
|
||||
action VARCHAR(64) NOT NULL,
|
||||
target_id VARCHAR(64) NOT NULL DEFAULT '',
|
||||
detail TEXT NOT NULL DEFAULT '',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
COMMENT ON TABLE audit_logs IS '审计日志:记录超管关键操作,用于追溯与合规审查。';
|
||||
COMMENT ON COLUMN audit_logs.id IS '主键ID。';
|
||||
COMMENT ON COLUMN audit_logs.tenant_id IS '租户ID;用途:关联租户审计(0表示平台级)。';
|
||||
COMMENT ON COLUMN audit_logs.operator_id IS '操作者用户ID;用途:审计追溯;约束:必须存在。';
|
||||
COMMENT ON COLUMN audit_logs.action IS '动作标识;用途:检索分类;约束:例如 review_content/freeze_coupon。';
|
||||
COMMENT ON COLUMN audit_logs.target_id IS '目标ID;用途:定位被操作对象;可为空字符串。';
|
||||
COMMENT ON COLUMN audit_logs.detail IS '动作详情;用途:记录操作原因与补充说明。';
|
||||
COMMENT ON COLUMN audit_logs.created_at IS '创建时间;用途:时间序列查询与审计留存。';
|
||||
|
||||
CREATE INDEX IF NOT EXISTS audit_logs_tenant_id_idx ON audit_logs(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS audit_logs_operator_id_idx ON audit_logs(operator_id);
|
||||
CREATE INDEX IF NOT EXISTS audit_logs_action_idx ON audit_logs(action);
|
||||
CREATE INDEX IF NOT EXISTS audit_logs_created_at_idx ON audit_logs(created_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS system_configs (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
config_key VARCHAR(64) NOT NULL,
|
||||
value JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
description VARCHAR(255) NOT NULL DEFAULT '',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
COMMENT ON TABLE system_configs IS '系统配置:平台级可配置项(JSON);用于功能开关与运营参数。';
|
||||
COMMENT ON COLUMN system_configs.id IS '主键ID。';
|
||||
COMMENT ON COLUMN system_configs.config_key IS '配置项Key;用途:按Key读取/更新;约束:唯一。';
|
||||
COMMENT ON COLUMN system_configs.value IS '配置值(JSON);用途:存储任意结构化配置;默认 {}。';
|
||||
COMMENT ON COLUMN system_configs.description IS '配置说明;用途:给运营/技术理解用途。';
|
||||
COMMENT ON COLUMN system_configs.created_at IS '创建时间;用途:审计与追溯。';
|
||||
COMMENT ON COLUMN system_configs.updated_at IS '更新时间;用途:变更记录。';
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS system_configs_config_key_uindex ON system_configs(config_key);
|
||||
CREATE INDEX IF NOT EXISTS system_configs_updated_at_idx ON system_configs(updated_at);
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
DROP TABLE IF EXISTS system_configs;
|
||||
DROP TABLE IF EXISTS audit_logs;
|
||||
-- +goose StatementEnd
|
||||
57
backend/database/models/audit_logs.gen.go
Normal file
57
backend/database/models/audit_logs.gen.go
Normal file
@@ -0,0 +1,57 @@
|
||||
// Code generated by go.ipao.vip/gen. DO NOT EDIT.
|
||||
// Code generated by go.ipao.vip/gen. DO NOT EDIT.
|
||||
// Code generated by go.ipao.vip/gen. DO NOT EDIT.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"go.ipao.vip/gen"
|
||||
)
|
||||
|
||||
const TableNameAuditLog = "audit_logs"
|
||||
|
||||
// AuditLog mapped from table <audit_logs>
|
||||
type AuditLog struct {
|
||||
ID int64 `gorm:"column:id;type:bigint;primaryKey;autoIncrement:true;comment:主键ID。" json:"id"` // 主键ID。
|
||||
TenantID int64 `gorm:"column:tenant_id;type:bigint;not null;comment:租户ID;用途:关联租户审计(0表示平台级)。" json:"tenant_id"` // 租户ID;用途:关联租户审计(0表示平台级)。
|
||||
OperatorID int64 `gorm:"column:operator_id;type:bigint;not null;comment:操作者用户ID;用途:审计追溯;约束:必须存在。" json:"operator_id"` // 操作者用户ID;用途:审计追溯;约束:必须存在。
|
||||
Action string `gorm:"column:action;type:character varying(64);not null;comment:动作标识;用途:检索分类;约束:例如 review_content/freeze_coupon。" json:"action"` // 动作标识;用途:检索分类;约束:例如 review_content/freeze_coupon。
|
||||
TargetID string `gorm:"column:target_id;type:character varying(64);not null;comment:目标ID;用途:定位被操作对象;可为空字符串。" json:"target_id"` // 目标ID;用途:定位被操作对象;可为空字符串。
|
||||
Detail string `gorm:"column:detail;type:text;not null;comment:动作详情;用途:记录操作原因与补充说明。" json:"detail"` // 动作详情;用途:记录操作原因与补充说明。
|
||||
CreatedAt time.Time `gorm:"column:created_at;type:timestamp with time zone;not null;default:now();comment:创建时间;用途:时间序列查询与审计留存。" json:"created_at"` // 创建时间;用途:时间序列查询与审计留存。
|
||||
}
|
||||
|
||||
// Quick operations without importing query package
|
||||
// Update applies changed fields to the database using the default DB.
|
||||
func (m *AuditLog) Update(ctx context.Context) (gen.ResultInfo, error) {
|
||||
return Q.AuditLog.WithContext(ctx).Updates(m)
|
||||
}
|
||||
|
||||
// Save upserts the model using the default DB.
|
||||
func (m *AuditLog) Save(ctx context.Context) error { return Q.AuditLog.WithContext(ctx).Save(m) }
|
||||
|
||||
// Create inserts the model using the default DB.
|
||||
func (m *AuditLog) Create(ctx context.Context) error { return Q.AuditLog.WithContext(ctx).Create(m) }
|
||||
|
||||
// Delete removes the row represented by the model using the default DB.
|
||||
func (m *AuditLog) Delete(ctx context.Context) (gen.ResultInfo, error) {
|
||||
return Q.AuditLog.WithContext(ctx).Delete(m)
|
||||
}
|
||||
|
||||
// ForceDelete permanently deletes the row (ignores soft delete) using the default DB.
|
||||
func (m *AuditLog) ForceDelete(ctx context.Context) (gen.ResultInfo, error) {
|
||||
return Q.AuditLog.WithContext(ctx).Unscoped().Delete(m)
|
||||
}
|
||||
|
||||
// Reload reloads the model from database by its primary key and overwrites current fields.
|
||||
func (m *AuditLog) Reload(ctx context.Context) error {
|
||||
fresh, err := Q.AuditLog.WithContext(ctx).GetByID(m.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*m = *fresh
|
||||
return nil
|
||||
}
|
||||
485
backend/database/models/audit_logs.query.gen.go
Normal file
485
backend/database/models/audit_logs.query.gen.go
Normal file
@@ -0,0 +1,485 @@
|
||||
// Code generated by go.ipao.vip/gen. DO NOT EDIT.
|
||||
// Code generated by go.ipao.vip/gen. DO NOT EDIT.
|
||||
// Code generated by go.ipao.vip/gen. DO NOT EDIT.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
"gorm.io/gorm/schema"
|
||||
|
||||
"go.ipao.vip/gen"
|
||||
"go.ipao.vip/gen/field"
|
||||
|
||||
"gorm.io/plugin/dbresolver"
|
||||
)
|
||||
|
||||
func newAuditLog(db *gorm.DB, opts ...gen.DOOption) auditLogQuery {
|
||||
_auditLogQuery := auditLogQuery{}
|
||||
|
||||
_auditLogQuery.auditLogQueryDo.UseDB(db, opts...)
|
||||
_auditLogQuery.auditLogQueryDo.UseModel(&AuditLog{})
|
||||
|
||||
tableName := _auditLogQuery.auditLogQueryDo.TableName()
|
||||
_auditLogQuery.ALL = field.NewAsterisk(tableName)
|
||||
_auditLogQuery.ID = field.NewInt64(tableName, "id")
|
||||
_auditLogQuery.TenantID = field.NewInt64(tableName, "tenant_id")
|
||||
_auditLogQuery.OperatorID = field.NewInt64(tableName, "operator_id")
|
||||
_auditLogQuery.Action = field.NewString(tableName, "action")
|
||||
_auditLogQuery.TargetID = field.NewString(tableName, "target_id")
|
||||
_auditLogQuery.Detail = field.NewString(tableName, "detail")
|
||||
_auditLogQuery.CreatedAt = field.NewTime(tableName, "created_at")
|
||||
|
||||
_auditLogQuery.fillFieldMap()
|
||||
|
||||
return _auditLogQuery
|
||||
}
|
||||
|
||||
type auditLogQuery struct {
|
||||
auditLogQueryDo auditLogQueryDo
|
||||
|
||||
ALL field.Asterisk
|
||||
ID field.Int64 // 主键ID。
|
||||
TenantID field.Int64 // 租户ID;用途:关联租户审计(0表示平台级)。
|
||||
OperatorID field.Int64 // 操作者用户ID;用途:审计追溯;约束:必须存在。
|
||||
Action field.String // 动作标识;用途:检索分类;约束:例如 review_content/freeze_coupon。
|
||||
TargetID field.String // 目标ID;用途:定位被操作对象;可为空字符串。
|
||||
Detail field.String // 动作详情;用途:记录操作原因与补充说明。
|
||||
CreatedAt field.Time // 创建时间;用途:时间序列查询与审计留存。
|
||||
|
||||
fieldMap map[string]field.Expr
|
||||
}
|
||||
|
||||
func (a auditLogQuery) Table(newTableName string) *auditLogQuery {
|
||||
a.auditLogQueryDo.UseTable(newTableName)
|
||||
return a.updateTableName(newTableName)
|
||||
}
|
||||
|
||||
func (a auditLogQuery) As(alias string) *auditLogQuery {
|
||||
a.auditLogQueryDo.DO = *(a.auditLogQueryDo.As(alias).(*gen.DO))
|
||||
return a.updateTableName(alias)
|
||||
}
|
||||
|
||||
func (a *auditLogQuery) updateTableName(table string) *auditLogQuery {
|
||||
a.ALL = field.NewAsterisk(table)
|
||||
a.ID = field.NewInt64(table, "id")
|
||||
a.TenantID = field.NewInt64(table, "tenant_id")
|
||||
a.OperatorID = field.NewInt64(table, "operator_id")
|
||||
a.Action = field.NewString(table, "action")
|
||||
a.TargetID = field.NewString(table, "target_id")
|
||||
a.Detail = field.NewString(table, "detail")
|
||||
a.CreatedAt = field.NewTime(table, "created_at")
|
||||
|
||||
a.fillFieldMap()
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *auditLogQuery) QueryContext(ctx context.Context) (*auditLogQuery, *auditLogQueryDo) {
|
||||
return a, a.auditLogQueryDo.WithContext(ctx)
|
||||
}
|
||||
|
||||
func (a *auditLogQuery) WithContext(ctx context.Context) *auditLogQueryDo {
|
||||
return a.auditLogQueryDo.WithContext(ctx)
|
||||
}
|
||||
|
||||
func (a auditLogQuery) TableName() string { return a.auditLogQueryDo.TableName() }
|
||||
|
||||
func (a auditLogQuery) Alias() string { return a.auditLogQueryDo.Alias() }
|
||||
|
||||
func (a auditLogQuery) Columns(cols ...field.Expr) gen.Columns {
|
||||
return a.auditLogQueryDo.Columns(cols...)
|
||||
}
|
||||
|
||||
func (a *auditLogQuery) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
||||
_f, ok := a.fieldMap[fieldName]
|
||||
if !ok || _f == nil {
|
||||
return nil, false
|
||||
}
|
||||
_oe, ok := _f.(field.OrderExpr)
|
||||
return _oe, ok
|
||||
}
|
||||
|
||||
func (a *auditLogQuery) fillFieldMap() {
|
||||
a.fieldMap = make(map[string]field.Expr, 7)
|
||||
a.fieldMap["id"] = a.ID
|
||||
a.fieldMap["tenant_id"] = a.TenantID
|
||||
a.fieldMap["operator_id"] = a.OperatorID
|
||||
a.fieldMap["action"] = a.Action
|
||||
a.fieldMap["target_id"] = a.TargetID
|
||||
a.fieldMap["detail"] = a.Detail
|
||||
a.fieldMap["created_at"] = a.CreatedAt
|
||||
}
|
||||
|
||||
func (a auditLogQuery) clone(db *gorm.DB) auditLogQuery {
|
||||
a.auditLogQueryDo.ReplaceConnPool(db.Statement.ConnPool)
|
||||
return a
|
||||
}
|
||||
|
||||
func (a auditLogQuery) replaceDB(db *gorm.DB) auditLogQuery {
|
||||
a.auditLogQueryDo.ReplaceDB(db)
|
||||
return a
|
||||
}
|
||||
|
||||
type auditLogQueryDo struct{ gen.DO }
|
||||
|
||||
func (a auditLogQueryDo) Debug() *auditLogQueryDo {
|
||||
return a.withDO(a.DO.Debug())
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) WithContext(ctx context.Context) *auditLogQueryDo {
|
||||
return a.withDO(a.DO.WithContext(ctx))
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) ReadDB() *auditLogQueryDo {
|
||||
return a.Clauses(dbresolver.Read)
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) WriteDB() *auditLogQueryDo {
|
||||
return a.Clauses(dbresolver.Write)
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) Session(config *gorm.Session) *auditLogQueryDo {
|
||||
return a.withDO(a.DO.Session(config))
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) Clauses(conds ...clause.Expression) *auditLogQueryDo {
|
||||
return a.withDO(a.DO.Clauses(conds...))
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) Returning(value interface{}, columns ...string) *auditLogQueryDo {
|
||||
return a.withDO(a.DO.Returning(value, columns...))
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) Not(conds ...gen.Condition) *auditLogQueryDo {
|
||||
return a.withDO(a.DO.Not(conds...))
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) Or(conds ...gen.Condition) *auditLogQueryDo {
|
||||
return a.withDO(a.DO.Or(conds...))
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) Select(conds ...field.Expr) *auditLogQueryDo {
|
||||
return a.withDO(a.DO.Select(conds...))
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) Where(conds ...gen.Condition) *auditLogQueryDo {
|
||||
return a.withDO(a.DO.Where(conds...))
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) Order(conds ...field.Expr) *auditLogQueryDo {
|
||||
return a.withDO(a.DO.Order(conds...))
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) Distinct(cols ...field.Expr) *auditLogQueryDo {
|
||||
return a.withDO(a.DO.Distinct(cols...))
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) Omit(cols ...field.Expr) *auditLogQueryDo {
|
||||
return a.withDO(a.DO.Omit(cols...))
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) Join(table schema.Tabler, on ...field.Expr) *auditLogQueryDo {
|
||||
return a.withDO(a.DO.Join(table, on...))
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) LeftJoin(table schema.Tabler, on ...field.Expr) *auditLogQueryDo {
|
||||
return a.withDO(a.DO.LeftJoin(table, on...))
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) RightJoin(table schema.Tabler, on ...field.Expr) *auditLogQueryDo {
|
||||
return a.withDO(a.DO.RightJoin(table, on...))
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) Group(cols ...field.Expr) *auditLogQueryDo {
|
||||
return a.withDO(a.DO.Group(cols...))
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) Having(conds ...gen.Condition) *auditLogQueryDo {
|
||||
return a.withDO(a.DO.Having(conds...))
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) Limit(limit int) *auditLogQueryDo {
|
||||
return a.withDO(a.DO.Limit(limit))
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) Offset(offset int) *auditLogQueryDo {
|
||||
return a.withDO(a.DO.Offset(offset))
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *auditLogQueryDo {
|
||||
return a.withDO(a.DO.Scopes(funcs...))
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) Unscoped() *auditLogQueryDo {
|
||||
return a.withDO(a.DO.Unscoped())
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) Create(values ...*AuditLog) error {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
return a.DO.Create(values)
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) CreateInBatches(values []*AuditLog, batchSize int) error {
|
||||
return a.DO.CreateInBatches(values, batchSize)
|
||||
}
|
||||
|
||||
// Save : !!! underlying implementation is different with GORM
|
||||
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
|
||||
func (a auditLogQueryDo) Save(values ...*AuditLog) error {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
return a.DO.Save(values)
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) First() (*AuditLog, error) {
|
||||
if result, err := a.DO.First(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*AuditLog), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) Take() (*AuditLog, error) {
|
||||
if result, err := a.DO.Take(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*AuditLog), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) Last() (*AuditLog, error) {
|
||||
if result, err := a.DO.Last(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*AuditLog), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) Find() ([]*AuditLog, error) {
|
||||
result, err := a.DO.Find()
|
||||
return result.([]*AuditLog), err
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*AuditLog, err error) {
|
||||
buf := make([]*AuditLog, 0, batchSize)
|
||||
err = a.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
|
||||
defer func() { results = append(results, buf...) }()
|
||||
return fc(tx, batch)
|
||||
})
|
||||
return results, err
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) FindInBatches(result *[]*AuditLog, batchSize int, fc func(tx gen.Dao, batch int) error) error {
|
||||
return a.DO.FindInBatches(result, batchSize, fc)
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) Attrs(attrs ...field.AssignExpr) *auditLogQueryDo {
|
||||
return a.withDO(a.DO.Attrs(attrs...))
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) Assign(attrs ...field.AssignExpr) *auditLogQueryDo {
|
||||
return a.withDO(a.DO.Assign(attrs...))
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) Joins(fields ...field.RelationField) *auditLogQueryDo {
|
||||
for _, _f := range fields {
|
||||
a = *a.withDO(a.DO.Joins(_f))
|
||||
}
|
||||
return &a
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) Preload(fields ...field.RelationField) *auditLogQueryDo {
|
||||
for _, _f := range fields {
|
||||
a = *a.withDO(a.DO.Preload(_f))
|
||||
}
|
||||
return &a
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) FirstOrInit() (*AuditLog, error) {
|
||||
if result, err := a.DO.FirstOrInit(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*AuditLog), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) FirstOrCreate() (*AuditLog, error) {
|
||||
if result, err := a.DO.FirstOrCreate(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*AuditLog), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) FindByPage(offset int, limit int) (result []*AuditLog, count int64, err error) {
|
||||
result, err = a.Offset(offset).Limit(limit).Find()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if size := len(result); 0 < limit && 0 < size && size < limit {
|
||||
count = int64(size + offset)
|
||||
return
|
||||
}
|
||||
|
||||
count, err = a.Offset(-1).Limit(-1).Count()
|
||||
return
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
|
||||
count, err = a.Count()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = a.Offset(offset).Limit(limit).Scan(result)
|
||||
return
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) Scan(result interface{}) (err error) {
|
||||
return a.DO.Scan(result)
|
||||
}
|
||||
|
||||
func (a auditLogQueryDo) Delete(models ...*AuditLog) (result gen.ResultInfo, err error) {
|
||||
return a.DO.Delete(models)
|
||||
}
|
||||
|
||||
// ForceDelete performs a permanent delete (ignores soft-delete) for current scope.
|
||||
func (a auditLogQueryDo) ForceDelete() (gen.ResultInfo, error) {
|
||||
return a.Unscoped().Delete()
|
||||
}
|
||||
|
||||
// Inc increases the given column by step for current scope.
|
||||
func (a auditLogQueryDo) Inc(column field.Expr, step int64) (gen.ResultInfo, error) {
|
||||
// column = column + step
|
||||
e := field.NewUnsafeFieldRaw("?+?", column.RawExpr(), step)
|
||||
return a.DO.UpdateColumn(column, e)
|
||||
}
|
||||
|
||||
// Dec decreases the given column by step for current scope.
|
||||
func (a auditLogQueryDo) Dec(column field.Expr, step int64) (gen.ResultInfo, error) {
|
||||
// column = column - step
|
||||
e := field.NewUnsafeFieldRaw("?-?", column.RawExpr(), step)
|
||||
return a.DO.UpdateColumn(column, e)
|
||||
}
|
||||
|
||||
// Sum returns SUM(column) for current scope.
|
||||
func (a auditLogQueryDo) Sum(column field.Expr) (float64, error) {
|
||||
var _v float64
|
||||
agg := field.NewUnsafeFieldRaw("SUM(?)", column.RawExpr())
|
||||
if err := a.Select(agg).Scan(&_v); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return _v, nil
|
||||
}
|
||||
|
||||
// Avg returns AVG(column) for current scope.
|
||||
func (a auditLogQueryDo) Avg(column field.Expr) (float64, error) {
|
||||
var _v float64
|
||||
agg := field.NewUnsafeFieldRaw("AVG(?)", column.RawExpr())
|
||||
if err := a.Select(agg).Scan(&_v); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return _v, nil
|
||||
}
|
||||
|
||||
// Min returns MIN(column) for current scope.
|
||||
func (a auditLogQueryDo) Min(column field.Expr) (float64, error) {
|
||||
var _v float64
|
||||
agg := field.NewUnsafeFieldRaw("MIN(?)", column.RawExpr())
|
||||
if err := a.Select(agg).Scan(&_v); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return _v, nil
|
||||
}
|
||||
|
||||
// Max returns MAX(column) for current scope.
|
||||
func (a auditLogQueryDo) Max(column field.Expr) (float64, error) {
|
||||
var _v float64
|
||||
agg := field.NewUnsafeFieldRaw("MAX(?)", column.RawExpr())
|
||||
if err := a.Select(agg).Scan(&_v); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return _v, nil
|
||||
}
|
||||
|
||||
// PluckMap returns a map[key]value for selected key/value expressions within current scope.
|
||||
func (a auditLogQueryDo) PluckMap(key, val field.Expr) (map[interface{}]interface{}, error) {
|
||||
do := a.Select(key, val)
|
||||
rows, err := do.DO.Rows()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
mm := make(map[interface{}]interface{})
|
||||
for rows.Next() {
|
||||
var k interface{}
|
||||
var v interface{}
|
||||
if err := rows.Scan(&k, &v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mm[k] = v
|
||||
}
|
||||
return mm, rows.Err()
|
||||
}
|
||||
|
||||
// Exists returns true if any record matches the given conditions.
|
||||
func (a auditLogQueryDo) Exists(conds ...gen.Condition) (bool, error) {
|
||||
cnt, err := a.Where(conds...).Count()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return cnt > 0, nil
|
||||
}
|
||||
|
||||
// PluckIDs returns all primary key values under current scope.
|
||||
func (a auditLogQueryDo) PluckIDs() ([]int64, error) {
|
||||
ids := make([]int64, 0, 16)
|
||||
pk := field.NewInt64(a.TableName(), "id")
|
||||
if err := a.DO.Pluck(pk, &ids); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
// GetByID finds a single record by primary key.
|
||||
func (a auditLogQueryDo) GetByID(id int64) (*AuditLog, error) {
|
||||
pk := field.NewInt64(a.TableName(), "id")
|
||||
return a.Where(pk.Eq(id)).First()
|
||||
}
|
||||
|
||||
// GetByIDs finds records by primary key list.
|
||||
func (a auditLogQueryDo) GetByIDs(ids ...int64) ([]*AuditLog, error) {
|
||||
if len(ids) == 0 {
|
||||
return []*AuditLog{}, nil
|
||||
}
|
||||
pk := field.NewInt64(a.TableName(), "id")
|
||||
return a.Where(pk.In(ids...)).Find()
|
||||
}
|
||||
|
||||
// DeleteByID deletes records by primary key.
|
||||
func (a auditLogQueryDo) DeleteByID(id int64) (gen.ResultInfo, error) {
|
||||
pk := field.NewInt64(a.TableName(), "id")
|
||||
return a.Where(pk.Eq(id)).Delete()
|
||||
}
|
||||
|
||||
// DeleteByIDs deletes records by a list of primary keys.
|
||||
func (a auditLogQueryDo) DeleteByIDs(ids ...int64) (gen.ResultInfo, error) {
|
||||
if len(ids) == 0 {
|
||||
return gen.ResultInfo{RowsAffected: 0, Error: nil}, nil
|
||||
}
|
||||
pk := field.NewInt64(a.TableName(), "id")
|
||||
return a.Where(pk.In(ids...)).Delete()
|
||||
}
|
||||
|
||||
func (a *auditLogQueryDo) withDO(do gen.Dao) *auditLogQueryDo {
|
||||
a.DO = *do.(*gen.DO)
|
||||
return a
|
||||
}
|
||||
@@ -40,9 +40,9 @@ type Content struct {
|
||||
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp with time zone" json:"deleted_at"`
|
||||
Key string `gorm:"column:key;type:character varying(32);comment:Musical key/tone" json:"key"` // Musical key/tone
|
||||
IsPinned bool `gorm:"column:is_pinned;type:boolean;comment:Whether content is pinned/featured" json:"is_pinned"` // Whether content is pinned/featured
|
||||
Author *User `gorm:"foreignKey:UserID;references:ID" json:"author,omitempty"`
|
||||
ContentAssets []*ContentAsset `gorm:"foreignKey:ContentID;references:ID" json:"content_assets,omitempty"`
|
||||
Comments []*Comment `gorm:"foreignKey:ContentID;references:ID" json:"comments,omitempty"`
|
||||
Author *User `gorm:"foreignKey:UserID;references:ID" json:"author,omitempty"`
|
||||
}
|
||||
|
||||
// Quick operations without importing query package
|
||||
|
||||
@@ -46,12 +46,6 @@ func newContent(db *gorm.DB, opts ...gen.DOOption) contentQuery {
|
||||
_contentQuery.DeletedAt = field.NewField(tableName, "deleted_at")
|
||||
_contentQuery.Key = field.NewString(tableName, "key")
|
||||
_contentQuery.IsPinned = field.NewBool(tableName, "is_pinned")
|
||||
_contentQuery.Author = contentQueryBelongsToAuthor{
|
||||
db: db.Session(&gorm.Session{}),
|
||||
|
||||
RelationField: field.NewRelation("Author", "User"),
|
||||
}
|
||||
|
||||
_contentQuery.ContentAssets = contentQueryHasManyContentAssets{
|
||||
db: db.Session(&gorm.Session{}),
|
||||
|
||||
@@ -64,6 +58,12 @@ func newContent(db *gorm.DB, opts ...gen.DOOption) contentQuery {
|
||||
RelationField: field.NewRelation("Comments", "Comment"),
|
||||
}
|
||||
|
||||
_contentQuery.Author = contentQueryBelongsToAuthor{
|
||||
db: db.Session(&gorm.Session{}),
|
||||
|
||||
RelationField: field.NewRelation("Author", "User"),
|
||||
}
|
||||
|
||||
_contentQuery.fillFieldMap()
|
||||
|
||||
return _contentQuery
|
||||
@@ -94,12 +94,12 @@ type contentQuery struct {
|
||||
DeletedAt field.Field
|
||||
Key field.String // Musical key/tone
|
||||
IsPinned field.Bool // Whether content is pinned/featured
|
||||
Author contentQueryBelongsToAuthor
|
||||
|
||||
ContentAssets contentQueryHasManyContentAssets
|
||||
ContentAssets contentQueryHasManyContentAssets
|
||||
|
||||
Comments contentQueryHasManyComments
|
||||
|
||||
Author contentQueryBelongsToAuthor
|
||||
|
||||
fieldMap map[string]field.Expr
|
||||
}
|
||||
|
||||
@@ -195,104 +195,23 @@ func (c *contentQuery) fillFieldMap() {
|
||||
|
||||
func (c contentQuery) clone(db *gorm.DB) contentQuery {
|
||||
c.contentQueryDo.ReplaceConnPool(db.Statement.ConnPool)
|
||||
c.Author.db = db.Session(&gorm.Session{Initialized: true})
|
||||
c.Author.db.Statement.ConnPool = db.Statement.ConnPool
|
||||
c.ContentAssets.db = db.Session(&gorm.Session{Initialized: true})
|
||||
c.ContentAssets.db.Statement.ConnPool = db.Statement.ConnPool
|
||||
c.Comments.db = db.Session(&gorm.Session{Initialized: true})
|
||||
c.Comments.db.Statement.ConnPool = db.Statement.ConnPool
|
||||
c.Author.db = db.Session(&gorm.Session{Initialized: true})
|
||||
c.Author.db.Statement.ConnPool = db.Statement.ConnPool
|
||||
return c
|
||||
}
|
||||
|
||||
func (c contentQuery) replaceDB(db *gorm.DB) contentQuery {
|
||||
c.contentQueryDo.ReplaceDB(db)
|
||||
c.Author.db = db.Session(&gorm.Session{})
|
||||
c.ContentAssets.db = db.Session(&gorm.Session{})
|
||||
c.Comments.db = db.Session(&gorm.Session{})
|
||||
c.Author.db = db.Session(&gorm.Session{})
|
||||
return c
|
||||
}
|
||||
|
||||
type contentQueryBelongsToAuthor struct {
|
||||
db *gorm.DB
|
||||
|
||||
field.RelationField
|
||||
}
|
||||
|
||||
func (a contentQueryBelongsToAuthor) Where(conds ...field.Expr) *contentQueryBelongsToAuthor {
|
||||
if len(conds) == 0 {
|
||||
return &a
|
||||
}
|
||||
|
||||
exprs := make([]clause.Expression, 0, len(conds))
|
||||
for _, cond := range conds {
|
||||
exprs = append(exprs, cond.BeCond().(clause.Expression))
|
||||
}
|
||||
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
|
||||
return &a
|
||||
}
|
||||
|
||||
func (a contentQueryBelongsToAuthor) WithContext(ctx context.Context) *contentQueryBelongsToAuthor {
|
||||
a.db = a.db.WithContext(ctx)
|
||||
return &a
|
||||
}
|
||||
|
||||
func (a contentQueryBelongsToAuthor) Session(session *gorm.Session) *contentQueryBelongsToAuthor {
|
||||
a.db = a.db.Session(session)
|
||||
return &a
|
||||
}
|
||||
|
||||
func (a contentQueryBelongsToAuthor) Model(m *Content) *contentQueryBelongsToAuthorTx {
|
||||
return &contentQueryBelongsToAuthorTx{a.db.Model(m).Association(a.Name())}
|
||||
}
|
||||
|
||||
func (a contentQueryBelongsToAuthor) Unscoped() *contentQueryBelongsToAuthor {
|
||||
a.db = a.db.Unscoped()
|
||||
return &a
|
||||
}
|
||||
|
||||
type contentQueryBelongsToAuthorTx struct{ tx *gorm.Association }
|
||||
|
||||
func (a contentQueryBelongsToAuthorTx) Find() (result *User, err error) {
|
||||
return result, a.tx.Find(&result)
|
||||
}
|
||||
|
||||
func (a contentQueryBelongsToAuthorTx) Append(values ...*User) (err error) {
|
||||
targetValues := make([]interface{}, len(values))
|
||||
for i, v := range values {
|
||||
targetValues[i] = v
|
||||
}
|
||||
return a.tx.Append(targetValues...)
|
||||
}
|
||||
|
||||
func (a contentQueryBelongsToAuthorTx) Replace(values ...*User) (err error) {
|
||||
targetValues := make([]interface{}, len(values))
|
||||
for i, v := range values {
|
||||
targetValues[i] = v
|
||||
}
|
||||
return a.tx.Replace(targetValues...)
|
||||
}
|
||||
|
||||
func (a contentQueryBelongsToAuthorTx) Delete(values ...*User) (err error) {
|
||||
targetValues := make([]interface{}, len(values))
|
||||
for i, v := range values {
|
||||
targetValues[i] = v
|
||||
}
|
||||
return a.tx.Delete(targetValues...)
|
||||
}
|
||||
|
||||
func (a contentQueryBelongsToAuthorTx) Clear() error {
|
||||
return a.tx.Clear()
|
||||
}
|
||||
|
||||
func (a contentQueryBelongsToAuthorTx) Count() int64 {
|
||||
return a.tx.Count()
|
||||
}
|
||||
|
||||
func (a contentQueryBelongsToAuthorTx) Unscoped() *contentQueryBelongsToAuthorTx {
|
||||
a.tx = a.tx.Unscoped()
|
||||
return &a
|
||||
}
|
||||
|
||||
type contentQueryHasManyContentAssets struct {
|
||||
db *gorm.DB
|
||||
|
||||
@@ -455,6 +374,87 @@ func (a contentQueryHasManyCommentsTx) Unscoped() *contentQueryHasManyCommentsTx
|
||||
return &a
|
||||
}
|
||||
|
||||
type contentQueryBelongsToAuthor struct {
|
||||
db *gorm.DB
|
||||
|
||||
field.RelationField
|
||||
}
|
||||
|
||||
func (a contentQueryBelongsToAuthor) Where(conds ...field.Expr) *contentQueryBelongsToAuthor {
|
||||
if len(conds) == 0 {
|
||||
return &a
|
||||
}
|
||||
|
||||
exprs := make([]clause.Expression, 0, len(conds))
|
||||
for _, cond := range conds {
|
||||
exprs = append(exprs, cond.BeCond().(clause.Expression))
|
||||
}
|
||||
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
|
||||
return &a
|
||||
}
|
||||
|
||||
func (a contentQueryBelongsToAuthor) WithContext(ctx context.Context) *contentQueryBelongsToAuthor {
|
||||
a.db = a.db.WithContext(ctx)
|
||||
return &a
|
||||
}
|
||||
|
||||
func (a contentQueryBelongsToAuthor) Session(session *gorm.Session) *contentQueryBelongsToAuthor {
|
||||
a.db = a.db.Session(session)
|
||||
return &a
|
||||
}
|
||||
|
||||
func (a contentQueryBelongsToAuthor) Model(m *Content) *contentQueryBelongsToAuthorTx {
|
||||
return &contentQueryBelongsToAuthorTx{a.db.Model(m).Association(a.Name())}
|
||||
}
|
||||
|
||||
func (a contentQueryBelongsToAuthor) Unscoped() *contentQueryBelongsToAuthor {
|
||||
a.db = a.db.Unscoped()
|
||||
return &a
|
||||
}
|
||||
|
||||
type contentQueryBelongsToAuthorTx struct{ tx *gorm.Association }
|
||||
|
||||
func (a contentQueryBelongsToAuthorTx) Find() (result *User, err error) {
|
||||
return result, a.tx.Find(&result)
|
||||
}
|
||||
|
||||
func (a contentQueryBelongsToAuthorTx) Append(values ...*User) (err error) {
|
||||
targetValues := make([]interface{}, len(values))
|
||||
for i, v := range values {
|
||||
targetValues[i] = v
|
||||
}
|
||||
return a.tx.Append(targetValues...)
|
||||
}
|
||||
|
||||
func (a contentQueryBelongsToAuthorTx) Replace(values ...*User) (err error) {
|
||||
targetValues := make([]interface{}, len(values))
|
||||
for i, v := range values {
|
||||
targetValues[i] = v
|
||||
}
|
||||
return a.tx.Replace(targetValues...)
|
||||
}
|
||||
|
||||
func (a contentQueryBelongsToAuthorTx) Delete(values ...*User) (err error) {
|
||||
targetValues := make([]interface{}, len(values))
|
||||
for i, v := range values {
|
||||
targetValues[i] = v
|
||||
}
|
||||
return a.tx.Delete(targetValues...)
|
||||
}
|
||||
|
||||
func (a contentQueryBelongsToAuthorTx) Clear() error {
|
||||
return a.tx.Clear()
|
||||
}
|
||||
|
||||
func (a contentQueryBelongsToAuthorTx) Count() int64 {
|
||||
return a.tx.Count()
|
||||
}
|
||||
|
||||
func (a contentQueryBelongsToAuthorTx) Unscoped() *contentQueryBelongsToAuthorTx {
|
||||
a.tx = a.tx.Unscoped()
|
||||
return &a
|
||||
}
|
||||
|
||||
type contentQueryDo struct{ gen.DO }
|
||||
|
||||
func (c contentQueryDo) Debug() *contentQueryDo {
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
|
||||
var (
|
||||
Q = new(Query)
|
||||
AuditLogQuery *auditLogQuery
|
||||
CommentQuery *commentQuery
|
||||
ContentQuery *contentQuery
|
||||
ContentAccessQuery *contentAccessQuery
|
||||
@@ -29,6 +30,7 @@ var (
|
||||
OrderQuery *orderQuery
|
||||
OrderItemQuery *orderItemQuery
|
||||
PayoutAccountQuery *payoutAccountQuery
|
||||
SystemConfigQuery *systemConfigQuery
|
||||
TenantQuery *tenantQuery
|
||||
TenantInviteQuery *tenantInviteQuery
|
||||
TenantJoinRequestQuery *tenantJoinRequestQuery
|
||||
@@ -42,6 +44,7 @@ var (
|
||||
|
||||
func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
|
||||
*Q = *Use(db, opts...)
|
||||
AuditLogQuery = &Q.AuditLog
|
||||
CommentQuery = &Q.Comment
|
||||
ContentQuery = &Q.Content
|
||||
ContentAccessQuery = &Q.ContentAccess
|
||||
@@ -54,6 +57,7 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
|
||||
OrderQuery = &Q.Order
|
||||
OrderItemQuery = &Q.OrderItem
|
||||
PayoutAccountQuery = &Q.PayoutAccount
|
||||
SystemConfigQuery = &Q.SystemConfig
|
||||
TenantQuery = &Q.Tenant
|
||||
TenantInviteQuery = &Q.TenantInvite
|
||||
TenantJoinRequestQuery = &Q.TenantJoinRequest
|
||||
@@ -68,6 +72,7 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
|
||||
func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
|
||||
return &Query{
|
||||
db: db,
|
||||
AuditLog: newAuditLog(db, opts...),
|
||||
Comment: newComment(db, opts...),
|
||||
Content: newContent(db, opts...),
|
||||
ContentAccess: newContentAccess(db, opts...),
|
||||
@@ -80,6 +85,7 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
|
||||
Order: newOrder(db, opts...),
|
||||
OrderItem: newOrderItem(db, opts...),
|
||||
PayoutAccount: newPayoutAccount(db, opts...),
|
||||
SystemConfig: newSystemConfig(db, opts...),
|
||||
Tenant: newTenant(db, opts...),
|
||||
TenantInvite: newTenantInvite(db, opts...),
|
||||
TenantJoinRequest: newTenantJoinRequest(db, opts...),
|
||||
@@ -95,6 +101,7 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
|
||||
type Query struct {
|
||||
db *gorm.DB
|
||||
|
||||
AuditLog auditLogQuery
|
||||
Comment commentQuery
|
||||
Content contentQuery
|
||||
ContentAccess contentAccessQuery
|
||||
@@ -107,6 +114,7 @@ type Query struct {
|
||||
Order orderQuery
|
||||
OrderItem orderItemQuery
|
||||
PayoutAccount payoutAccountQuery
|
||||
SystemConfig systemConfigQuery
|
||||
Tenant tenantQuery
|
||||
TenantInvite tenantInviteQuery
|
||||
TenantJoinRequest tenantJoinRequestQuery
|
||||
@@ -123,6 +131,7 @@ func (q *Query) Available() bool { return q.db != nil }
|
||||
func (q *Query) clone(db *gorm.DB) *Query {
|
||||
return &Query{
|
||||
db: db,
|
||||
AuditLog: q.AuditLog.clone(db),
|
||||
Comment: q.Comment.clone(db),
|
||||
Content: q.Content.clone(db),
|
||||
ContentAccess: q.ContentAccess.clone(db),
|
||||
@@ -135,6 +144,7 @@ func (q *Query) clone(db *gorm.DB) *Query {
|
||||
Order: q.Order.clone(db),
|
||||
OrderItem: q.OrderItem.clone(db),
|
||||
PayoutAccount: q.PayoutAccount.clone(db),
|
||||
SystemConfig: q.SystemConfig.clone(db),
|
||||
Tenant: q.Tenant.clone(db),
|
||||
TenantInvite: q.TenantInvite.clone(db),
|
||||
TenantJoinRequest: q.TenantJoinRequest.clone(db),
|
||||
@@ -158,6 +168,7 @@ func (q *Query) WriteDB() *Query {
|
||||
func (q *Query) ReplaceDB(db *gorm.DB) *Query {
|
||||
return &Query{
|
||||
db: db,
|
||||
AuditLog: q.AuditLog.replaceDB(db),
|
||||
Comment: q.Comment.replaceDB(db),
|
||||
Content: q.Content.replaceDB(db),
|
||||
ContentAccess: q.ContentAccess.replaceDB(db),
|
||||
@@ -170,6 +181,7 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query {
|
||||
Order: q.Order.replaceDB(db),
|
||||
OrderItem: q.OrderItem.replaceDB(db),
|
||||
PayoutAccount: q.PayoutAccount.replaceDB(db),
|
||||
SystemConfig: q.SystemConfig.replaceDB(db),
|
||||
Tenant: q.Tenant.replaceDB(db),
|
||||
TenantInvite: q.TenantInvite.replaceDB(db),
|
||||
TenantJoinRequest: q.TenantJoinRequest.replaceDB(db),
|
||||
@@ -183,6 +195,7 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query {
|
||||
}
|
||||
|
||||
type queryCtx struct {
|
||||
AuditLog *auditLogQueryDo
|
||||
Comment *commentQueryDo
|
||||
Content *contentQueryDo
|
||||
ContentAccess *contentAccessQueryDo
|
||||
@@ -195,6 +208,7 @@ type queryCtx struct {
|
||||
Order *orderQueryDo
|
||||
OrderItem *orderItemQueryDo
|
||||
PayoutAccount *payoutAccountQueryDo
|
||||
SystemConfig *systemConfigQueryDo
|
||||
Tenant *tenantQueryDo
|
||||
TenantInvite *tenantInviteQueryDo
|
||||
TenantJoinRequest *tenantJoinRequestQueryDo
|
||||
@@ -208,6 +222,7 @@ type queryCtx struct {
|
||||
|
||||
func (q *Query) WithContext(ctx context.Context) *queryCtx {
|
||||
return &queryCtx{
|
||||
AuditLog: q.AuditLog.WithContext(ctx),
|
||||
Comment: q.Comment.WithContext(ctx),
|
||||
Content: q.Content.WithContext(ctx),
|
||||
ContentAccess: q.ContentAccess.WithContext(ctx),
|
||||
@@ -220,6 +235,7 @@ func (q *Query) WithContext(ctx context.Context) *queryCtx {
|
||||
Order: q.Order.WithContext(ctx),
|
||||
OrderItem: q.OrderItem.WithContext(ctx),
|
||||
PayoutAccount: q.PayoutAccount.WithContext(ctx),
|
||||
SystemConfig: q.SystemConfig.WithContext(ctx),
|
||||
Tenant: q.Tenant.WithContext(ctx),
|
||||
TenantInvite: q.TenantInvite.WithContext(ctx),
|
||||
TenantJoinRequest: q.TenantJoinRequest.WithContext(ctx),
|
||||
|
||||
61
backend/database/models/system_configs.gen.go
Normal file
61
backend/database/models/system_configs.gen.go
Normal file
@@ -0,0 +1,61 @@
|
||||
// Code generated by go.ipao.vip/gen. DO NOT EDIT.
|
||||
// Code generated by go.ipao.vip/gen. DO NOT EDIT.
|
||||
// Code generated by go.ipao.vip/gen. DO NOT EDIT.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"go.ipao.vip/gen"
|
||||
"go.ipao.vip/gen/types"
|
||||
)
|
||||
|
||||
const TableNameSystemConfig = "system_configs"
|
||||
|
||||
// SystemConfig mapped from table <system_configs>
|
||||
type SystemConfig struct {
|
||||
ID int64 `gorm:"column:id;type:bigint;primaryKey;autoIncrement:true;comment:主键ID。" json:"id"` // 主键ID。
|
||||
ConfigKey string `gorm:"column:config_key;type:character varying(64);not null;comment:配置项Key;用途:按Key读取/更新;约束:唯一。" json:"config_key"` // 配置项Key;用途:按Key读取/更新;约束:唯一。
|
||||
Value types.JSON `gorm:"column:value;type:jsonb;not null;default:{};comment:配置值(JSON);用途:存储任意结构化配置;默认 {}。" json:"value"` // 配置值(JSON);用途:存储任意结构化配置;默认 {}。
|
||||
Description string `gorm:"column:description;type:character varying(255);not null;comment:配置说明;用途:给运营/技术理解用途。" json:"description"` // 配置说明;用途:给运营/技术理解用途。
|
||||
CreatedAt time.Time `gorm:"column:created_at;type:timestamp with time zone;not null;default:now();comment:创建时间;用途:审计与追溯。" json:"created_at"` // 创建时间;用途:审计与追溯。
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;type:timestamp with time zone;not null;default:now();comment:更新时间;用途:变更记录。" json:"updated_at"` // 更新时间;用途:变更记录。
|
||||
}
|
||||
|
||||
// Quick operations without importing query package
|
||||
// Update applies changed fields to the database using the default DB.
|
||||
func (m *SystemConfig) Update(ctx context.Context) (gen.ResultInfo, error) {
|
||||
return Q.SystemConfig.WithContext(ctx).Updates(m)
|
||||
}
|
||||
|
||||
// Save upserts the model using the default DB.
|
||||
func (m *SystemConfig) Save(ctx context.Context) error {
|
||||
return Q.SystemConfig.WithContext(ctx).Save(m)
|
||||
}
|
||||
|
||||
// Create inserts the model using the default DB.
|
||||
func (m *SystemConfig) Create(ctx context.Context) error {
|
||||
return Q.SystemConfig.WithContext(ctx).Create(m)
|
||||
}
|
||||
|
||||
// Delete removes the row represented by the model using the default DB.
|
||||
func (m *SystemConfig) Delete(ctx context.Context) (gen.ResultInfo, error) {
|
||||
return Q.SystemConfig.WithContext(ctx).Delete(m)
|
||||
}
|
||||
|
||||
// ForceDelete permanently deletes the row (ignores soft delete) using the default DB.
|
||||
func (m *SystemConfig) ForceDelete(ctx context.Context) (gen.ResultInfo, error) {
|
||||
return Q.SystemConfig.WithContext(ctx).Unscoped().Delete(m)
|
||||
}
|
||||
|
||||
// Reload reloads the model from database by its primary key and overwrites current fields.
|
||||
func (m *SystemConfig) Reload(ctx context.Context) error {
|
||||
fresh, err := Q.SystemConfig.WithContext(ctx).GetByID(m.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*m = *fresh
|
||||
return nil
|
||||
}
|
||||
481
backend/database/models/system_configs.query.gen.go
Normal file
481
backend/database/models/system_configs.query.gen.go
Normal file
@@ -0,0 +1,481 @@
|
||||
// Code generated by go.ipao.vip/gen. DO NOT EDIT.
|
||||
// Code generated by go.ipao.vip/gen. DO NOT EDIT.
|
||||
// Code generated by go.ipao.vip/gen. DO NOT EDIT.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
"gorm.io/gorm/schema"
|
||||
|
||||
"go.ipao.vip/gen"
|
||||
"go.ipao.vip/gen/field"
|
||||
|
||||
"gorm.io/plugin/dbresolver"
|
||||
)
|
||||
|
||||
func newSystemConfig(db *gorm.DB, opts ...gen.DOOption) systemConfigQuery {
|
||||
_systemConfigQuery := systemConfigQuery{}
|
||||
|
||||
_systemConfigQuery.systemConfigQueryDo.UseDB(db, opts...)
|
||||
_systemConfigQuery.systemConfigQueryDo.UseModel(&SystemConfig{})
|
||||
|
||||
tableName := _systemConfigQuery.systemConfigQueryDo.TableName()
|
||||
_systemConfigQuery.ALL = field.NewAsterisk(tableName)
|
||||
_systemConfigQuery.ID = field.NewInt64(tableName, "id")
|
||||
_systemConfigQuery.ConfigKey = field.NewString(tableName, "config_key")
|
||||
_systemConfigQuery.Value = field.NewJSONB(tableName, "value")
|
||||
_systemConfigQuery.Description = field.NewString(tableName, "description")
|
||||
_systemConfigQuery.CreatedAt = field.NewTime(tableName, "created_at")
|
||||
_systemConfigQuery.UpdatedAt = field.NewTime(tableName, "updated_at")
|
||||
|
||||
_systemConfigQuery.fillFieldMap()
|
||||
|
||||
return _systemConfigQuery
|
||||
}
|
||||
|
||||
type systemConfigQuery struct {
|
||||
systemConfigQueryDo systemConfigQueryDo
|
||||
|
||||
ALL field.Asterisk
|
||||
ID field.Int64 // 主键ID。
|
||||
ConfigKey field.String // 配置项Key;用途:按Key读取/更新;约束:唯一。
|
||||
Value field.JSONB // 配置值(JSON);用途:存储任意结构化配置;默认 {}。
|
||||
Description field.String // 配置说明;用途:给运营/技术理解用途。
|
||||
CreatedAt field.Time // 创建时间;用途:审计与追溯。
|
||||
UpdatedAt field.Time // 更新时间;用途:变更记录。
|
||||
|
||||
fieldMap map[string]field.Expr
|
||||
}
|
||||
|
||||
func (s systemConfigQuery) Table(newTableName string) *systemConfigQuery {
|
||||
s.systemConfigQueryDo.UseTable(newTableName)
|
||||
return s.updateTableName(newTableName)
|
||||
}
|
||||
|
||||
func (s systemConfigQuery) As(alias string) *systemConfigQuery {
|
||||
s.systemConfigQueryDo.DO = *(s.systemConfigQueryDo.As(alias).(*gen.DO))
|
||||
return s.updateTableName(alias)
|
||||
}
|
||||
|
||||
func (s *systemConfigQuery) updateTableName(table string) *systemConfigQuery {
|
||||
s.ALL = field.NewAsterisk(table)
|
||||
s.ID = field.NewInt64(table, "id")
|
||||
s.ConfigKey = field.NewString(table, "config_key")
|
||||
s.Value = field.NewJSONB(table, "value")
|
||||
s.Description = field.NewString(table, "description")
|
||||
s.CreatedAt = field.NewTime(table, "created_at")
|
||||
s.UpdatedAt = field.NewTime(table, "updated_at")
|
||||
|
||||
s.fillFieldMap()
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *systemConfigQuery) QueryContext(ctx context.Context) (*systemConfigQuery, *systemConfigQueryDo) {
|
||||
return s, s.systemConfigQueryDo.WithContext(ctx)
|
||||
}
|
||||
|
||||
func (s *systemConfigQuery) WithContext(ctx context.Context) *systemConfigQueryDo {
|
||||
return s.systemConfigQueryDo.WithContext(ctx)
|
||||
}
|
||||
|
||||
func (s systemConfigQuery) TableName() string { return s.systemConfigQueryDo.TableName() }
|
||||
|
||||
func (s systemConfigQuery) Alias() string { return s.systemConfigQueryDo.Alias() }
|
||||
|
||||
func (s systemConfigQuery) Columns(cols ...field.Expr) gen.Columns {
|
||||
return s.systemConfigQueryDo.Columns(cols...)
|
||||
}
|
||||
|
||||
func (s *systemConfigQuery) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
||||
_f, ok := s.fieldMap[fieldName]
|
||||
if !ok || _f == nil {
|
||||
return nil, false
|
||||
}
|
||||
_oe, ok := _f.(field.OrderExpr)
|
||||
return _oe, ok
|
||||
}
|
||||
|
||||
func (s *systemConfigQuery) fillFieldMap() {
|
||||
s.fieldMap = make(map[string]field.Expr, 6)
|
||||
s.fieldMap["id"] = s.ID
|
||||
s.fieldMap["config_key"] = s.ConfigKey
|
||||
s.fieldMap["value"] = s.Value
|
||||
s.fieldMap["description"] = s.Description
|
||||
s.fieldMap["created_at"] = s.CreatedAt
|
||||
s.fieldMap["updated_at"] = s.UpdatedAt
|
||||
}
|
||||
|
||||
func (s systemConfigQuery) clone(db *gorm.DB) systemConfigQuery {
|
||||
s.systemConfigQueryDo.ReplaceConnPool(db.Statement.ConnPool)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s systemConfigQuery) replaceDB(db *gorm.DB) systemConfigQuery {
|
||||
s.systemConfigQueryDo.ReplaceDB(db)
|
||||
return s
|
||||
}
|
||||
|
||||
type systemConfigQueryDo struct{ gen.DO }
|
||||
|
||||
func (s systemConfigQueryDo) Debug() *systemConfigQueryDo {
|
||||
return s.withDO(s.DO.Debug())
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) WithContext(ctx context.Context) *systemConfigQueryDo {
|
||||
return s.withDO(s.DO.WithContext(ctx))
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) ReadDB() *systemConfigQueryDo {
|
||||
return s.Clauses(dbresolver.Read)
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) WriteDB() *systemConfigQueryDo {
|
||||
return s.Clauses(dbresolver.Write)
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) Session(config *gorm.Session) *systemConfigQueryDo {
|
||||
return s.withDO(s.DO.Session(config))
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) Clauses(conds ...clause.Expression) *systemConfigQueryDo {
|
||||
return s.withDO(s.DO.Clauses(conds...))
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) Returning(value interface{}, columns ...string) *systemConfigQueryDo {
|
||||
return s.withDO(s.DO.Returning(value, columns...))
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) Not(conds ...gen.Condition) *systemConfigQueryDo {
|
||||
return s.withDO(s.DO.Not(conds...))
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) Or(conds ...gen.Condition) *systemConfigQueryDo {
|
||||
return s.withDO(s.DO.Or(conds...))
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) Select(conds ...field.Expr) *systemConfigQueryDo {
|
||||
return s.withDO(s.DO.Select(conds...))
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) Where(conds ...gen.Condition) *systemConfigQueryDo {
|
||||
return s.withDO(s.DO.Where(conds...))
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) Order(conds ...field.Expr) *systemConfigQueryDo {
|
||||
return s.withDO(s.DO.Order(conds...))
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) Distinct(cols ...field.Expr) *systemConfigQueryDo {
|
||||
return s.withDO(s.DO.Distinct(cols...))
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) Omit(cols ...field.Expr) *systemConfigQueryDo {
|
||||
return s.withDO(s.DO.Omit(cols...))
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) Join(table schema.Tabler, on ...field.Expr) *systemConfigQueryDo {
|
||||
return s.withDO(s.DO.Join(table, on...))
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) LeftJoin(table schema.Tabler, on ...field.Expr) *systemConfigQueryDo {
|
||||
return s.withDO(s.DO.LeftJoin(table, on...))
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) RightJoin(table schema.Tabler, on ...field.Expr) *systemConfigQueryDo {
|
||||
return s.withDO(s.DO.RightJoin(table, on...))
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) Group(cols ...field.Expr) *systemConfigQueryDo {
|
||||
return s.withDO(s.DO.Group(cols...))
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) Having(conds ...gen.Condition) *systemConfigQueryDo {
|
||||
return s.withDO(s.DO.Having(conds...))
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) Limit(limit int) *systemConfigQueryDo {
|
||||
return s.withDO(s.DO.Limit(limit))
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) Offset(offset int) *systemConfigQueryDo {
|
||||
return s.withDO(s.DO.Offset(offset))
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *systemConfigQueryDo {
|
||||
return s.withDO(s.DO.Scopes(funcs...))
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) Unscoped() *systemConfigQueryDo {
|
||||
return s.withDO(s.DO.Unscoped())
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) Create(values ...*SystemConfig) error {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
return s.DO.Create(values)
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) CreateInBatches(values []*SystemConfig, batchSize int) error {
|
||||
return s.DO.CreateInBatches(values, batchSize)
|
||||
}
|
||||
|
||||
// Save : !!! underlying implementation is different with GORM
|
||||
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
|
||||
func (s systemConfigQueryDo) Save(values ...*SystemConfig) error {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
return s.DO.Save(values)
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) First() (*SystemConfig, error) {
|
||||
if result, err := s.DO.First(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*SystemConfig), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) Take() (*SystemConfig, error) {
|
||||
if result, err := s.DO.Take(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*SystemConfig), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) Last() (*SystemConfig, error) {
|
||||
if result, err := s.DO.Last(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*SystemConfig), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) Find() ([]*SystemConfig, error) {
|
||||
result, err := s.DO.Find()
|
||||
return result.([]*SystemConfig), err
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*SystemConfig, err error) {
|
||||
buf := make([]*SystemConfig, 0, batchSize)
|
||||
err = s.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
|
||||
defer func() { results = append(results, buf...) }()
|
||||
return fc(tx, batch)
|
||||
})
|
||||
return results, err
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) FindInBatches(result *[]*SystemConfig, batchSize int, fc func(tx gen.Dao, batch int) error) error {
|
||||
return s.DO.FindInBatches(result, batchSize, fc)
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) Attrs(attrs ...field.AssignExpr) *systemConfigQueryDo {
|
||||
return s.withDO(s.DO.Attrs(attrs...))
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) Assign(attrs ...field.AssignExpr) *systemConfigQueryDo {
|
||||
return s.withDO(s.DO.Assign(attrs...))
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) Joins(fields ...field.RelationField) *systemConfigQueryDo {
|
||||
for _, _f := range fields {
|
||||
s = *s.withDO(s.DO.Joins(_f))
|
||||
}
|
||||
return &s
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) Preload(fields ...field.RelationField) *systemConfigQueryDo {
|
||||
for _, _f := range fields {
|
||||
s = *s.withDO(s.DO.Preload(_f))
|
||||
}
|
||||
return &s
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) FirstOrInit() (*SystemConfig, error) {
|
||||
if result, err := s.DO.FirstOrInit(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*SystemConfig), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) FirstOrCreate() (*SystemConfig, error) {
|
||||
if result, err := s.DO.FirstOrCreate(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*SystemConfig), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) FindByPage(offset int, limit int) (result []*SystemConfig, count int64, err error) {
|
||||
result, err = s.Offset(offset).Limit(limit).Find()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if size := len(result); 0 < limit && 0 < size && size < limit {
|
||||
count = int64(size + offset)
|
||||
return
|
||||
}
|
||||
|
||||
count, err = s.Offset(-1).Limit(-1).Count()
|
||||
return
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
|
||||
count, err = s.Count()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = s.Offset(offset).Limit(limit).Scan(result)
|
||||
return
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) Scan(result interface{}) (err error) {
|
||||
return s.DO.Scan(result)
|
||||
}
|
||||
|
||||
func (s systemConfigQueryDo) Delete(models ...*SystemConfig) (result gen.ResultInfo, err error) {
|
||||
return s.DO.Delete(models)
|
||||
}
|
||||
|
||||
// ForceDelete performs a permanent delete (ignores soft-delete) for current scope.
|
||||
func (s systemConfigQueryDo) ForceDelete() (gen.ResultInfo, error) {
|
||||
return s.Unscoped().Delete()
|
||||
}
|
||||
|
||||
// Inc increases the given column by step for current scope.
|
||||
func (s systemConfigQueryDo) Inc(column field.Expr, step int64) (gen.ResultInfo, error) {
|
||||
// column = column + step
|
||||
e := field.NewUnsafeFieldRaw("?+?", column.RawExpr(), step)
|
||||
return s.DO.UpdateColumn(column, e)
|
||||
}
|
||||
|
||||
// Dec decreases the given column by step for current scope.
|
||||
func (s systemConfigQueryDo) Dec(column field.Expr, step int64) (gen.ResultInfo, error) {
|
||||
// column = column - step
|
||||
e := field.NewUnsafeFieldRaw("?-?", column.RawExpr(), step)
|
||||
return s.DO.UpdateColumn(column, e)
|
||||
}
|
||||
|
||||
// Sum returns SUM(column) for current scope.
|
||||
func (s systemConfigQueryDo) Sum(column field.Expr) (float64, error) {
|
||||
var _v float64
|
||||
agg := field.NewUnsafeFieldRaw("SUM(?)", column.RawExpr())
|
||||
if err := s.Select(agg).Scan(&_v); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return _v, nil
|
||||
}
|
||||
|
||||
// Avg returns AVG(column) for current scope.
|
||||
func (s systemConfigQueryDo) Avg(column field.Expr) (float64, error) {
|
||||
var _v float64
|
||||
agg := field.NewUnsafeFieldRaw("AVG(?)", column.RawExpr())
|
||||
if err := s.Select(agg).Scan(&_v); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return _v, nil
|
||||
}
|
||||
|
||||
// Min returns MIN(column) for current scope.
|
||||
func (s systemConfigQueryDo) Min(column field.Expr) (float64, error) {
|
||||
var _v float64
|
||||
agg := field.NewUnsafeFieldRaw("MIN(?)", column.RawExpr())
|
||||
if err := s.Select(agg).Scan(&_v); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return _v, nil
|
||||
}
|
||||
|
||||
// Max returns MAX(column) for current scope.
|
||||
func (s systemConfigQueryDo) Max(column field.Expr) (float64, error) {
|
||||
var _v float64
|
||||
agg := field.NewUnsafeFieldRaw("MAX(?)", column.RawExpr())
|
||||
if err := s.Select(agg).Scan(&_v); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return _v, nil
|
||||
}
|
||||
|
||||
// PluckMap returns a map[key]value for selected key/value expressions within current scope.
|
||||
func (s systemConfigQueryDo) PluckMap(key, val field.Expr) (map[interface{}]interface{}, error) {
|
||||
do := s.Select(key, val)
|
||||
rows, err := do.DO.Rows()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
mm := make(map[interface{}]interface{})
|
||||
for rows.Next() {
|
||||
var k interface{}
|
||||
var v interface{}
|
||||
if err := rows.Scan(&k, &v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mm[k] = v
|
||||
}
|
||||
return mm, rows.Err()
|
||||
}
|
||||
|
||||
// Exists returns true if any record matches the given conditions.
|
||||
func (s systemConfigQueryDo) Exists(conds ...gen.Condition) (bool, error) {
|
||||
cnt, err := s.Where(conds...).Count()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return cnt > 0, nil
|
||||
}
|
||||
|
||||
// PluckIDs returns all primary key values under current scope.
|
||||
func (s systemConfigQueryDo) PluckIDs() ([]int64, error) {
|
||||
ids := make([]int64, 0, 16)
|
||||
pk := field.NewInt64(s.TableName(), "id")
|
||||
if err := s.DO.Pluck(pk, &ids); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
// GetByID finds a single record by primary key.
|
||||
func (s systemConfigQueryDo) GetByID(id int64) (*SystemConfig, error) {
|
||||
pk := field.NewInt64(s.TableName(), "id")
|
||||
return s.Where(pk.Eq(id)).First()
|
||||
}
|
||||
|
||||
// GetByIDs finds records by primary key list.
|
||||
func (s systemConfigQueryDo) GetByIDs(ids ...int64) ([]*SystemConfig, error) {
|
||||
if len(ids) == 0 {
|
||||
return []*SystemConfig{}, nil
|
||||
}
|
||||
pk := field.NewInt64(s.TableName(), "id")
|
||||
return s.Where(pk.In(ids...)).Find()
|
||||
}
|
||||
|
||||
// DeleteByID deletes records by primary key.
|
||||
func (s systemConfigQueryDo) DeleteByID(id int64) (gen.ResultInfo, error) {
|
||||
pk := field.NewInt64(s.TableName(), "id")
|
||||
return s.Where(pk.Eq(id)).Delete()
|
||||
}
|
||||
|
||||
// DeleteByIDs deletes records by a list of primary keys.
|
||||
func (s systemConfigQueryDo) DeleteByIDs(ids ...int64) (gen.ResultInfo, error) {
|
||||
if len(ids) == 0 {
|
||||
return gen.ResultInfo{RowsAffected: 0, Error: nil}, nil
|
||||
}
|
||||
pk := field.NewInt64(s.TableName(), "id")
|
||||
return s.Where(pk.In(ids...)).Delete()
|
||||
}
|
||||
|
||||
func (s *systemConfigQueryDo) withDO(do gen.Dao) *systemConfigQueryDo {
|
||||
s.DO = *do.(*gen.DO)
|
||||
return s
|
||||
}
|
||||
@@ -138,6 +138,58 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/super/v1/audit-logs": {
|
||||
"get": {
|
||||
"description": "List audit logs across tenants",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Audit"
|
||||
],
|
||||
"summary": "List audit logs",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Page number",
|
||||
"name": "page",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Page size",
|
||||
"name": "limit",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/requests.Pager"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/dto.SuperAuditLogItem"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/super/v1/auth/login": {
|
||||
"post": {
|
||||
"description": "Login",
|
||||
@@ -1138,6 +1190,132 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/super/v1/system-configs": {
|
||||
"get": {
|
||||
"description": "List platform system configs",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"SystemConfig"
|
||||
],
|
||||
"summary": "List system configs",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Page number",
|
||||
"name": "page",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Page size",
|
||||
"name": "limit",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/requests.Pager"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/dto.SuperSystemConfigItem"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"description": "Create platform system config",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"SystemConfig"
|
||||
],
|
||||
"summary": "Create system config",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Create form",
|
||||
"name": "form",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.SuperSystemConfigCreateForm"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.SuperSystemConfigItem"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/super/v1/system-configs/{id}": {
|
||||
"patch": {
|
||||
"description": "Update platform system config",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"SystemConfig"
|
||||
],
|
||||
"summary": "Update system config",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "Config ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Update form",
|
||||
"name": "form",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.SuperSystemConfigUpdateForm"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.SuperSystemConfigItem"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/super/v1/tenant-join-requests": {
|
||||
"get": {
|
||||
"description": "List tenant join requests across tenants",
|
||||
@@ -7212,6 +7390,51 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.SuperAuditLogItem": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": {
|
||||
"description": "Action 动作标识。",
|
||||
"type": "string"
|
||||
},
|
||||
"created_at": {
|
||||
"description": "CreatedAt 创建时间(RFC3339)。",
|
||||
"type": "string"
|
||||
},
|
||||
"detail": {
|
||||
"description": "Detail 操作详情。",
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"description": "ID 审计日志ID。",
|
||||
"type": "integer"
|
||||
},
|
||||
"operator_id": {
|
||||
"description": "OperatorID 操作者用户ID。",
|
||||
"type": "integer"
|
||||
},
|
||||
"operator_name": {
|
||||
"description": "OperatorName 操作者用户名/昵称。",
|
||||
"type": "string"
|
||||
},
|
||||
"target_id": {
|
||||
"description": "TargetID 目标ID。",
|
||||
"type": "string"
|
||||
},
|
||||
"tenant_code": {
|
||||
"description": "TenantCode 租户编码。",
|
||||
"type": "string"
|
||||
},
|
||||
"tenant_id": {
|
||||
"description": "TenantID 租户ID。",
|
||||
"type": "integer"
|
||||
},
|
||||
"tenant_name": {
|
||||
"description": "TenantName 租户名称。",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.SuperContentBatchReviewForm": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -7992,6 +8215,74 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.SuperSystemConfigCreateForm": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"config_key": {
|
||||
"description": "ConfigKey 配置项Key(唯一)。",
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"description": "Description 配置说明。",
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"description": "Value 配置值(JSON)。",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.SuperSystemConfigItem": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"config_key": {
|
||||
"description": "ConfigKey 配置项Key。",
|
||||
"type": "string"
|
||||
},
|
||||
"created_at": {
|
||||
"description": "CreatedAt 创建时间(RFC3339)。",
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"description": "Description 配置说明。",
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"description": "ID 配置ID。",
|
||||
"type": "integer"
|
||||
},
|
||||
"updated_at": {
|
||||
"description": "UpdatedAt 更新时间(RFC3339)。",
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"description": "Value 配置值(JSON)。",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.SuperSystemConfigUpdateForm": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"description": "Description 配置说明(可选)。",
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"description": "Value 配置值(JSON,可选)。",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.SuperTenantContentStatusUpdateForm": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
||||
@@ -132,6 +132,58 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/super/v1/audit-logs": {
|
||||
"get": {
|
||||
"description": "List audit logs across tenants",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Audit"
|
||||
],
|
||||
"summary": "List audit logs",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Page number",
|
||||
"name": "page",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Page size",
|
||||
"name": "limit",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/requests.Pager"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/dto.SuperAuditLogItem"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/super/v1/auth/login": {
|
||||
"post": {
|
||||
"description": "Login",
|
||||
@@ -1132,6 +1184,132 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/super/v1/system-configs": {
|
||||
"get": {
|
||||
"description": "List platform system configs",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"SystemConfig"
|
||||
],
|
||||
"summary": "List system configs",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Page number",
|
||||
"name": "page",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Page size",
|
||||
"name": "limit",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/requests.Pager"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/dto.SuperSystemConfigItem"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"description": "Create platform system config",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"SystemConfig"
|
||||
],
|
||||
"summary": "Create system config",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Create form",
|
||||
"name": "form",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.SuperSystemConfigCreateForm"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.SuperSystemConfigItem"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/super/v1/system-configs/{id}": {
|
||||
"patch": {
|
||||
"description": "Update platform system config",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"SystemConfig"
|
||||
],
|
||||
"summary": "Update system config",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "Config ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Update form",
|
||||
"name": "form",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.SuperSystemConfigUpdateForm"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.SuperSystemConfigItem"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/super/v1/tenant-join-requests": {
|
||||
"get": {
|
||||
"description": "List tenant join requests across tenants",
|
||||
@@ -7206,6 +7384,51 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.SuperAuditLogItem": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": {
|
||||
"description": "Action 动作标识。",
|
||||
"type": "string"
|
||||
},
|
||||
"created_at": {
|
||||
"description": "CreatedAt 创建时间(RFC3339)。",
|
||||
"type": "string"
|
||||
},
|
||||
"detail": {
|
||||
"description": "Detail 操作详情。",
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"description": "ID 审计日志ID。",
|
||||
"type": "integer"
|
||||
},
|
||||
"operator_id": {
|
||||
"description": "OperatorID 操作者用户ID。",
|
||||
"type": "integer"
|
||||
},
|
||||
"operator_name": {
|
||||
"description": "OperatorName 操作者用户名/昵称。",
|
||||
"type": "string"
|
||||
},
|
||||
"target_id": {
|
||||
"description": "TargetID 目标ID。",
|
||||
"type": "string"
|
||||
},
|
||||
"tenant_code": {
|
||||
"description": "TenantCode 租户编码。",
|
||||
"type": "string"
|
||||
},
|
||||
"tenant_id": {
|
||||
"description": "TenantID 租户ID。",
|
||||
"type": "integer"
|
||||
},
|
||||
"tenant_name": {
|
||||
"description": "TenantName 租户名称。",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.SuperContentBatchReviewForm": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -7986,6 +8209,74 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.SuperSystemConfigCreateForm": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"config_key": {
|
||||
"description": "ConfigKey 配置项Key(唯一)。",
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"description": "Description 配置说明。",
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"description": "Value 配置值(JSON)。",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.SuperSystemConfigItem": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"config_key": {
|
||||
"description": "ConfigKey 配置项Key。",
|
||||
"type": "string"
|
||||
},
|
||||
"created_at": {
|
||||
"description": "CreatedAt 创建时间(RFC3339)。",
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"description": "Description 配置说明。",
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"description": "ID 配置ID。",
|
||||
"type": "integer"
|
||||
},
|
||||
"updated_at": {
|
||||
"description": "UpdatedAt 更新时间(RFC3339)。",
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"description": "Value 配置值(JSON)。",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.SuperSystemConfigUpdateForm": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"description": "Description 配置说明(可选)。",
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"description": "Value 配置值(JSON,可选)。",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.SuperTenantContentStatusUpdateForm": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
||||
@@ -1231,6 +1231,39 @@ definitions:
|
||||
description: TotalSize 资产总大小(字节)。
|
||||
type: integer
|
||||
type: object
|
||||
dto.SuperAuditLogItem:
|
||||
properties:
|
||||
action:
|
||||
description: Action 动作标识。
|
||||
type: string
|
||||
created_at:
|
||||
description: CreatedAt 创建时间(RFC3339)。
|
||||
type: string
|
||||
detail:
|
||||
description: Detail 操作详情。
|
||||
type: string
|
||||
id:
|
||||
description: ID 审计日志ID。
|
||||
type: integer
|
||||
operator_id:
|
||||
description: OperatorID 操作者用户ID。
|
||||
type: integer
|
||||
operator_name:
|
||||
description: OperatorName 操作者用户名/昵称。
|
||||
type: string
|
||||
target_id:
|
||||
description: TargetID 目标ID。
|
||||
type: string
|
||||
tenant_code:
|
||||
description: TenantCode 租户编码。
|
||||
type: string
|
||||
tenant_id:
|
||||
description: TenantID 租户ID。
|
||||
type: integer
|
||||
tenant_name:
|
||||
description: TenantName 租户名称。
|
||||
type: string
|
||||
type: object
|
||||
dto.SuperContentBatchReviewForm:
|
||||
properties:
|
||||
action:
|
||||
@@ -1766,6 +1799,54 @@ definitions:
|
||||
description: TenantID 租户ID(不传代表全平台)。
|
||||
type: integer
|
||||
type: object
|
||||
dto.SuperSystemConfigCreateForm:
|
||||
properties:
|
||||
config_key:
|
||||
description: ConfigKey 配置项Key(唯一)。
|
||||
type: string
|
||||
description:
|
||||
description: Description 配置说明。
|
||||
type: string
|
||||
value:
|
||||
description: Value 配置值(JSON)。
|
||||
items:
|
||||
type: integer
|
||||
type: array
|
||||
type: object
|
||||
dto.SuperSystemConfigItem:
|
||||
properties:
|
||||
config_key:
|
||||
description: ConfigKey 配置项Key。
|
||||
type: string
|
||||
created_at:
|
||||
description: CreatedAt 创建时间(RFC3339)。
|
||||
type: string
|
||||
description:
|
||||
description: Description 配置说明。
|
||||
type: string
|
||||
id:
|
||||
description: ID 配置ID。
|
||||
type: integer
|
||||
updated_at:
|
||||
description: UpdatedAt 更新时间(RFC3339)。
|
||||
type: string
|
||||
value:
|
||||
description: Value 配置值(JSON)。
|
||||
items:
|
||||
type: integer
|
||||
type: array
|
||||
type: object
|
||||
dto.SuperSystemConfigUpdateForm:
|
||||
properties:
|
||||
description:
|
||||
description: Description 配置说明(可选)。
|
||||
type: string
|
||||
value:
|
||||
description: Value 配置值(JSON,可选)。
|
||||
items:
|
||||
type: integer
|
||||
type: array
|
||||
type: object
|
||||
dto.SuperTenantContentStatusUpdateForm:
|
||||
properties:
|
||||
status:
|
||||
@@ -2936,6 +3017,37 @@ paths:
|
||||
summary: Asset usage
|
||||
tags:
|
||||
- Asset
|
||||
/super/v1/audit-logs:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: List audit logs across tenants
|
||||
parameters:
|
||||
- description: Page number
|
||||
in: query
|
||||
name: page
|
||||
type: integer
|
||||
- description: Page size
|
||||
in: query
|
||||
name: limit
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/requests.Pager'
|
||||
- properties:
|
||||
items:
|
||||
items:
|
||||
$ref: '#/definitions/dto.SuperAuditLogItem'
|
||||
type: array
|
||||
type: object
|
||||
summary: List audit logs
|
||||
tags:
|
||||
- Audit
|
||||
/super/v1/auth/login:
|
||||
post:
|
||||
consumes:
|
||||
@@ -3562,6 +3674,86 @@ paths:
|
||||
summary: Report overview
|
||||
tags:
|
||||
- Report
|
||||
/super/v1/system-configs:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: List platform system configs
|
||||
parameters:
|
||||
- description: Page number
|
||||
in: query
|
||||
name: page
|
||||
type: integer
|
||||
- description: Page size
|
||||
in: query
|
||||
name: limit
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/requests.Pager'
|
||||
- properties:
|
||||
items:
|
||||
items:
|
||||
$ref: '#/definitions/dto.SuperSystemConfigItem'
|
||||
type: array
|
||||
type: object
|
||||
summary: List system configs
|
||||
tags:
|
||||
- SystemConfig
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Create platform system config
|
||||
parameters:
|
||||
- description: Create form
|
||||
in: body
|
||||
name: form
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dto.SuperSystemConfigCreateForm'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/dto.SuperSystemConfigItem'
|
||||
summary: Create system config
|
||||
tags:
|
||||
- SystemConfig
|
||||
/super/v1/system-configs/{id}:
|
||||
patch:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Update platform system config
|
||||
parameters:
|
||||
- description: Config ID
|
||||
format: int64
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: integer
|
||||
- description: Update form
|
||||
in: body
|
||||
name: form
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dto.SuperSystemConfigUpdateForm'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/dto.SuperSystemConfigItem'
|
||||
summary: Update system config
|
||||
tags:
|
||||
- SystemConfig
|
||||
/super/v1/tenant-join-requests:
|
||||
get:
|
||||
consumes:
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
## 1) 总体结论
|
||||
|
||||
- **已落地**:登录、租户/用户/订单/内容基础管理、内容审核(含批量)、平台概览(内容趋势/退款率/漏斗)、提现审核、报表概览与导出、用户钱包/通知/优惠券/实名/充值记录、互动(收藏/点赞/关注)与内容消费明细视图、创作者申请/成员审核/邀请、优惠券创建/编辑/发放/冻结/发放记录/异常核查、资产治理(列表/用量/清理)、通知中心(列表/群发/模板)。
|
||||
- **已落地**:登录、租户/用户/订单/内容基础管理、内容审核(含批量)、平台概览(内容趋势/退款率/漏斗)、提现审核、报表概览与导出、用户钱包/通知/优惠券/实名/充值记录、互动(收藏/点赞/关注)与内容消费明细视图、创作者申请/成员审核/邀请、优惠券创建/编辑/发放/冻结/发放记录/异常核查、资产治理(列表/用量/清理)、通知中心(列表/群发/模板)、审计日志与系统配置。
|
||||
- **部分落地**:租户详情(缺财务/报表聚合)、内容治理(缺评论/举报)、创作者治理(缺提现审核联动与结算账户审批流)、财务(缺钱包流水/异常排查)。
|
||||
- **未落地**:审计与系统配置类能力。
|
||||
- **未落地**:暂无。
|
||||
|
||||
## 2) 按页面完成度(对照 2.x)
|
||||
|
||||
@@ -80,12 +80,17 @@
|
||||
- 已有:通知列表、批量发送、模板管理。
|
||||
- 缺口:无显著功能缺口。
|
||||
|
||||
### 2.15 审计与系统配置 `/superadmin/audit-logs` `/superadmin/system-configs`
|
||||
- 状态:**已完成**
|
||||
- 已有:跨租户审计日志查询、系统配置列表/创建/更新。
|
||||
- 缺口:无显著功能缺口。
|
||||
|
||||
## 3) `/super/v1` 接口覆盖度概览
|
||||
|
||||
- **已具备**:Auth、Tenants(含成员审核/邀请)、Users(含钱包/通知/优惠券/实名/互动/内容消费)、Contents、Orders、Withdrawals、Reports、Coupons(列表/创建/编辑/发放/冻结/记录)、Creators(列表/申请/成员审核)、Payout Accounts(列表/删除)、Assets(列表/用量/删除)、Notifications(列表/群发/模板)。
|
||||
- **缺失/待补**:创作者提现审核、审计与系统配置类能力。
|
||||
- **缺失/待补**:创作者提现审核。
|
||||
|
||||
## 4) 建议的下一步(按优先级)
|
||||
|
||||
1. **审计与系统配置**:完善全量操作审计与系统级配置能力。
|
||||
2. **创作者提现审核**:补齐跨租户提现审核与财务联动入口。
|
||||
1. **创作者提现审核**:补齐跨租户提现审核与财务联动入口。
|
||||
2. **内容/财务治理补齐**:评论/举报治理、钱包流水与异常排查能力。
|
||||
|
||||
@@ -20,7 +20,9 @@ const model = ref([
|
||||
{ label: 'Finance', icon: 'pi pi-fw pi-wallet', to: '/superadmin/finance' },
|
||||
{ label: 'Reports', icon: 'pi pi-fw pi-chart-line', to: '/superadmin/reports' },
|
||||
{ label: 'Assets', icon: 'pi pi-fw pi-folder', to: '/superadmin/assets' },
|
||||
{ label: 'Notifications', icon: 'pi pi-fw pi-bell', to: '/superadmin/notifications' }
|
||||
{ label: 'Notifications', icon: 'pi pi-fw pi-bell', to: '/superadmin/notifications' },
|
||||
{ label: 'Audit Logs', icon: 'pi pi-fw pi-shield', to: '/superadmin/audit-logs' },
|
||||
{ label: 'System Configs', icon: 'pi pi-fw pi-cog', to: '/superadmin/system-configs' }
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
@@ -174,6 +174,16 @@ const router = createRouter({
|
||||
name: 'superadmin-notifications',
|
||||
component: () => import('@/views/superadmin/Notifications.vue')
|
||||
},
|
||||
{
|
||||
path: '/superadmin/audit-logs',
|
||||
name: 'superadmin-audit-logs',
|
||||
component: () => import('@/views/superadmin/AuditLogs.vue')
|
||||
},
|
||||
{
|
||||
path: '/superadmin/system-configs',
|
||||
name: 'superadmin-system-configs',
|
||||
component: () => import('@/views/superadmin/SystemConfigs.vue')
|
||||
},
|
||||
{
|
||||
path: '/superadmin/orders/:orderID',
|
||||
name: 'superadmin-order-detail',
|
||||
|
||||
46
frontend/superadmin/src/service/AuditService.js
Normal file
46
frontend/superadmin/src/service/AuditService.js
Normal file
@@ -0,0 +1,46 @@
|
||||
import { requestJson } from './apiClient';
|
||||
|
||||
function normalizeItems(items) {
|
||||
if (Array.isArray(items)) return items;
|
||||
if (items && typeof items === 'object') return [items];
|
||||
return [];
|
||||
}
|
||||
|
||||
export const AuditService = {
|
||||
async listAuditLogs({ page, limit, id, tenant_id, tenant_code, tenant_name, operator_id, operator_name, action, target_id, keyword, created_at_from, created_at_to, sortField, sortOrder } = {}) {
|
||||
const iso = (d) => {
|
||||
if (!d) return undefined;
|
||||
const date = d instanceof Date ? d : new Date(d);
|
||||
if (Number.isNaN(date.getTime())) return undefined;
|
||||
return date.toISOString();
|
||||
};
|
||||
|
||||
const query = {
|
||||
page,
|
||||
limit,
|
||||
id,
|
||||
tenant_id,
|
||||
tenant_code,
|
||||
tenant_name,
|
||||
operator_id,
|
||||
operator_name,
|
||||
action,
|
||||
target_id,
|
||||
keyword,
|
||||
created_at_from: iso(created_at_from),
|
||||
created_at_to: iso(created_at_to)
|
||||
};
|
||||
if (sortField && sortOrder) {
|
||||
if (sortOrder === 1) query.asc = sortField;
|
||||
if (sortOrder === -1) query.desc = sortField;
|
||||
}
|
||||
|
||||
const data = await requestJson('/super/v1/audit-logs', { query });
|
||||
return {
|
||||
page: data?.page ?? page ?? 1,
|
||||
limit: data?.limit ?? limit ?? 10,
|
||||
total: data?.total ?? 0,
|
||||
items: normalizeItems(data?.items)
|
||||
};
|
||||
}
|
||||
};
|
||||
60
frontend/superadmin/src/service/SystemConfigService.js
Normal file
60
frontend/superadmin/src/service/SystemConfigService.js
Normal file
@@ -0,0 +1,60 @@
|
||||
import { requestJson } from './apiClient';
|
||||
|
||||
function normalizeItems(items) {
|
||||
if (Array.isArray(items)) return items;
|
||||
if (items && typeof items === 'object') return [items];
|
||||
return [];
|
||||
}
|
||||
|
||||
export const SystemConfigService = {
|
||||
async listConfigs({ page, limit, config_key, keyword, created_at_from, created_at_to, updated_at_from, updated_at_to, sortField, sortOrder } = {}) {
|
||||
const iso = (d) => {
|
||||
if (!d) return undefined;
|
||||
const date = d instanceof Date ? d : new Date(d);
|
||||
if (Number.isNaN(date.getTime())) return undefined;
|
||||
return date.toISOString();
|
||||
};
|
||||
|
||||
const query = {
|
||||
page,
|
||||
limit,
|
||||
config_key,
|
||||
keyword,
|
||||
created_at_from: iso(created_at_from),
|
||||
created_at_to: iso(created_at_to),
|
||||
updated_at_from: iso(updated_at_from),
|
||||
updated_at_to: iso(updated_at_to)
|
||||
};
|
||||
if (sortField && sortOrder) {
|
||||
if (sortOrder === 1) query.asc = sortField;
|
||||
if (sortOrder === -1) query.desc = sortField;
|
||||
}
|
||||
|
||||
const data = await requestJson('/super/v1/system-configs', { query });
|
||||
return {
|
||||
page: data?.page ?? page ?? 1,
|
||||
limit: data?.limit ?? limit ?? 10,
|
||||
total: data?.total ?? 0,
|
||||
items: normalizeItems(data?.items)
|
||||
};
|
||||
},
|
||||
async createConfig({ config_key, value, description } = {}) {
|
||||
return requestJson('/super/v1/system-configs', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
config_key,
|
||||
value,
|
||||
description
|
||||
}
|
||||
});
|
||||
},
|
||||
async updateConfig(id, { value, description } = {}) {
|
||||
return requestJson(`/super/v1/system-configs/${id}`, {
|
||||
method: 'PATCH',
|
||||
body: {
|
||||
value,
|
||||
description
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
192
frontend/superadmin/src/views/superadmin/AuditLogs.vue
Normal file
192
frontend/superadmin/src/views/superadmin/AuditLogs.vue
Normal file
@@ -0,0 +1,192 @@
|
||||
<script setup>
|
||||
import SearchField from '@/components/SearchField.vue';
|
||||
import SearchPanel from '@/components/SearchPanel.vue';
|
||||
import { AuditService } from '@/service/AuditService';
|
||||
import { useToast } from 'primevue/usetoast';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
const logs = ref([]);
|
||||
const loading = ref(false);
|
||||
const totalRecords = ref(0);
|
||||
const page = ref(1);
|
||||
const rows = ref(10);
|
||||
const sortField = ref('created_at');
|
||||
const sortOrder = ref(-1);
|
||||
|
||||
const logID = ref(null);
|
||||
const tenantID = ref(null);
|
||||
const tenantCode = ref('');
|
||||
const tenantName = ref('');
|
||||
const operatorID = ref(null);
|
||||
const operatorName = ref('');
|
||||
const action = ref('');
|
||||
const targetID = ref('');
|
||||
const keyword = ref('');
|
||||
const createdAtFrom = ref(null);
|
||||
const createdAtTo = ref(null);
|
||||
|
||||
function formatDate(value) {
|
||||
if (!value) return '-';
|
||||
if (String(value).startsWith('0001-01-01')) return '-';
|
||||
const date = new Date(value);
|
||||
if (Number.isNaN(date.getTime())) return String(value);
|
||||
return date.toLocaleString();
|
||||
}
|
||||
|
||||
function getActionSeverity(value) {
|
||||
if (!value) return 'secondary';
|
||||
if (value.includes('reject') || value.includes('freeze')) return 'danger';
|
||||
if (value.includes('approve') || value.includes('create')) return 'success';
|
||||
if (value.includes('update')) return 'info';
|
||||
return 'secondary';
|
||||
}
|
||||
|
||||
async function loadLogs() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const result = await AuditService.listAuditLogs({
|
||||
page: page.value,
|
||||
limit: rows.value,
|
||||
id: logID.value || undefined,
|
||||
tenant_id: tenantID.value || undefined,
|
||||
tenant_code: tenantCode.value,
|
||||
tenant_name: tenantName.value,
|
||||
operator_id: operatorID.value || undefined,
|
||||
operator_name: operatorName.value,
|
||||
action: action.value,
|
||||
target_id: targetID.value,
|
||||
keyword: keyword.value,
|
||||
created_at_from: createdAtFrom.value || undefined,
|
||||
created_at_to: createdAtTo.value || undefined,
|
||||
sortField: sortField.value,
|
||||
sortOrder: sortOrder.value
|
||||
});
|
||||
logs.value = result.items;
|
||||
totalRecords.value = result.total;
|
||||
} catch (error) {
|
||||
toast.add({ severity: 'error', summary: '加载失败', detail: error?.message || '无法加载审计日志', life: 4000 });
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onSearch() {
|
||||
page.value = 1;
|
||||
loadLogs();
|
||||
}
|
||||
|
||||
function onReset() {
|
||||
logID.value = null;
|
||||
tenantID.value = null;
|
||||
tenantCode.value = '';
|
||||
tenantName.value = '';
|
||||
operatorID.value = null;
|
||||
operatorName.value = '';
|
||||
action.value = '';
|
||||
targetID.value = '';
|
||||
keyword.value = '';
|
||||
createdAtFrom.value = null;
|
||||
createdAtTo.value = null;
|
||||
sortField.value = 'created_at';
|
||||
sortOrder.value = -1;
|
||||
page.value = 1;
|
||||
rows.value = 10;
|
||||
loadLogs();
|
||||
}
|
||||
|
||||
function onPage(event) {
|
||||
page.value = (event.page ?? 0) + 1;
|
||||
rows.value = event.rows ?? rows.value;
|
||||
loadLogs();
|
||||
}
|
||||
|
||||
function onSort(event) {
|
||||
sortField.value = event.sortField ?? sortField.value;
|
||||
sortOrder.value = event.sortOrder ?? sortOrder.value;
|
||||
loadLogs();
|
||||
}
|
||||
|
||||
loadLogs();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h4 class="m-0">审计日志</h4>
|
||||
</div>
|
||||
|
||||
<SearchPanel :loading="loading" @search="onSearch" @reset="onReset">
|
||||
<SearchField label="日志ID">
|
||||
<InputNumber v-model="logID" :min="1" placeholder="精确匹配" class="w-full" />
|
||||
</SearchField>
|
||||
<SearchField label="TenantID">
|
||||
<InputNumber v-model="tenantID" :min="1" placeholder="精确匹配" class="w-full" />
|
||||
</SearchField>
|
||||
<SearchField label="租户编码">
|
||||
<InputText v-model="tenantCode" placeholder="模糊匹配" class="w-full" @keyup.enter="onSearch" />
|
||||
</SearchField>
|
||||
<SearchField label="租户名称">
|
||||
<InputText v-model="tenantName" placeholder="模糊匹配" class="w-full" @keyup.enter="onSearch" />
|
||||
</SearchField>
|
||||
<SearchField label="操作者ID">
|
||||
<InputNumber v-model="operatorID" :min="1" placeholder="精确匹配" class="w-full" />
|
||||
</SearchField>
|
||||
<SearchField label="操作者">
|
||||
<InputText v-model="operatorName" placeholder="用户名/昵称" class="w-full" @keyup.enter="onSearch" />
|
||||
</SearchField>
|
||||
<SearchField label="动作">
|
||||
<InputText v-model="action" placeholder="动作标识" class="w-full" @keyup.enter="onSearch" />
|
||||
</SearchField>
|
||||
<SearchField label="目标ID">
|
||||
<InputText v-model="targetID" placeholder="目标ID" class="w-full" @keyup.enter="onSearch" />
|
||||
</SearchField>
|
||||
<SearchField label="关键字">
|
||||
<InputText v-model="keyword" placeholder="详情关键字" class="w-full" @keyup.enter="onSearch" />
|
||||
</SearchField>
|
||||
<SearchField label="创建时间 From">
|
||||
<DatePicker v-model="createdAtFrom" showIcon showButtonBar placeholder="开始时间" class="w-full" />
|
||||
</SearchField>
|
||||
<SearchField label="创建时间 To">
|
||||
<DatePicker v-model="createdAtTo" showIcon showButtonBar placeholder="结束时间" class="w-full" />
|
||||
</SearchField>
|
||||
</SearchPanel>
|
||||
|
||||
<DataTable :value="logs" :loading="loading" :rows="rows" :totalRecords="totalRecords" :first="(page - 1) * rows" paginator lazy dataKey="id" @page="onPage" @sort="onSort" :sortField="sortField" :sortOrder="sortOrder">
|
||||
<Column field="id" header="日志ID" style="min-width: 8rem" sortable />
|
||||
<Column header="租户" style="min-width: 14rem">
|
||||
<template #body="{ data }">
|
||||
<div class="flex flex-col">
|
||||
<span class="font-medium">{{ data.tenant_name || '-' }}</span>
|
||||
<span class="text-sm text-muted-color">{{ data.tenant_code || '-' }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
<Column header="操作者" style="min-width: 12rem">
|
||||
<template #body="{ data }">
|
||||
<div class="flex flex-col">
|
||||
<span class="font-medium">{{ data.operator_name || '-' }}</span>
|
||||
<span class="text-sm text-muted-color">ID: {{ data.operator_id ?? '-' }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="action" header="动作" style="min-width: 10rem">
|
||||
<template #body="{ data }">
|
||||
<Tag :value="data.action || '-'" :severity="getActionSeverity(data.action)" />
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="target_id" header="目标ID" style="min-width: 10rem" />
|
||||
<Column header="详情" style="min-width: 16rem">
|
||||
<template #body="{ data }">
|
||||
<span class="block max-w-[320px] truncate" :title="data.detail">{{ data.detail || '-' }}</span>
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="created_at" header="创建时间" style="min-width: 14rem">
|
||||
<template #body="{ data }">
|
||||
{{ formatDate(data.created_at) }}
|
||||
</template>
|
||||
</Column>
|
||||
</DataTable>
|
||||
</div>
|
||||
</template>
|
||||
276
frontend/superadmin/src/views/superadmin/SystemConfigs.vue
Normal file
276
frontend/superadmin/src/views/superadmin/SystemConfigs.vue
Normal file
@@ -0,0 +1,276 @@
|
||||
<script setup>
|
||||
import SearchField from '@/components/SearchField.vue';
|
||||
import SearchPanel from '@/components/SearchPanel.vue';
|
||||
import { SystemConfigService } from '@/service/SystemConfigService';
|
||||
import { useToast } from 'primevue/usetoast';
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
const configs = ref([]);
|
||||
const loading = ref(false);
|
||||
const totalRecords = ref(0);
|
||||
const page = ref(1);
|
||||
const rows = ref(10);
|
||||
const sortField = ref('updated_at');
|
||||
const sortOrder = ref(-1);
|
||||
|
||||
const configKey = ref('');
|
||||
const keyword = ref('');
|
||||
const createdAtFrom = ref(null);
|
||||
const createdAtTo = ref(null);
|
||||
const updatedAtFrom = ref(null);
|
||||
const updatedAtTo = ref(null);
|
||||
|
||||
const editDialogVisible = ref(false);
|
||||
const editSubmitting = ref(false);
|
||||
const editingConfig = ref(null);
|
||||
const formKey = ref('');
|
||||
const formDescription = ref('');
|
||||
const formValueText = ref('');
|
||||
|
||||
const dialogTitle = computed(() => (editingConfig.value ? '编辑系统配置' : '新建系统配置'));
|
||||
|
||||
function formatDate(value) {
|
||||
if (!value) return '-';
|
||||
if (String(value).startsWith('0001-01-01')) return '-';
|
||||
const date = new Date(value);
|
||||
if (Number.isNaN(date.getTime())) return String(value);
|
||||
return date.toLocaleString();
|
||||
}
|
||||
|
||||
function formatJsonPreview(value) {
|
||||
if (value === undefined || value === null) return '-';
|
||||
try {
|
||||
return JSON.stringify(value);
|
||||
} catch (error) {
|
||||
return String(value);
|
||||
}
|
||||
}
|
||||
|
||||
function toJsonText(value) {
|
||||
if (value === undefined || value === null) return '';
|
||||
try {
|
||||
return JSON.stringify(value, null, 2);
|
||||
} catch (error) {
|
||||
return String(value);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadConfigs() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const result = await SystemConfigService.listConfigs({
|
||||
page: page.value,
|
||||
limit: rows.value,
|
||||
config_key: configKey.value,
|
||||
keyword: keyword.value,
|
||||
created_at_from: createdAtFrom.value || undefined,
|
||||
created_at_to: createdAtTo.value || undefined,
|
||||
updated_at_from: updatedAtFrom.value || undefined,
|
||||
updated_at_to: updatedAtTo.value || undefined,
|
||||
sortField: sortField.value,
|
||||
sortOrder: sortOrder.value
|
||||
});
|
||||
configs.value = result.items;
|
||||
totalRecords.value = result.total;
|
||||
} catch (error) {
|
||||
toast.add({ severity: 'error', summary: '加载失败', detail: error?.message || '无法加载系统配置', life: 4000 });
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onSearch() {
|
||||
page.value = 1;
|
||||
loadConfigs();
|
||||
}
|
||||
|
||||
function onReset() {
|
||||
configKey.value = '';
|
||||
keyword.value = '';
|
||||
createdAtFrom.value = null;
|
||||
createdAtTo.value = null;
|
||||
updatedAtFrom.value = null;
|
||||
updatedAtTo.value = null;
|
||||
sortField.value = 'updated_at';
|
||||
sortOrder.value = -1;
|
||||
page.value = 1;
|
||||
rows.value = 10;
|
||||
loadConfigs();
|
||||
}
|
||||
|
||||
function onPage(event) {
|
||||
page.value = (event.page ?? 0) + 1;
|
||||
rows.value = event.rows ?? rows.value;
|
||||
loadConfigs();
|
||||
}
|
||||
|
||||
function onSort(event) {
|
||||
sortField.value = event.sortField ?? sortField.value;
|
||||
sortOrder.value = event.sortOrder ?? sortOrder.value;
|
||||
loadConfigs();
|
||||
}
|
||||
|
||||
function openCreateDialog() {
|
||||
editingConfig.value = null;
|
||||
formKey.value = '';
|
||||
formDescription.value = '';
|
||||
formValueText.value = '{\n \n}';
|
||||
editDialogVisible.value = true;
|
||||
}
|
||||
|
||||
function openEditDialog(row) {
|
||||
editingConfig.value = row;
|
||||
formKey.value = row?.config_key || '';
|
||||
formDescription.value = row?.description || '';
|
||||
formValueText.value = toJsonText(row?.value);
|
||||
editDialogVisible.value = true;
|
||||
}
|
||||
|
||||
function parseValueOrToast() {
|
||||
const raw = formValueText.value.trim();
|
||||
if (!raw) {
|
||||
toast.add({ severity: 'warn', summary: '提示', detail: '请输入配置值(JSON)', life: 3000 });
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return JSON.parse(raw);
|
||||
} catch (error) {
|
||||
toast.add({ severity: 'error', summary: '格式错误', detail: '配置值必须是合法 JSON', life: 4000 });
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function submitConfig() {
|
||||
const desc = formDescription.value.trim();
|
||||
if (!desc) {
|
||||
toast.add({ severity: 'warn', summary: '提示', detail: '配置说明不能为空', life: 3000 });
|
||||
return;
|
||||
}
|
||||
|
||||
const value = parseValueOrToast();
|
||||
if (value === null) return;
|
||||
|
||||
editSubmitting.value = true;
|
||||
try {
|
||||
if (editingConfig.value) {
|
||||
await SystemConfigService.updateConfig(editingConfig.value.id, {
|
||||
value,
|
||||
description: desc
|
||||
});
|
||||
toast.add({ severity: 'success', summary: '已更新', detail: '系统配置已更新', life: 3000 });
|
||||
} else {
|
||||
const key = formKey.value.trim();
|
||||
if (!key) {
|
||||
toast.add({ severity: 'warn', summary: '提示', detail: '配置Key不能为空', life: 3000 });
|
||||
return;
|
||||
}
|
||||
await SystemConfigService.createConfig({
|
||||
config_key: key,
|
||||
value,
|
||||
description: desc
|
||||
});
|
||||
toast.add({ severity: 'success', summary: '已创建', detail: '系统配置已创建', life: 3000 });
|
||||
}
|
||||
editDialogVisible.value = false;
|
||||
loadConfigs();
|
||||
} catch (error) {
|
||||
toast.add({ severity: 'error', summary: '提交失败', detail: error?.message || '无法提交系统配置', life: 4000 });
|
||||
} finally {
|
||||
editSubmitting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
loadConfigs();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h4 class="m-0">系统配置</h4>
|
||||
<Button label="新建配置" icon="pi pi-plus" @click="openCreateDialog" />
|
||||
</div>
|
||||
|
||||
<SearchPanel :loading="loading" @search="onSearch" @reset="onReset">
|
||||
<SearchField label="Config Key">
|
||||
<InputText v-model="configKey" placeholder="精确匹配" class="w-full" @keyup.enter="onSearch" />
|
||||
</SearchField>
|
||||
<SearchField label="关键字">
|
||||
<InputText v-model="keyword" placeholder="Key/说明" class="w-full" @keyup.enter="onSearch" />
|
||||
</SearchField>
|
||||
<SearchField label="创建时间 From">
|
||||
<DatePicker v-model="createdAtFrom" showIcon showButtonBar placeholder="开始时间" class="w-full" />
|
||||
</SearchField>
|
||||
<SearchField label="创建时间 To">
|
||||
<DatePicker v-model="createdAtTo" showIcon showButtonBar placeholder="结束时间" class="w-full" />
|
||||
</SearchField>
|
||||
<SearchField label="更新时间 From">
|
||||
<DatePicker v-model="updatedAtFrom" showIcon showButtonBar placeholder="开始时间" class="w-full" />
|
||||
</SearchField>
|
||||
<SearchField label="更新时间 To">
|
||||
<DatePicker v-model="updatedAtTo" showIcon showButtonBar placeholder="结束时间" class="w-full" />
|
||||
</SearchField>
|
||||
</SearchPanel>
|
||||
|
||||
<DataTable :value="configs" :loading="loading" :rows="rows" :totalRecords="totalRecords" :first="(page - 1) * rows" paginator lazy dataKey="id" @page="onPage" @sort="onSort" :sortField="sortField" :sortOrder="sortOrder">
|
||||
<Column field="id" header="ID" style="min-width: 6rem" sortable />
|
||||
<Column field="config_key" header="Key" style="min-width: 14rem" sortable />
|
||||
<Column header="说明" style="min-width: 14rem">
|
||||
<template #body="{ data }">
|
||||
<span class="block max-w-[260px] truncate" :title="data.description">{{ data.description || '-' }}</span>
|
||||
</template>
|
||||
</Column>
|
||||
<Column header="配置值" style="min-width: 18rem">
|
||||
<template #body="{ data }">
|
||||
<span class="block max-w-[360px] truncate" :title="formatJsonPreview(data.value)">
|
||||
{{ formatJsonPreview(data.value) }}
|
||||
</span>
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="updated_at" header="更新时间" style="min-width: 14rem" sortable>
|
||||
<template #body="{ data }">
|
||||
{{ formatDate(data.updated_at) }}
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="created_at" header="创建时间" style="min-width: 14rem" sortable>
|
||||
<template #body="{ data }">
|
||||
{{ formatDate(data.created_at) }}
|
||||
</template>
|
||||
</Column>
|
||||
<Column header="操作" style="min-width: 8rem">
|
||||
<template #body="{ data }">
|
||||
<Button label="编辑" icon="pi pi-pencil" text size="small" class="p-0" @click="openEditDialog(data)" />
|
||||
</template>
|
||||
</Column>
|
||||
</DataTable>
|
||||
</div>
|
||||
|
||||
<Dialog v-model:visible="editDialogVisible" :modal="true" :style="{ width: '640px' }">
|
||||
<template #header>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-medium">{{ dialogTitle }}</span>
|
||||
<span v-if="editingConfig?.id" class="text-muted-color">ID: {{ editingConfig.id }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div>
|
||||
<label class="block font-medium mb-2">Config Key</label>
|
||||
<InputText v-model="formKey" placeholder="唯一Key" class="w-full" :disabled="!!editingConfig" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="block font-medium mb-2">配置说明</label>
|
||||
<InputText v-model="formDescription" placeholder="配置说明" class="w-full" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="block font-medium mb-2">配置值(JSON)</label>
|
||||
<Textarea v-model="formValueText" rows="8" placeholder='例如 {"enabled": true}' class="w-full font-mono" />
|
||||
<div class="text-sm text-muted-color mt-2">需输入合法 JSON;字符串需使用双引号包裹。</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<Button label="取消" icon="pi pi-times" text @click="editDialogVisible = false" :disabled="editSubmitting" />
|
||||
<Button label="确认保存" icon="pi pi-check" @click="submitConfig" :loading="editSubmitting" />
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
Reference in New Issue
Block a user