更新小程序开发文档,新增2026-03-03的最佳实践记录,优化个人中心类页面的卡片区边距规范,确保一致性与可用性。调整相关页面以反映最新设计稿,提升用户体验与功能一致性。

This commit is contained in:
Alex-larget
2026-03-04 19:06:06 +08:00
parent 7064f82126
commit 5a5f0087d2
66 changed files with 2555 additions and 1059 deletions

View File

@@ -16,9 +16,8 @@ import (
)
// GetPublicDBConfig GET /api/miniprogram/config 公开接口,供小程序获取完整配置(与 next-project 对齐)
// 从 system_config 读取 free_chapters、mp_config、feature_config、chapter_config合并后返回
// 从 system_config 读取 chapter_config、feature_config、mp_config合并后返回(免费以章节 is_free/price 为准)
func GetPublicDBConfig(c *gin.Context) {
defaultFree := []string{"preface", "epilogue", "1.1", "appendix-1", "appendix-2", "appendix-3"}
defaultPrices := gin.H{"section": float64(1), "fullbook": 9.9}
defaultFeatures := gin.H{"matchEnabled": true, "referralEnabled": true, "searchEnabled": true, "aboutEnabled": true}
apiDomain := "https://soulapi.quwanzhi.com"
@@ -36,16 +35,15 @@ func GetPublicDBConfig(c *gin.Context) {
}
out := gin.H{
"success": true,
"freeChapters": defaultFree,
"prices": defaultPrices,
"features": defaultFeatures,
"mpConfig": defaultMp,
"configs": gin.H{}, // 兼容 miniprogram 备用格式 res.configs.feature_config
"success": true,
"prices": defaultPrices,
"features": defaultFeatures,
"mpConfig": defaultMp,
"configs": gin.H{},
}
db := database.DB()
keys := []string{"chapter_config", "free_chapters", "feature_config", "mp_config"}
keys := []string{"chapter_config", "feature_config", "mp_config"}
for _, k := range keys {
var row model.SystemConfig
if err := db.Where("config_key = ?", k).First(&row).Error; err != nil {
@@ -58,17 +56,6 @@ func GetPublicDBConfig(c *gin.Context) {
switch k {
case "chapter_config":
if m, ok := val.(map[string]interface{}); ok {
if v, ok := m["freeChapters"].([]interface{}); ok && len(v) > 0 {
arr := make([]string, 0, len(v))
for _, x := range v {
if s, ok := x.(string); ok {
arr = append(arr, s)
}
}
if len(arr) > 0 {
out["freeChapters"] = arr
}
}
if v, ok := m["prices"].(map[string]interface{}); ok {
out["prices"] = v
}
@@ -77,19 +64,6 @@ func GetPublicDBConfig(c *gin.Context) {
}
out["configs"].(gin.H)["chapter_config"] = m
}
case "free_chapters":
if arr, ok := val.([]interface{}); ok && len(arr) > 0 {
ss := make([]string, 0, len(arr))
for _, x := range arr {
if s, ok := x.(string); ok {
ss = append(ss, s)
}
}
if len(ss) > 0 {
out["freeChapters"] = ss
}
out["configs"].(gin.H)["free_chapters"] = arr
}
case "feature_config":
if m, ok := val.(map[string]interface{}); ok {
// 合并到 features不整体覆盖以保留 chapter_config 里的
@@ -158,7 +132,7 @@ func DBConfigGet(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"success": true, "data": data})
}
// AdminSettingsGet GET /api/admin/settings 系统设置页专用:仅返回免费章节、功能开关、站点/作者与价格、小程序配置
// AdminSettingsGet GET /api/admin/settings 系统设置页专用:仅返回功能开关、站点/作者与价格、小程序配置
func AdminSettingsGet(c *gin.Context) {
db := database.DB()
apiDomain := "https://soulapi.quwanzhi.com"
@@ -174,12 +148,11 @@ func AdminSettingsGet(c *gin.Context) {
}
out := gin.H{
"success": true,
"freeChapters": []string{"preface", "epilogue", "1.1", "appendix-1", "appendix-2", "appendix-3"},
"featureConfig": gin.H{"matchEnabled": true, "referralEnabled": true, "searchEnabled": true, "aboutEnabled": true},
"siteSettings": gin.H{"sectionPrice": float64(1), "baseBookPrice": 9.9, "distributorShare": float64(90), "authorInfo": gin.H{}},
"mpConfig": defaultMp,
}
keys := []string{"free_chapters", "feature_config", "site_settings", "mp_config"}
keys := []string{"feature_config", "site_settings", "mp_config"}
for _, k := range keys {
var row model.SystemConfig
if err := db.Where("config_key = ?", k).First(&row).Error; err != nil {
@@ -190,18 +163,6 @@ func AdminSettingsGet(c *gin.Context) {
continue
}
switch k {
case "free_chapters":
if arr, ok := val.([]interface{}); ok && len(arr) > 0 {
ss := make([]string, 0, len(arr))
for _, x := range arr {
if s, ok := x.(string); ok {
ss = append(ss, s)
}
}
if len(ss) > 0 {
out["freeChapters"] = ss
}
}
case "feature_config":
if m, ok := val.(map[string]interface{}); ok && len(m) > 0 {
out["featureConfig"] = m
@@ -226,10 +187,9 @@ func AdminSettingsGet(c *gin.Context) {
c.JSON(http.StatusOK, out)
}
// AdminSettingsPost POST /api/admin/settings 系统设置页专用:一次性保存免费章节、功能开关、站点/作者与价格、小程序配置
// AdminSettingsPost POST /api/admin/settings 系统设置页专用:一次性保存功能开关、站点/作者与价格、小程序配置
func AdminSettingsPost(c *gin.Context) {
var body struct {
FreeChapters []string `json:"freeChapters"`
FeatureConfig map[string]interface{} `json:"featureConfig"`
SiteSettings map[string]interface{} `json:"siteSettings"`
MpConfig map[string]interface{} `json:"mpConfig"`
@@ -256,12 +216,6 @@ func AdminSettingsPost(c *gin.Context) {
}
return db.Save(&row).Error
}
if body.FreeChapters != nil {
if err := saveKey("free_chapters", "免费章节ID列表", body.FreeChapters); err != nil {
c.JSON(http.StatusOK, gin.H{"success": false, "error": "保存免费章节失败: " + err.Error()})
return
}
}
if body.FeatureConfig != nil {
if err := saveKey("feature_config", "功能开关配置", body.FeatureConfig); err != nil {
c.JSON(http.StatusOK, gin.H{"success": false, "error": "保存功能开关失败: " + err.Error()})
@@ -366,6 +320,140 @@ func AdminReferralSettingsPost(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"success": true, "message": "推广设置已保存"})
}
func authorConfigToResponse(row *model.AuthorConfig) gin.H {
defaultStats := []gin.H{{"label": "商业案例", "value": "62"}, {"label": "连续直播", "value": "365天"}, {"label": "派对分享", "value": "1000+"}}
defaultHighlights := []string{"5年私域运营经验", "帮助100+品牌从0到1增长", "连续创业者,擅长商业模式设计"}
var stats []gin.H
if row.Stats != "" {
_ = json.Unmarshal([]byte(row.Stats), &stats)
}
if len(stats) == 0 {
stats = defaultStats
}
var highlights []string
if row.Highlights != "" {
_ = json.Unmarshal([]byte(row.Highlights), &highlights)
}
if len(highlights) == 0 {
highlights = defaultHighlights
}
return gin.H{
"name": row.Name,
"avatar": row.Avatar,
"avatarImg": row.AvatarImg,
"title": row.Title,
"bio": row.Bio,
"stats": stats,
"highlights": highlights,
}
}
// AdminAuthorSettingsGet GET /api/admin/author-settings 作者详情配置(管理端专用)
func AdminAuthorSettingsGet(c *gin.Context) {
defaultAuthor := gin.H{
"name": "卡若",
"avatar": "K",
"avatarImg": "",
"title": "Soul派对房主理人 · 私域运营专家",
"bio": "每天早上6点到9点在Soul派对房分享真实的创业故事。专注私域运营与项目变现用云阿米巴模式帮助创业者构建可持续的商业体系。",
"stats": []gin.H{{"label": "商业案例", "value": "62"}, {"label": "连续直播", "value": "365天"}, {"label": "派对分享", "value": "1000+"}},
"highlights": []string{"5年私域运营经验", "帮助100+品牌从0到1增长", "连续创业者,擅长商业模式设计"},
}
db := database.DB()
var row model.AuthorConfig
if err := db.First(&row).Error; err != nil {
c.JSON(http.StatusOK, gin.H{"success": true, "data": defaultAuthor})
return
}
c.JSON(http.StatusOK, gin.H{"success": true, "data": authorConfigToResponse(&row)})
}
// AdminAuthorSettingsPost POST /api/admin/author-settings 保存作者详情配置
func AdminAuthorSettingsPost(c *gin.Context) {
var body map[string]interface{}
if err := c.ShouldBindJSON(&body); err != nil {
c.JSON(http.StatusOK, gin.H{"success": false, "error": "请求体无效"})
return
}
str := func(k string) string {
if v, ok := body[k]; ok && v != nil {
if s, ok := v.(string); ok {
return s
}
return fmt.Sprintf("%v", v)
}
return ""
}
name := str("name")
if name == "" {
name = "卡若"
}
avatar := str("avatar")
if avatar == "" {
avatar = "K"
}
statsVal := body["stats"]
if statsVal == nil {
statsVal = []gin.H{{"label": "商业案例", "value": "62"}, {"label": "连续直播", "value": "365天"}, {"label": "派对分享", "value": "1000+"}}
}
highlightsVal := body["highlights"]
if highlightsVal == nil {
highlightsVal = []string{}
}
statsBytes, _ := json.Marshal(statsVal)
highlightsBytes, _ := json.Marshal(highlightsVal)
db := database.DB()
var row model.AuthorConfig
err := db.First(&row).Error
if err != nil {
row = model.AuthorConfig{
Name: name,
Avatar: avatar,
AvatarImg: str("avatarImg"),
Title: str("title"),
Bio: str("bio"),
Stats: string(statsBytes),
Highlights: string(highlightsBytes),
}
err = db.Create(&row).Error
} else {
row.Name = name
row.Avatar = avatar
row.AvatarImg = str("avatarImg")
row.Title = str("title")
row.Bio = str("bio")
row.Stats = string(statsBytes)
row.Highlights = string(highlightsBytes)
err = db.Save(&row).Error
}
if err != nil {
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"success": true, "message": "作者设置已保存"})
}
// MiniprogramAboutAuthor GET /api/miniprogram/about/author 小程序-关于作者页拉取作者配置(公开,无需鉴权)
func MiniprogramAboutAuthor(c *gin.Context) {
defaultAuthor := gin.H{
"name": "卡若",
"avatar": "K",
"avatarImg": "",
"title": "Soul派对房主理人 · 私域运营专家",
"bio": "每天早上6点到9点在Soul派对房分享真实的创业故事。",
"stats": []gin.H{{"label": "商业案例", "value": "62"}, {"label": "连续直播", "value": "365天"}, {"label": "派对分享", "value": "1000+"}},
"highlights": []string{"5年私域运营经验", "帮助100+品牌从0到1增长", "连续创业者,擅长商业模式设计"},
}
db := database.DB()
var row model.AuthorConfig
if err := db.First(&row).Error; err != nil {
c.JSON(http.StatusOK, gin.H{"success": true, "data": defaultAuthor})
return
}
c.JSON(http.StatusOK, gin.H{"success": true, "data": authorConfigToResponse(&row)})
}
// DBConfigPost POST /api/db/config
func DBConfigPost(c *gin.Context) {
var body struct {