feat: 添加独立其他设置菜单及有效端参数保存

This commit is contained in:
Yvan
2026-11-04 21:18:31 +08:00
parent c2e8b729be
commit ca9ce99b66
27 changed files with 805 additions and 9 deletions
+1 -1
View File
@@ -11,4 +11,4 @@ import (
)
// ProviderSet is biz providers.
var ProviderSet = wire.NewSet(system.ProviderSet, payment.ProviderSet, integration.ProviderSet, task.ProviderSet, business.NewUserPreferenceUsecase, business.NewAccountUsecase, business.NewBindingUsecase, business.NewPasswordResetUsecase, business.NewBusinessPromoteUsecase, business.NewAlertRuleUsecase, business.NewAlertRecordUsecase, business.NewActivationUsecase, business.NewChannelUsecase, business.NewBusinessPortraitUsecase, business.NewChannelPortraitUsecase, business.NewLedgerUsecase, business.NewAnalyticsUsecase, business.NewServerAnalysisUsecase, business.NewWorkbenchUsecase, business.NewRankingUsecase, business.NewReportBuildUsecase, business.NewAlertTriggerUsecase)
var ProviderSet = wire.NewSet(system.ProviderSet, payment.ProviderSet, integration.ProviderSet, task.ProviderSet, business.NewUserPreferenceUsecase, business.NewOtherSettingsUsecase, business.NewAccountUsecase, business.NewBindingUsecase, business.NewPasswordResetUsecase, business.NewBusinessPromoteUsecase, business.NewAlertRuleUsecase, business.NewAlertRecordUsecase, business.NewActivationUsecase, business.NewChannelUsecase, business.NewBusinessPortraitUsecase, business.NewChannelPortraitUsecase, business.NewLedgerUsecase, business.NewAnalyticsUsecase, business.NewServerAnalysisUsecase, business.NewWorkbenchUsecase, business.NewRankingUsecase, business.NewReportBuildUsecase, business.NewAlertTriggerUsecase)
+78
View File
@@ -0,0 +1,78 @@
package business
import (
"context"
"time"
"github.com/go-kratos/kratos/v3/errors"
"kra/internal/biz/system"
)
var (
ErrOtherSettingsScope = errors.Forbidden("SW_OTHER_SETTINGS_SCOPE", "仅超级管理员可以查看和修改其他设置")
ErrOtherSettingsInput = errors.BadRequest("SW_OTHER_SETTINGS_INPUT", "达标金额须为不小于0的整数,判定窗口须为不小于1的整数,且不能超过安全整数范围")
ErrOtherSettingsStored = errors.BadRequest("SW_OTHER_SETTINGS_STORED", "已保存的设置格式不正确,请恢复默认后重新保存")
)
const MaxSettingsInteger int64 = 9007199254740991
type OtherSettings struct {
Amount, Days int64
UpdatedAt time.Time
}
func DefaultOtherSettings() OtherSettings { return OtherSettings{Amount: 3000, Days: 30} }
func ValidateOtherSettings(value OtherSettings) error {
if value.Amount < 0 || value.Amount > MaxSettingsInteger || value.Days < 1 || value.Days > MaxSettingsInteger {
return ErrOtherSettingsInput
}
return nil
}
type OtherSettingsRepo interface {
Find(context.Context) (*OtherSettings, error)
Save(context.Context, OtherSettings) (*OtherSettings, error)
}
type OtherSettingsUsecase struct{ repo OtherSettingsRepo }
func NewOtherSettingsUsecase(repo OtherSettingsRepo) *OtherSettingsUsecase {
return &OtherSettingsUsecase{repo: repo}
}
func requireSettingsAdmin(ctx context.Context) error {
actor, ok := system.ActorFromContext(ctx)
if !ok || actor.UserID == 0 {
return ErrUnauthenticated
}
if actor.AuthorityID != system.SuperAdminAuthorityID {
return ErrOtherSettingsScope
}
return nil
}
func (uc *OtherSettingsUsecase) Get(ctx context.Context) (*OtherSettings, error) {
if err := requireSettingsAdmin(ctx); err != nil {
return nil, err
}
value, err := uc.repo.Find(ctx)
if err != nil {
return nil, err
}
if value == nil {
defaults := DefaultOtherSettings()
return &defaults, nil
}
return value, nil
}
func (uc *OtherSettingsUsecase) Save(ctx context.Context, value OtherSettings) (*OtherSettings, error) {
if err := requireSettingsAdmin(ctx); err != nil {
return nil, err
}
if err := ValidateOtherSettings(value); err != nil {
return nil, err
}
return uc.repo.Save(ctx, value)
}
@@ -0,0 +1,57 @@
package business
import (
"context"
"testing"
)
type otherSettingsFake struct {
value *OtherSettings
calls int
}
func (r *otherSettingsFake) Find(context.Context) (*OtherSettings, error) {
r.calls++
return r.value, nil
}
func (r *otherSettingsFake) Save(_ context.Context, value OtherSettings) (*OtherSettings, error) {
r.calls++
r.value = &value
return r.value, nil
}
func TestOtherSettingsOnlySuperAdminAndDefaults(t *testing.T) {
r := &otherSettingsFake{}
uc := NewOtherSettingsUsecase(r)
for _, ctx := range []context.Context{context.Background(), accountContext(7, 901), accountContext(8, 902), accountContext(9, 903)} {
if _, err := uc.Get(ctx); err == nil {
t.Fatal("非超管不能读取设置")
}
if _, err := uc.Save(ctx, DefaultOtherSettings()); err == nil {
t.Fatal("非超管不能保存设置")
}
}
if r.calls != 0 {
t.Fatal("无权限请求不得访问存储")
}
value, err := uc.Get(accountContext(1, 888))
if err != nil || *value != DefaultOtherSettings() {
t.Fatalf("首次读取应返回默认值: %+v %v", value, err)
}
if _, err := uc.Save(accountContext(1, 888), OtherSettings{Amount: 0, Days: 1}); err != nil {
t.Fatal(err)
}
value, err = uc.Get(accountContext(2, 888))
if err != nil || value.Amount != 0 || value.Days != 1 {
t.Fatal("参数应为全站共享,不能按用户各存一份")
}
}
func TestOtherSettingsRejectInvalidRange(t *testing.T) {
uc := NewOtherSettingsUsecase(&otherSettingsFake{})
for _, value := range []OtherSettings{{Amount: -1, Days: 30}, {Amount: 3000, Days: 0}, {Amount: MaxSettingsInteger + 1, Days: 30}, {Amount: 3000, Days: MaxSettingsInteger + 1}} {
if _, err := uc.Save(accountContext(1, 888), value); err != ErrOtherSettingsInput {
t.Fatalf("不应保存非法值: %+v %v", value, err)
}
}
}