chore: 清理敏感与开发文档,仅同步代码
- 永久忽略并从仓库移除 开发文档/ - 移除并忽略 .env 与小程序私有配置 - 同步小程序/管理端/API与脚本改动 Made-with: Cursor
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@@ -8,6 +9,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"soul-api/internal/cache"
|
||||
"soul-api/internal/config"
|
||||
"soul-api/internal/database"
|
||||
"soul-api/internal/model"
|
||||
@@ -16,10 +18,15 @@ import (
|
||||
)
|
||||
|
||||
// GetPublicDBConfig GET /api/miniprogram/config 公开接口,供小程序获取完整配置(与 next-project 对齐)
|
||||
// 从 system_config 读取 chapter_config、feature_config、mp_config,合并后返回(免费以章节 is_free/price 为准)
|
||||
// Redis 缓存 10min,配置变更时失效
|
||||
func GetPublicDBConfig(c *gin.Context) {
|
||||
var cached map[string]interface{}
|
||||
if cache.Get(context.Background(), cache.KeyConfigMiniprogram, &cached) && len(cached) > 0 {
|
||||
c.JSON(http.StatusOK, cached)
|
||||
return
|
||||
}
|
||||
defaultPrices := gin.H{"section": float64(1), "fullbook": 9.9}
|
||||
defaultFeatures := gin.H{"matchEnabled": true, "referralEnabled": true, "searchEnabled": true, "aboutEnabled": true}
|
||||
defaultFeatures := gin.H{"matchEnabled": true, "referralEnabled": true, "searchEnabled": true}
|
||||
apiDomain := "https://soulapi.quwanzhi.com"
|
||||
if cfg := config.Get(); cfg != nil && cfg.BaseURL != "" {
|
||||
apiDomain = cfg.BaseURL
|
||||
@@ -101,6 +108,33 @@ func GetPublicDBConfig(c *gin.Context) {
|
||||
if _, has := out["userDiscount"]; !has {
|
||||
out["userDiscount"] = float64(5)
|
||||
}
|
||||
// 链接标签列表(小程序 onLinkTagTap 需要 type;miniprogram 类型存 mpKey,用 key 查 linkedMiniprograms 得 appId)
|
||||
var linkTagRows []model.LinkTag
|
||||
if err := db.Order("label ASC").Find(&linkTagRows).Error; err == nil {
|
||||
tags := make([]gin.H, 0, len(linkTagRows))
|
||||
for _, t := range linkTagRows {
|
||||
h := gin.H{"tagId": t.TagID, "label": t.Label, "url": t.URL, "type": t.Type, "pagePath": t.PagePath}
|
||||
if t.Type == "miniprogram" {
|
||||
h["mpKey"] = t.AppID // miniprogram 类型时 AppID 列存的是密钥
|
||||
} else {
|
||||
h["appId"] = t.AppID
|
||||
}
|
||||
tags = append(tags, h)
|
||||
}
|
||||
out["linkTags"] = tags
|
||||
}
|
||||
// 关联小程序列表(key 为 32 位密钥,小程序用 key 查 appId 后 wx.navigateToMiniProgram)
|
||||
var linkedMpRow model.SystemConfig
|
||||
if err := db.Where("config_key = ?", "linked_miniprograms").First(&linkedMpRow).Error; err == nil && len(linkedMpRow.ConfigValue) > 0 {
|
||||
var linkedList []gin.H
|
||||
if err := json.Unmarshal(linkedMpRow.ConfigValue, &linkedList); err == nil && len(linkedList) > 0 {
|
||||
out["linkedMiniprograms"] = linkedList
|
||||
}
|
||||
}
|
||||
if _, has := out["linkedMiniprograms"]; !has {
|
||||
out["linkedMiniprograms"] = []gin.H{}
|
||||
}
|
||||
cache.Set(context.Background(), cache.KeyConfigMiniprogram, out, cache.ConfigTTL)
|
||||
c.JSON(http.StatusOK, out)
|
||||
}
|
||||
|
||||
@@ -148,11 +182,12 @@ func AdminSettingsGet(c *gin.Context) {
|
||||
}
|
||||
out := gin.H{
|
||||
"success": true,
|
||||
"featureConfig": gin.H{"matchEnabled": true, "referralEnabled": true, "searchEnabled": true, "aboutEnabled": true},
|
||||
"featureConfig": gin.H{"matchEnabled": true, "referralEnabled": true, "searchEnabled": true},
|
||||
"siteSettings": gin.H{"sectionPrice": float64(1), "baseBookPrice": 9.9, "distributorShare": float64(90), "authorInfo": gin.H{}},
|
||||
"mpConfig": defaultMp,
|
||||
"ossConfig": gin.H{},
|
||||
}
|
||||
keys := []string{"feature_config", "site_settings", "mp_config"}
|
||||
keys := []string{"feature_config", "site_settings", "mp_config", "oss_config"}
|
||||
for _, k := range keys {
|
||||
var row model.SystemConfig
|
||||
if err := db.Where("config_key = ?", k).First(&row).Error; err != nil {
|
||||
@@ -182,6 +217,10 @@ func AdminSettingsGet(c *gin.Context) {
|
||||
}
|
||||
out["mpConfig"] = merged
|
||||
}
|
||||
case "oss_config":
|
||||
if m, ok := val.(map[string]interface{}); ok {
|
||||
out["ossConfig"] = m
|
||||
}
|
||||
}
|
||||
}
|
||||
c.JSON(http.StatusOK, out)
|
||||
@@ -193,6 +232,7 @@ func AdminSettingsPost(c *gin.Context) {
|
||||
FeatureConfig map[string]interface{} `json:"featureConfig"`
|
||||
SiteSettings map[string]interface{} `json:"siteSettings"`
|
||||
MpConfig map[string]interface{} `json:"mpConfig"`
|
||||
OssConfig map[string]interface{} `json:"ossConfig"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "请求体无效"})
|
||||
@@ -234,6 +274,13 @@ func AdminSettingsPost(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if body.OssConfig != nil {
|
||||
if err := saveKey("oss_config", "阿里云 OSS 配置", body.OssConfig); err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "保存 OSS 配置失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
}
|
||||
cache.InvalidateConfig()
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "message": "设置已保存"})
|
||||
}
|
||||
|
||||
@@ -317,6 +364,7 @@ func AdminReferralSettingsPost(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
cache.InvalidateConfig()
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "message": "推广设置已保存"})
|
||||
}
|
||||
|
||||
@@ -488,6 +536,7 @@ func DBConfigPost(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
cache.InvalidateConfig()
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "message": "配置保存成功"})
|
||||
}
|
||||
|
||||
@@ -514,14 +563,12 @@ func DBUsersList(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "user": nil})
|
||||
return
|
||||
}
|
||||
// 填充 hasFullBook(含 is_vip 或 orders)
|
||||
// 填充 hasFullBook(含 orders、is_vip、手动设置的 has_full_book)
|
||||
var cnt int64
|
||||
db.Model(&model.Order{}).Where("user_id = ? AND (status = ? OR status = ?) AND (product_type = ? OR product_type = ?)",
|
||||
id, "paid", "completed", "fullbook", "vip").Count(&cnt)
|
||||
user.HasFullBook = ptrBool(cnt > 0)
|
||||
if user.IsVip != nil && *user.IsVip {
|
||||
user.HasFullBook = ptrBool(true)
|
||||
}
|
||||
hasFull := cnt > 0 || (user.IsVip != nil && *user.IsVip) || (user.HasFullBook != nil && *user.HasFullBook)
|
||||
user.HasFullBook = ptrBool(hasFull)
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "user": user})
|
||||
return
|
||||
}
|
||||
@@ -644,11 +691,14 @@ func DBUsersList(c *gin.Context) {
|
||||
// 填充每个用户的实时计算字段
|
||||
for i := range users {
|
||||
uid := users[i].ID
|
||||
// 购买状态(含手动设置的 VIP:is_vip=1 且 vip_expire_date>NOW)
|
||||
// 购买状态(含订单、is_vip、手动设置的 has_full_book)
|
||||
hasFull := hasFullBookMap[uid]
|
||||
if users[i].IsVip != nil && *users[i].IsVip && users[i].VipExpireDate != nil && users[i].VipExpireDate.After(time.Now()) {
|
||||
hasFull = true
|
||||
}
|
||||
if users[i].HasFullBook != nil && *users[i].HasFullBook {
|
||||
hasFull = true
|
||||
}
|
||||
users[i].HasFullBook = ptrBool(hasFull)
|
||||
users[i].PurchasedSectionCount = sectionCountMap[uid]
|
||||
// 分销收益
|
||||
@@ -712,13 +762,14 @@ func DBUsersAction(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "user": u, "isNew": true, "message": "用户创建成功"})
|
||||
return
|
||||
}
|
||||
// PUT 更新(含 VIP 手动设置:is_vip、vip_expire_date、vip_name、vip_avatar、vip_project、vip_contact、vip_bio)
|
||||
// PUT 更新(含 VIP 手动设置:is_vip、vip_expire_date、vip_name、vip_avatar、vip_project、vip_contact、vip_bio;tags 存 ckb_tags)
|
||||
var body struct {
|
||||
ID string `json:"id"`
|
||||
Nickname *string `json:"nickname"`
|
||||
Phone *string `json:"phone"`
|
||||
WechatID *string `json:"wechatId"`
|
||||
Avatar *string `json:"avatar"`
|
||||
Tags *string `json:"tags"` // JSON 数组字符串,如 ["创业者","电商"],存 ckb_tags
|
||||
HasFullBook *bool `json:"hasFullBook"`
|
||||
IsAdmin *bool `json:"isAdmin"`
|
||||
Earnings *float64 `json:"earnings"`
|
||||
@@ -763,6 +814,9 @@ func DBUsersAction(c *gin.Context) {
|
||||
if body.Avatar != nil {
|
||||
updates["avatar"] = *body.Avatar
|
||||
}
|
||||
if body.Tags != nil {
|
||||
updates["ckb_tags"] = *body.Tags
|
||||
}
|
||||
if body.HasFullBook != nil {
|
||||
updates["has_full_book"] = *body.HasFullBook
|
||||
}
|
||||
@@ -855,7 +909,26 @@ func DBUsersDelete(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "用户ID不能为空"})
|
||||
return
|
||||
}
|
||||
if err := database.DB().Where("id = ?", id).Delete(&model.User{}).Error; err != nil {
|
||||
db := database.DB()
|
||||
cleanupTables := []struct{ table, col string }{
|
||||
{"match_records", "user_id"},
|
||||
{"reading_progress", "user_id"},
|
||||
{"user_tracks", "user_id"},
|
||||
{"referral_bindings", "referrer_id"},
|
||||
{"referral_bindings", "referee_id"},
|
||||
{"referral_visits", "visitor_id"},
|
||||
{"ckb_submit_records", "user_id"},
|
||||
{"ckb_lead_records", "user_id"},
|
||||
{"user_addresses", "user_id"},
|
||||
{"user_balances", "user_id"},
|
||||
{"balance_transactions", "user_id"},
|
||||
{"withdrawals", "user_id"},
|
||||
{"orders", "user_id"},
|
||||
}
|
||||
for _, t := range cleanupTables {
|
||||
db.Exec("DELETE FROM "+t.table+" WHERE "+t.col+" = ?", id)
|
||||
}
|
||||
if err := db.Where("id = ?", id).Delete(&model.User{}).Error; err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user