refactor(*): 平台架构 - 拆分后端领域模块并同步插件协议与模型能力
This commit is contained in:
1 parent
0945115e53
commit
7ce967df0f
644 files changed
+62731
-2320
No files matched your search
@@ -0,0 +1,194 @@
|
||||
package platform
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"infinite-canvas/backend/internal/kernel"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"infinite-canvas/backend/internal/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const featureAvailabilitySettingKey = "feature_availability"
|
||||
|
||||
const (
|
||||
FeatureShortDrama = "shortDrama"
|
||||
FeatureTaskCenter = "taskCenter"
|
||||
FeatureCredits = "credits"
|
||||
FeatureCustomChannels = "customChannels"
|
||||
FeatureFrontendModels = "frontendModels"
|
||||
FeaturePluginCenter = "pluginCenter"
|
||||
FeatureSystemPlugins = "systemPluginsVisibleToUsers"
|
||||
FeatureTimelineTranscription = "timelineTranscription"
|
||||
)
|
||||
|
||||
type FeatureAvailability struct {
|
||||
ShortDramaEnabled bool `json:"shortDramaEnabled"`
|
||||
TaskCenterEnabled bool `json:"taskCenterEnabled"`
|
||||
CreditsEnabled bool `json:"creditsEnabled"`
|
||||
CustomChannelsEnabled bool `json:"customChannelsEnabled"`
|
||||
FrontendModelsEnabled bool `json:"frontendModelsEnabled"`
|
||||
PluginCenterEnabled bool `json:"pluginCenterEnabled"`
|
||||
SystemPluginsVisibleToUsers bool `json:"systemPluginsVisibleToUsers"`
|
||||
TimelineTranscriptionEnabled bool `json:"timelineTranscriptionEnabled"`
|
||||
}
|
||||
|
||||
type PublicFeatureAvailability struct {
|
||||
FeatureAvailability
|
||||
DesktopLocalChannelsEnabled bool `json:"desktopLocalChannelsEnabled"`
|
||||
Configured bool `json:"configured"`
|
||||
UpdatedBy string `json:"updatedBy,omitempty"`
|
||||
UpdatedAt time.Time `json:"updatedAt,omitempty"`
|
||||
}
|
||||
|
||||
func DefaultFeatureAvailability() FeatureAvailability {
|
||||
// 缺少配置代表尚未由运维接管;前台模型需要明确配置后才开放。
|
||||
return FeatureAvailability{
|
||||
ShortDramaEnabled: true,
|
||||
TaskCenterEnabled: true,
|
||||
CreditsEnabled: true,
|
||||
CustomChannelsEnabled: true,
|
||||
FrontendModelsEnabled: false,
|
||||
PluginCenterEnabled: true,
|
||||
SystemPluginsVisibleToUsers: true,
|
||||
TimelineTranscriptionEnabled: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) FeatureAvailability() (*PublicFeatureAvailability, error) {
|
||||
setting, value, err := s.readFeatureAvailability()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.withRuntimeCapabilities(publicFeatureAvailability(setting, value)), nil
|
||||
}
|
||||
|
||||
func (s *Service) AdminFeatureAvailability(actor *model.User) (*PublicFeatureAvailability, error) {
|
||||
if err := s.requireAdmin(actor); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.FeatureAvailability()
|
||||
}
|
||||
|
||||
func (s *Service) UpdateFeatureAvailability(actor *model.User, value FeatureAvailability) (*PublicFeatureAvailability, error) {
|
||||
if err := s.requireAdmin(actor); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
current, before, err := s.readFeatureAvailability()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encoded, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
setting := model.SystemSetting{Key: featureAvailabilitySettingKey, ValueJSON: string(encoded), UpdatedBy: actor.ID}
|
||||
if current != nil {
|
||||
setting.CreatedAt = current.CreatedAt
|
||||
}
|
||||
if err := s.repo.SaveSystemSetting(&setting); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.appendAdminAudit(actor, "feature_availability.update", "system_setting", featureAvailabilitySettingKey, "更新功能开放配置", map[string]any{"before": before, "after": value}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.withRuntimeCapabilities(publicFeatureAvailability(&setting, value)), nil
|
||||
}
|
||||
|
||||
func (s *Service) FeatureEnabled(feature string) (bool, error) {
|
||||
_, value, err := s.readFeatureAvailability()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
switch feature {
|
||||
case FeatureShortDrama:
|
||||
return value.ShortDramaEnabled, nil
|
||||
case FeatureTaskCenter:
|
||||
return value.TaskCenterEnabled, nil
|
||||
case FeatureCredits:
|
||||
return value.CreditsEnabled, nil
|
||||
case FeatureCustomChannels:
|
||||
return value.CustomChannelsEnabled, nil
|
||||
case FeatureFrontendModels:
|
||||
return value.FrontendModelsEnabled, nil
|
||||
case FeaturePluginCenter:
|
||||
return value.PluginCenterEnabled, nil
|
||||
case FeatureSystemPlugins:
|
||||
return value.SystemPluginsVisibleToUsers, nil
|
||||
case FeatureTimelineTranscription:
|
||||
return value.TimelineTranscriptionEnabled, nil
|
||||
default:
|
||||
return false, errors.New("未知功能开放配置")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) RequireFeature(feature string) error {
|
||||
enabled, err := s.FeatureEnabled(feature)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if enabled {
|
||||
return nil
|
||||
}
|
||||
switch feature {
|
||||
case FeatureShortDrama:
|
||||
return kernel.Forbidden("短剧创作暂未开放")
|
||||
case FeatureTaskCenter:
|
||||
return kernel.Forbidden("任务中心暂未开放")
|
||||
case FeatureCredits:
|
||||
return kernel.Forbidden("积分功能暂未开放")
|
||||
case FeatureCustomChannels:
|
||||
return kernel.Forbidden("自定义渠道暂未开放")
|
||||
case FeatureFrontendModels:
|
||||
return kernel.Forbidden("前台模型目录暂未开放")
|
||||
case FeaturePluginCenter:
|
||||
return kernel.Forbidden("插件中心暂未开放")
|
||||
case FeatureSystemPlugins:
|
||||
return kernel.Forbidden("系统插件暂未向普通用户展示")
|
||||
case FeatureTimelineTranscription:
|
||||
return kernel.Forbidden("字幕转写暂未开放")
|
||||
default:
|
||||
return kernel.Forbidden("该功能暂未开放")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) readFeatureAvailability() (*model.SystemSetting, FeatureAvailability, error) {
|
||||
setting, err := s.repo.SystemSetting(featureAvailabilitySettingKey)
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, DefaultFeatureAvailability(), nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, FeatureAvailability{}, err
|
||||
}
|
||||
// 以全开放默认值为基底,避免已有三字段配置在升级后因缺少新字段而意外关闭功能。
|
||||
value := DefaultFeatureAvailability()
|
||||
if strings.TrimSpace(setting.ValueJSON) == "" || json.Unmarshal([]byte(setting.ValueJSON), &value) != nil {
|
||||
return nil, FeatureAvailability{}, errors.New("功能开放配置格式无效")
|
||||
}
|
||||
return setting, value, nil
|
||||
}
|
||||
|
||||
func (s *Service) withRuntimeCapabilities(result *PublicFeatureAvailability) *PublicFeatureAvailability {
|
||||
if result != nil {
|
||||
result.DesktopLocalChannelsEnabled = s.desktopLocalChannelsEnabled()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func publicFeatureAvailability(setting *model.SystemSetting, value FeatureAvailability) *PublicFeatureAvailability {
|
||||
result := &PublicFeatureAvailability{FeatureAvailability: value, Configured: setting != nil}
|
||||
if setting != nil {
|
||||
result.UpdatedBy = setting.UpdatedBy
|
||||
result.UpdatedAt = setting.UpdatedAt
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// PublicFeatureAvailability projects the persisted feature flags without
|
||||
// exposing the platform service internals to compatibility callers.
|
||||
func ProjectFeatureAvailability(setting *model.SystemSetting, value FeatureAvailability) *PublicFeatureAvailability {
|
||||
return publicFeatureAvailability(setting, value)
|
||||
}
|
||||
Reference in new issue
Block a user