chore: 清理敏感与开发文档,仅同步代码
- 永久忽略并从仓库移除 开发文档/ - 移除并忽略 .env 与小程序私有配置 - 同步小程序/管理端/API与脚本改动 Made-with: Cursor
This commit is contained in:
206
soul-api/internal/handler/admin_linked_mp.go
Normal file
206
soul-api/internal/handler/admin_linked_mp.go
Normal file
@@ -0,0 +1,206 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"soul-api/internal/cache"
|
||||
"soul-api/internal/database"
|
||||
"soul-api/internal/model"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
const linkedMpConfigKey = "linked_miniprograms"
|
||||
|
||||
// LinkedMpItem 关联小程序项,key 为 32 位密钥,链接标签存 key,小程序端用 key 查 appId
|
||||
type LinkedMpItem struct {
|
||||
Key string `json:"key"`
|
||||
Name string `json:"name"`
|
||||
AppID string `json:"appId"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Sort int `json:"sort"`
|
||||
}
|
||||
|
||||
// AdminLinkedMpList GET /api/admin/linked-miniprograms 管理端-关联小程序列表
|
||||
func AdminLinkedMpList(c *gin.Context) {
|
||||
db := database.DB()
|
||||
var row model.SystemConfig
|
||||
if err := db.Where("config_key = ?", linkedMpConfigKey).First(&row).Error; err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": []LinkedMpItem{}})
|
||||
return
|
||||
}
|
||||
var list []LinkedMpItem
|
||||
if err := json.Unmarshal(row.ConfigValue, &list); err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": []LinkedMpItem{}})
|
||||
return
|
||||
}
|
||||
if list == nil {
|
||||
list = []LinkedMpItem{}
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": list})
|
||||
}
|
||||
|
||||
// AdminLinkedMpCreate POST /api/admin/linked-miniprograms 管理端-新增关联小程序
|
||||
func AdminLinkedMpCreate(c *gin.Context) {
|
||||
var body struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
AppID string `json:"appId" binding:"required"`
|
||||
Path string `json:"path"`
|
||||
Sort int `json:"sort"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "请填写小程序名称和 AppID"})
|
||||
return
|
||||
}
|
||||
body.Name = trimSpace(body.Name)
|
||||
body.AppID = trimSpace(body.AppID)
|
||||
if body.Name == "" || body.AppID == "" {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "小程序名称和 AppID 不能为空"})
|
||||
return
|
||||
}
|
||||
key, err := genMpKey()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "生成密钥失败"})
|
||||
return
|
||||
}
|
||||
item := LinkedMpItem{Key: key, Name: body.Name, AppID: body.AppID, Path: body.Path, Sort: body.Sort}
|
||||
db := database.DB()
|
||||
var row model.SystemConfig
|
||||
var list []LinkedMpItem
|
||||
if err := db.Where("config_key = ?", linkedMpConfigKey).First(&row).Error; err != nil {
|
||||
list = []LinkedMpItem{}
|
||||
} else {
|
||||
_ = json.Unmarshal(row.ConfigValue, &list)
|
||||
if list == nil {
|
||||
list = []LinkedMpItem{}
|
||||
}
|
||||
}
|
||||
list = append(list, item)
|
||||
valBytes, _ := json.Marshal(list)
|
||||
desc := "关联小程序列表,用于 wx.navigateToMiniProgram 跳转"
|
||||
if row.ConfigKey == "" {
|
||||
row = model.SystemConfig{ConfigKey: linkedMpConfigKey, ConfigValue: valBytes, Description: &desc}
|
||||
if err := db.Create(&row).Error; err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "保存失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
} else {
|
||||
row.ConfigValue = valBytes
|
||||
if err := db.Save(&row).Error; err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "保存失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
}
|
||||
cache.InvalidateConfig()
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": item})
|
||||
}
|
||||
|
||||
// AdminLinkedMpUpdate PUT /api/admin/linked-miniprograms 管理端-编辑关联小程序
|
||||
func AdminLinkedMpUpdate(c *gin.Context) {
|
||||
var body struct {
|
||||
Key string `json:"key" binding:"required"`
|
||||
Name string `json:"name" binding:"required"`
|
||||
AppID string `json:"appId" binding:"required"`
|
||||
Path string `json:"path"`
|
||||
Sort int `json:"sort"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "参数无效"})
|
||||
return
|
||||
}
|
||||
body.Name = trimSpace(body.Name)
|
||||
body.AppID = trimSpace(body.AppID)
|
||||
if body.Name == "" || body.AppID == "" {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "小程序名称和 AppID 不能为空"})
|
||||
return
|
||||
}
|
||||
db := database.DB()
|
||||
var row model.SystemConfig
|
||||
if err := db.Where("config_key = ?", linkedMpConfigKey).First(&row).Error; err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "未找到该记录"})
|
||||
return
|
||||
}
|
||||
var list []LinkedMpItem
|
||||
if err := json.Unmarshal(row.ConfigValue, &list); err != nil || list == nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "数据格式错误"})
|
||||
return
|
||||
}
|
||||
found := false
|
||||
for i := range list {
|
||||
if list[i].Key == body.Key {
|
||||
list[i].Name = body.Name
|
||||
list[i].AppID = body.AppID
|
||||
list[i].Path = body.Path
|
||||
list[i].Sort = body.Sort
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "未找到该记录"})
|
||||
return
|
||||
}
|
||||
valBytes, _ := json.Marshal(list)
|
||||
row.ConfigValue = valBytes
|
||||
if err := db.Save(&row).Error; err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "保存失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
cache.InvalidateConfig()
|
||||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||||
}
|
||||
|
||||
// AdminLinkedMpDelete DELETE /api/admin/linked-miniprograms/:id 管理端-删除(:id 实际传 key)
|
||||
func AdminLinkedMpDelete(c *gin.Context) {
|
||||
key := c.Param("id")
|
||||
if key == "" {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "缺少密钥"})
|
||||
return
|
||||
}
|
||||
db := database.DB()
|
||||
var row model.SystemConfig
|
||||
if err := db.Where("config_key = ?", linkedMpConfigKey).First(&row).Error; err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "未找到该记录"})
|
||||
return
|
||||
}
|
||||
var list []LinkedMpItem
|
||||
if err := json.Unmarshal(row.ConfigValue, &list); err != nil || list == nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "数据格式错误"})
|
||||
return
|
||||
}
|
||||
newList := make([]LinkedMpItem, 0, len(list))
|
||||
for _, item := range list {
|
||||
if item.Key != key {
|
||||
newList = append(newList, item)
|
||||
}
|
||||
}
|
||||
valBytes, _ := json.Marshal(newList)
|
||||
row.ConfigValue = valBytes
|
||||
if err := db.Save(&row).Error; err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "删除失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
cache.InvalidateConfig()
|
||||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||||
}
|
||||
|
||||
// genMpKey 生成 32 位英文+数字密钥,供链接标签引用
|
||||
func genMpKey() (string, error) {
|
||||
b := make([]byte, 24)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
return "", err
|
||||
}
|
||||
// base64 编码后取 32 位,去掉 +/= 仅保留字母数字
|
||||
s := base64.URLEncoding.EncodeToString(b)
|
||||
s = strings.ReplaceAll(s, "+", "")
|
||||
s = strings.ReplaceAll(s, "/", "")
|
||||
s = strings.ReplaceAll(s, "=", "")
|
||||
if len(s) >= 32 {
|
||||
return s[:32], nil
|
||||
}
|
||||
return s + "0123456789abcdefghijklmnopqrstuv"[:(32-len(s))], nil
|
||||
}
|
||||
Reference in New Issue
Block a user