feat: 添加联系我们配置接口和字典配置缓存
1. 新增DictionaryItem字典配置结构体 2. 添加字典配置缓存及失效逻辑 3. 实现联系我们接口的业务逻辑和测试用例 4. 注册联系我们API路由 5. 集成字典配置到admin内部客户端 6. 处理字典配置更新的kafka事件
This commit is contained in:
@@ -60,6 +60,7 @@ var (
|
||||
tenantByIDCache = newConfigCache[*types.Tenant]()
|
||||
registrationSMSCache = newConfigCache[bool]()
|
||||
thirdPartyConfigCache = newConfigCache[map[string]string]()
|
||||
dictConfigCache = newConfigCache[[]types.DictionaryItem]()
|
||||
appReleaseConfigCache = newConfigCache[*types.AppRelease]()
|
||||
moderationProviderCache = newConfigCache[*types.ModerationProviderConfig]()
|
||||
moderationPolicyCache = newConfigCache[*types.ModerationPolicy]()
|
||||
@@ -174,6 +175,29 @@ func InvalidateThirdPartyConfigCache(group string) {
|
||||
thirdPartyConfigCache.mu.Unlock()
|
||||
}
|
||||
|
||||
func dictConfigCacheKey(dictType string) string {
|
||||
return strings.TrimSpace(dictType)
|
||||
}
|
||||
|
||||
func cloneDictionaryItems(src []types.DictionaryItem) []types.DictionaryItem {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
dst := make([]types.DictionaryItem, len(src))
|
||||
copy(dst, src)
|
||||
return dst
|
||||
}
|
||||
|
||||
// InvalidateDictionaryConfigCache clears cached dictionary config. An empty
|
||||
// type clears all dictionary entries.
|
||||
func InvalidateDictionaryConfigCache(dictType string) {
|
||||
if strings.TrimSpace(dictType) == "" {
|
||||
dictConfigCache.clear()
|
||||
return
|
||||
}
|
||||
dictConfigCache.delete(strings.TrimSpace(dictType))
|
||||
}
|
||||
|
||||
func formatTenantIDForCache(id types.TenantID) string {
|
||||
return strconv.FormatInt(int64(id), 10)
|
||||
}
|
||||
|
||||
@@ -181,8 +181,21 @@ type AdminInternalConfigProvider interface {
|
||||
|
||||
var adminInternalConfigProvider AdminInternalConfigProvider
|
||||
|
||||
// DictionaryConfigProvider is an optional capability of the admin internal
|
||||
// config provider that resolves admin-side dictionary config.
|
||||
type DictionaryConfigProvider interface {
|
||||
DictionaryConfig(dictType string) ([]types.DictionaryItem, error)
|
||||
}
|
||||
|
||||
var adminInternalDictionaryProvider DictionaryConfigProvider
|
||||
|
||||
func SetAdminInternalConfigProvider(provider AdminInternalConfigProvider) {
|
||||
adminInternalConfigProvider = provider
|
||||
if dp, ok := provider.(DictionaryConfigProvider); ok {
|
||||
adminInternalDictionaryProvider = dp
|
||||
} else {
|
||||
adminInternalDictionaryProvider = nil
|
||||
}
|
||||
}
|
||||
|
||||
type ModerationConfigPersistenceInterface interface {
|
||||
@@ -322,6 +335,41 @@ func (thirdPartyConfigMapper) Get(group string, keys ...string) (map[string]stri
|
||||
return result, err
|
||||
}
|
||||
|
||||
// DictionaryConfigPersistenceInterface exposes admin-side dictionary config,
|
||||
// such as the "contact us" entries shown in mobile clients.
|
||||
type DictionaryConfigPersistenceInterface interface {
|
||||
Get(dictType string) ([]types.DictionaryItem, error)
|
||||
}
|
||||
|
||||
type dictionaryConfigMapper struct{}
|
||||
|
||||
// DictionaryConfig reads dictionary config from the Admin service.
|
||||
var DictionaryConfig DictionaryConfigPersistenceInterface = dictionaryConfigMapper{}
|
||||
|
||||
func (dictionaryConfigMapper) Get(dictType string) ([]types.DictionaryItem, error) {
|
||||
dictType = strings.TrimSpace(dictType)
|
||||
if dictType == "" {
|
||||
return nil, types.ErrMalformed
|
||||
}
|
||||
cacheKey := dictConfigCacheKey(dictType)
|
||||
if cached, ok := dictConfigCache.get(cacheKey); ok {
|
||||
return cloneDictionaryItems(cached), nil
|
||||
}
|
||||
if adminInternalDictionaryProvider != nil {
|
||||
result, err := adminInternalDictionaryProvider.DictionaryConfig(dictType)
|
||||
if err == nil {
|
||||
items := cloneDictionaryItems(result)
|
||||
if items == nil {
|
||||
items = []types.DictionaryItem{}
|
||||
}
|
||||
dictConfigCache.set(cacheKey, items)
|
||||
return cloneDictionaryItems(items), nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return nil, types.ErrUnsupported
|
||||
}
|
||||
|
||||
type RealNameConfigPersistenceInterface interface {
|
||||
Policy(tenantID types.TenantID) (*types.RealNamePolicy, error)
|
||||
}
|
||||
|
||||
@@ -71,6 +71,14 @@ type AppRelease struct {
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
// DictionaryItem is a single entry of an admin-side dictionary config, such as
|
||||
// the "contact us" email/phone entries shown in mobile clients.
|
||||
type DictionaryItem struct {
|
||||
Label string
|
||||
Value string
|
||||
Sort int
|
||||
}
|
||||
|
||||
type YouthModeSettings struct {
|
||||
UserID Uid
|
||||
Enabled bool
|
||||
|
||||
Reference in New Issue
Block a user