优化首页公告与轮播图
This commit is contained in:
@@ -48,6 +48,14 @@ func (h *Handler) Upload(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (h *Handler) Object(c *gin.Context) {
|
||||
h.writeObject(c, false)
|
||||
}
|
||||
|
||||
func (h *Handler) PublicObject(c *gin.Context) {
|
||||
h.writeObject(c, true)
|
||||
}
|
||||
|
||||
func (h *Handler) writeObject(c *gin.Context, publicOnly bool) {
|
||||
if h.storage == nil {
|
||||
response.ServiceUnavailable(c, "文件存储未连接")
|
||||
return
|
||||
@@ -57,6 +65,10 @@ func (h *Handler) Object(c *gin.Context) {
|
||||
response.BadRequest(c, "文件 key 不正确")
|
||||
return
|
||||
}
|
||||
if publicOnly && !strings.HasPrefix(key, "home-banner/") {
|
||||
response.Error(c, http.StatusNotFound, "not_found", "文件不存在或暂不可访问")
|
||||
return
|
||||
}
|
||||
object, err := h.storage.Get(c.Request.Context(), key)
|
||||
if err != nil {
|
||||
response.Error(c, http.StatusNotFound, "not_found", "文件不存在或暂不可访问")
|
||||
|
||||
@@ -48,9 +48,13 @@ func (s *Service) Upload(req uploadRequest) (*UploadDTO, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
url := "/api/files/object?key=" + key
|
||||
if scene == "home-banner" {
|
||||
url = "/api/public/files/object?key=" + key
|
||||
}
|
||||
return &UploadDTO{
|
||||
ObjectKey: key,
|
||||
URL: "/api/files/object?key=" + key,
|
||||
URL: url,
|
||||
Filename: req.Header.Filename,
|
||||
ContentType: contentType,
|
||||
Size: req.Header.Size,
|
||||
@@ -60,7 +64,7 @@ func (s *Service) Upload(req uploadRequest) (*UploadDTO, error) {
|
||||
func normalizeScene(scene string) string {
|
||||
scene = strings.TrimSpace(strings.ToLower(scene))
|
||||
switch scene {
|
||||
case "listing", "handoff", "dispute", "realname", "avatar":
|
||||
case "listing", "handoff", "dispute", "realname", "avatar", "home-banner":
|
||||
return scene
|
||||
default:
|
||||
return "misc"
|
||||
|
||||
@@ -17,6 +17,25 @@ type UpdateRequest struct {
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type HomeAnnouncementsDTO struct {
|
||||
Items []string `json:"items"`
|
||||
}
|
||||
|
||||
type HomeConfigDTO struct {
|
||||
Announcements []string `json:"announcements"`
|
||||
Banners []HomeBannerItem `json:"banners"`
|
||||
PublishOptions PublishOptionsDTO `json:"publish_options"`
|
||||
}
|
||||
|
||||
type HomeBannerItem struct {
|
||||
Eyebrow string `json:"eyebrow"`
|
||||
Title string `json:"title"`
|
||||
Badge string `json:"badge"`
|
||||
Pill string `json:"pill"`
|
||||
Tone string `json:"tone"`
|
||||
ImageURL string `json:"image_url,omitempty"`
|
||||
}
|
||||
|
||||
type PublishOptionsDTO struct {
|
||||
ServerOptions []string `json:"server_options"`
|
||||
FaceOptions []string `json:"face_options"`
|
||||
|
||||
@@ -36,6 +36,24 @@ func (h *Handler) PublishOptions(c *gin.Context) {
|
||||
response.OK(c, options)
|
||||
}
|
||||
|
||||
func (h *Handler) HomeAnnouncements(c *gin.Context) {
|
||||
announcements, err := h.service.HomeAnnouncements()
|
||||
if err != nil {
|
||||
writeConfigError(c, err)
|
||||
return
|
||||
}
|
||||
response.OK(c, announcements)
|
||||
}
|
||||
|
||||
func (h *Handler) HomeConfig(c *gin.Context) {
|
||||
config, err := h.service.HomeConfig()
|
||||
if err != nil {
|
||||
writeConfigError(c, err)
|
||||
return
|
||||
}
|
||||
response.OK(c, config)
|
||||
}
|
||||
|
||||
func (h *Handler) Update(c *gin.Context) {
|
||||
adminID, ok := currentAdminID(c)
|
||||
if !ok {
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package systemconfig
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
const homeAnnouncementsConfigKey = "mobile.home_announcements"
|
||||
const homeBannersConfigKey = "mobile.home_banners"
|
||||
|
||||
func defaultHomeAnnouncements() []string {
|
||||
return []string{
|
||||
"平台担保交易,拒绝私下转账/共享验证码,交接全程留痕。",
|
||||
"优先推荐同地区账号,减少异地登录保护触发。",
|
||||
"下单前请核对哈夫币、保险、体力负重和截图信息。",
|
||||
}
|
||||
}
|
||||
|
||||
func defaultHomeAnnouncementsConfigValue() string {
|
||||
raw, err := json.Marshal(defaultHomeAnnouncements())
|
||||
if err != nil {
|
||||
return "[]"
|
||||
}
|
||||
return string(raw)
|
||||
}
|
||||
|
||||
func defaultHomeBanners() []HomeBannerItem {
|
||||
return []HomeBannerItem{
|
||||
{
|
||||
Eyebrow: "三角洲行动账号专区",
|
||||
Title: "高哈夫币 · 安全交接 · 随租随玩",
|
||||
Badge: "HOT",
|
||||
Pill: "新人领券最高减 20 元",
|
||||
Tone: "blue",
|
||||
ImageURL: "",
|
||||
},
|
||||
{
|
||||
Eyebrow: "平台担保交易",
|
||||
Title: "交接全程留痕,拒绝私下转账",
|
||||
Badge: "SAFE",
|
||||
Pill: "租号前先验实名与资料",
|
||||
Tone: "green",
|
||||
ImageURL: "",
|
||||
},
|
||||
{
|
||||
Eyebrow: "高效筛选",
|
||||
Title: "按区服、段位、哈夫币快速找号",
|
||||
Badge: "FAST",
|
||||
Pill: "支持扫码号与账密号",
|
||||
Tone: "orange",
|
||||
ImageURL: "",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func defaultHomeBannersConfigValue() string {
|
||||
raw, err := json.Marshal(defaultHomeBanners())
|
||||
if err != nil {
|
||||
return "[]"
|
||||
}
|
||||
return string(raw)
|
||||
}
|
||||
@@ -38,6 +38,8 @@ var defaultConfigs = []defaultConfig{
|
||||
{Key: "settlement.platform_fee_rate", Value: "0.00", Description: "平台抽成比例"},
|
||||
{Key: "settlement.owner_cycle_days", Value: "0", Description: "号主结算周期天数"},
|
||||
{Key: "withdraw.min_amount", Value: "100", Description: "提现最低金额预留"},
|
||||
{Key: homeAnnouncementsConfigKey, Value: defaultHomeAnnouncementsConfigValue(), Description: "移动端首页公告 JSON 数组"},
|
||||
{Key: homeBannersConfigKey, Value: defaultHomeBannersConfigValue(), Description: "移动端首页轮播图 JSON 数组"},
|
||||
{Key: publishOptionsConfigKey, Value: defaultPublishOptionsConfigValue(), Description: "发布页选项配置 JSON"},
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package systemconfig
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -29,16 +30,82 @@ func (s *Service) PublishOptions() (*PublishOptionsDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
value, err := s.repo.FindValue(publishOptionsConfigKey)
|
||||
options, err := s.publishOptions()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &options, nil
|
||||
}
|
||||
|
||||
func (s *Service) publishOptions() (PublishOptionsDTO, error) {
|
||||
value, err := s.repo.FindValue(publishOptionsConfigKey)
|
||||
if err != nil {
|
||||
return PublishOptionsDTO{}, err
|
||||
}
|
||||
options := DefaultPublishOptions()
|
||||
if err := json.Unmarshal([]byte(value), &options); err != nil {
|
||||
options = DefaultPublishOptions()
|
||||
}
|
||||
normalizePublishOptions(&options)
|
||||
return &options, nil
|
||||
return options, nil
|
||||
}
|
||||
|
||||
func (s *Service) HomeAnnouncements() (*HomeAnnouncementsDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
items, err := s.homeAnnouncements()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &HomeAnnouncementsDTO{Items: items}, nil
|
||||
}
|
||||
|
||||
func (s *Service) HomeConfig() (*HomeConfigDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
announcements, err := s.homeAnnouncements()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
banners, err := s.homeBanners()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
publishOptions, err := s.publishOptions()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &HomeConfigDTO{
|
||||
Announcements: announcements,
|
||||
Banners: banners,
|
||||
PublishOptions: publishOptions,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Service) homeAnnouncements() ([]string, error) {
|
||||
value, err := s.repo.FindValue(homeAnnouncementsConfigKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items := defaultHomeAnnouncements()
|
||||
if err := json.Unmarshal([]byte(value), &items); err != nil {
|
||||
items = defaultHomeAnnouncements()
|
||||
}
|
||||
return normalizeAnnouncements(items), nil
|
||||
}
|
||||
|
||||
func (s *Service) homeBanners() ([]HomeBannerItem, error) {
|
||||
value, err := s.repo.FindValue(homeBannersConfigKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items := defaultHomeBanners()
|
||||
if err := json.Unmarshal([]byte(value), &items); err != nil {
|
||||
items = defaultHomeBanners()
|
||||
}
|
||||
return normalizeHomeBanners(items), nil
|
||||
}
|
||||
|
||||
func (s *Service) Update(actorID uint64, key string, req UpdateRequest, meta AuditMeta) (*ConfigDTO, error) {
|
||||
@@ -51,6 +118,43 @@ func (s *Service) Update(actorID uint64, key string, req UpdateRequest, meta Aud
|
||||
return s.repo.Update(actorID, key, req, meta)
|
||||
}
|
||||
|
||||
func normalizeAnnouncements(items []string) []string {
|
||||
normalized := make([]string, 0, len(items))
|
||||
for _, item := range items {
|
||||
item = strings.TrimSpace(item)
|
||||
if item != "" {
|
||||
normalized = append(normalized, item)
|
||||
}
|
||||
}
|
||||
if len(normalized) == 0 {
|
||||
return defaultHomeAnnouncements()
|
||||
}
|
||||
return normalized
|
||||
}
|
||||
|
||||
func normalizeHomeBanners(items []HomeBannerItem) []HomeBannerItem {
|
||||
normalized := make([]HomeBannerItem, 0, len(items))
|
||||
for _, item := range items {
|
||||
item.Eyebrow = strings.TrimSpace(item.Eyebrow)
|
||||
item.Title = strings.TrimSpace(item.Title)
|
||||
item.Badge = strings.TrimSpace(item.Badge)
|
||||
item.Pill = strings.TrimSpace(item.Pill)
|
||||
item.Tone = strings.TrimSpace(item.Tone)
|
||||
item.ImageURL = strings.TrimSpace(item.ImageURL)
|
||||
if item.Title == "" && item.ImageURL == "" {
|
||||
continue
|
||||
}
|
||||
if item.Tone != "green" && item.Tone != "orange" {
|
||||
item.Tone = "blue"
|
||||
}
|
||||
normalized = append(normalized, item)
|
||||
}
|
||||
if len(normalized) == 0 {
|
||||
return defaultHomeBanners()
|
||||
}
|
||||
return normalized
|
||||
}
|
||||
|
||||
func normalizePublishOptions(options *PublishOptionsDTO) {
|
||||
defaults := DefaultPublishOptions()
|
||||
if len(options.ServerOptions) == 0 {
|
||||
|
||||
@@ -127,6 +127,9 @@ func New(cfg config.Config, deps Dependencies, logger *zap.Logger) *gin.Engine {
|
||||
{
|
||||
api.GET("/health", health.Check)
|
||||
api.GET("/listing-publish-options", systemConfigHandler.PublishOptions)
|
||||
api.GET("/home-announcements", systemConfigHandler.HomeAnnouncements)
|
||||
api.GET("/mobile-home-config", systemConfigHandler.HomeConfig)
|
||||
api.GET("/public/files/object", fileHandler.PublicObject)
|
||||
|
||||
authRoutes := api.Group("/auth")
|
||||
{
|
||||
@@ -208,6 +211,7 @@ func New(cfg config.Config, deps Dependencies, logger *zap.Logger) *gin.Engine {
|
||||
{
|
||||
adminRoutes.GET("/me", adminAuthHandler.Me)
|
||||
adminRoutes.POST("/auth/logout", adminAuthHandler.Logout)
|
||||
adminRoutes.POST("/files/upload", fileHandler.Upload)
|
||||
adminRoutes.GET("/dashboard", adminDashboardHandler.Summary)
|
||||
adminRoutes.GET("/files/object", fileHandler.Object)
|
||||
adminRoutes.GET("/users", adminUserHandler.List)
|
||||
|
||||
Reference in New Issue
Block a user