新增置顶人功能,允许在小程序首页展示特定用户信息(昵称、头像)。更新相关 API 接口以支持获取和设置置顶人,优化用户体验。调整小程序页面以动态显示置顶人信息,确保一致性和可用性。
This commit is contained in:
@@ -77,6 +77,24 @@ func ckbSign(params map[string]interface{}, apiKey string) string {
|
||||
return hex.EncodeToString(h2[:])
|
||||
}
|
||||
|
||||
const pinnedPersonTokenKey = "pinned_person_token"
|
||||
|
||||
// getPinnedPersonToken 读取置顶人物 token(system_config)
|
||||
func getPinnedPersonToken() string {
|
||||
var row model.SystemConfig
|
||||
if err := database.DB().Where("config_key = ?", pinnedPersonTokenKey).First(&row).Error; err != nil || len(row.ConfigValue) == 0 {
|
||||
return ""
|
||||
}
|
||||
var m map[string]interface{}
|
||||
if err := json.Unmarshal(row.ConfigValue, &m); err != nil {
|
||||
return ""
|
||||
}
|
||||
if v, ok := m["token"].(string); ok && strings.TrimSpace(v) != "" {
|
||||
return strings.TrimSpace(v)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// getCkbLeadApiKey 链接卡若密钥优先级:system_config.site_settings.ckbLeadApiKey > .env CKB_LEAD_API_KEY > 代码内置 ckbAPIKey
|
||||
func getCkbLeadApiKey() string {
|
||||
var row model.SystemConfig
|
||||
@@ -307,8 +325,49 @@ func CKBSync(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||||
}
|
||||
|
||||
// CKBPinnedPerson GET /api/miniprogram/ckb/pinned-person 小程序首页获取置顶人信息(昵称、头像)
|
||||
// 有 user_id 时从 users 表取;无则用 Person.name、Person.avatar
|
||||
func CKBPinnedPerson(c *gin.Context) {
|
||||
token := getPinnedPersonToken()
|
||||
if token == "" {
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": nil})
|
||||
return
|
||||
}
|
||||
db := database.DB()
|
||||
var p model.Person
|
||||
if err := db.Where("token = ?", token).First(&p).Error; err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": nil})
|
||||
return
|
||||
}
|
||||
nickname := strings.TrimSpace(p.Name)
|
||||
avatar := strings.TrimSpace(p.Avatar)
|
||||
if p.UserID != nil && strings.TrimSpace(*p.UserID) != "" {
|
||||
var u model.User
|
||||
if db.Select("nickname", "avatar").Where("id = ?", *p.UserID).First(&u).Error == nil {
|
||||
if u.Nickname != nil && strings.TrimSpace(*u.Nickname) != "" {
|
||||
nickname = strings.TrimSpace(*u.Nickname)
|
||||
}
|
||||
if u.Avatar != nil && strings.TrimSpace(*u.Avatar) != "" {
|
||||
avatar = strings.TrimSpace(*u.Avatar)
|
||||
}
|
||||
}
|
||||
}
|
||||
if nickname == "" {
|
||||
nickname = "链接人"
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"data": gin.H{
|
||||
"nickname": nickname,
|
||||
"avatar": avatar,
|
||||
"token": token,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// CKBIndexLead POST /api/miniprogram/ckb/index-lead 小程序首页「点击链接卡若」专用留资接口
|
||||
// - 固定使用全局 CKB_LEAD_API_KEY,不受文章 @ 人物的 ckb_api_key 影响
|
||||
// - 有置顶人时:使用置顶人的 ckb_api_key 推送到其存客宝计划(与文章 @ 获客逻辑一致)
|
||||
// - 无置顶人时:fallback 全局 CKB_LEAD_API_KEY
|
||||
// - 请求体:userId(可选,用于补全昵称)、phone/wechatId(至少一个)、name(可选)
|
||||
func CKBIndexLead(c *gin.Context) {
|
||||
var body struct {
|
||||
@@ -337,8 +396,17 @@ func CKBIndexLead(c *gin.Context) {
|
||||
name = "小程序用户"
|
||||
}
|
||||
|
||||
// 首页固定使用全局密钥:system_config > .env > 代码内置
|
||||
// 优先使用置顶人的 ckb_api_key;无则用全局密钥
|
||||
leadKey := getCkbLeadApiKey()
|
||||
targetPersonID := ""
|
||||
pinnedToken := getPinnedPersonToken()
|
||||
if pinnedToken != "" {
|
||||
var p model.Person
|
||||
if db.Where("token = ?", pinnedToken).First(&p).Error == nil && strings.TrimSpace(p.CkbApiKey) != "" {
|
||||
leadKey = p.CkbApiKey
|
||||
targetPersonID = p.PersonID
|
||||
}
|
||||
}
|
||||
|
||||
// 去重限频:2 分钟内同一用户/手机/微信只能提交一次
|
||||
var cond []string
|
||||
@@ -362,16 +430,17 @@ func CKBIndexLead(c *gin.Context) {
|
||||
source := "index_link_button"
|
||||
paramsJSON, _ := json.Marshal(map[string]interface{}{
|
||||
"userId": body.UserID, "phone": phone, "wechatId": wechatId, "name": body.Name,
|
||||
"source": source,
|
||||
"source": source, "pinnedToken": pinnedToken,
|
||||
})
|
||||
_ = db.Create(&model.CkbLeadRecord{
|
||||
UserID: body.UserID,
|
||||
Nickname: name,
|
||||
Phone: phone,
|
||||
WechatID: wechatId,
|
||||
Name: strings.TrimSpace(body.Name),
|
||||
Source: source,
|
||||
Params: string(paramsJSON),
|
||||
UserID: body.UserID,
|
||||
Nickname: name,
|
||||
Phone: phone,
|
||||
WechatID: wechatId,
|
||||
Name: strings.TrimSpace(body.Name),
|
||||
TargetPersonID: targetPersonID,
|
||||
Source: source,
|
||||
Params: string(paramsJSON),
|
||||
}).Error
|
||||
|
||||
ts := time.Now().Unix()
|
||||
@@ -405,9 +474,17 @@ func CKBIndexLead(c *gin.Context) {
|
||||
}
|
||||
_ = json.Unmarshal(b, &result)
|
||||
if result.Code == 200 {
|
||||
msg := "提交成功,卡若会尽快联系您"
|
||||
msg := "提交成功,我们会尽快联系您"
|
||||
if pinnedToken != "" {
|
||||
var p model.Person
|
||||
if db.Where("token = ?", pinnedToken).First(&p).Error == nil && p.Name != "" {
|
||||
msg = "提交成功," + p.Name + "会尽快联系您"
|
||||
}
|
||||
} else {
|
||||
msg = "提交成功,卡若会尽快联系您"
|
||||
}
|
||||
if repeatedSubmit {
|
||||
msg = "您已留资过,我们已再次通知卡若,请耐心等待添加"
|
||||
msg = "您已留资过,我们已再次通知,请耐心等待添加"
|
||||
}
|
||||
data := gin.H{}
|
||||
if result.Data != nil {
|
||||
|
||||
@@ -3,6 +3,7 @@ package handler
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
@@ -404,8 +405,65 @@ func genPersonToken() (string, error) {
|
||||
return s + "0123456789abcdefghijklmnopqrstuv"[:(32-len(s))], nil
|
||||
}
|
||||
|
||||
// DBPersonPin PUT /api/db/persons/pin 管理端-设置置顶人(唯一,新置顶会覆盖旧的)
|
||||
func DBPersonPin(c *gin.Context) {
|
||||
var body struct {
|
||||
Token string `json:"token" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "缺少 token"})
|
||||
return
|
||||
}
|
||||
token := strings.TrimSpace(body.Token)
|
||||
if token == "" {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "token 不能为空"})
|
||||
return
|
||||
}
|
||||
db := database.DB()
|
||||
var p model.Person
|
||||
if err := db.Where("token = ?", token).First(&p).Error; err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "该人物不存在"})
|
||||
return
|
||||
}
|
||||
valBytes, _ := json.Marshal(map[string]string{"token": token})
|
||||
desc := "小程序首页置顶人物 token"
|
||||
var row model.SystemConfig
|
||||
var err error
|
||||
if db.Where("config_key = ?", pinnedPersonTokenKey).First(&row).Error != nil {
|
||||
row = model.SystemConfig{ConfigKey: pinnedPersonTokenKey, ConfigValue: model.ConfigValue(valBytes), Description: &desc}
|
||||
err = db.Create(&row).Error
|
||||
} else {
|
||||
row.ConfigValue = model.ConfigValue(valBytes)
|
||||
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": "已置顶 " + p.Name})
|
||||
}
|
||||
|
||||
// DBPersonPinnedToken GET /api/db/persons/pinned-token 管理端-获取当前置顶人 token
|
||||
func DBPersonPinnedToken(c *gin.Context) {
|
||||
var row model.SystemConfig
|
||||
if err := database.DB().Where("config_key = ?", pinnedPersonTokenKey).First(&row).Error; err != nil || len(row.ConfigValue) == 0 {
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "token": ""})
|
||||
return
|
||||
}
|
||||
var m map[string]interface{}
|
||||
if err := json.Unmarshal(row.ConfigValue, &m); err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "token": ""})
|
||||
return
|
||||
}
|
||||
token := ""
|
||||
if v, ok := m["token"].(string); ok {
|
||||
token = strings.TrimSpace(v)
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "token": token})
|
||||
}
|
||||
|
||||
// DBPersonDelete DELETE /api/db/persons?personId=xxx 管理端-删除人物
|
||||
// 若有 ckb_plan_id,先调存客宝删除计划,再删本地
|
||||
// 若有 ckb_plan_id,先调存客宝删除计划,再删本地;若为当前置顶则清空置顶
|
||||
func DBPersonDelete(c *gin.Context) {
|
||||
pid := c.Query("personId")
|
||||
if pid == "" {
|
||||
@@ -417,6 +475,16 @@ func DBPersonDelete(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "人物不存在"})
|
||||
return
|
||||
}
|
||||
// 若为当前置顶,先清空置顶配置
|
||||
var cfg model.SystemConfig
|
||||
if database.DB().Where("config_key = ?", pinnedPersonTokenKey).First(&cfg).Error == nil && len(cfg.ConfigValue) > 0 {
|
||||
var m map[string]interface{}
|
||||
if json.Unmarshal(cfg.ConfigValue, &m) == nil {
|
||||
if v, ok := m["token"].(string); ok && strings.TrimSpace(v) == row.Token {
|
||||
_ = database.DB().Where("config_key = ?", pinnedPersonTokenKey).Delete(&model.SystemConfig{}).Error
|
||||
}
|
||||
}
|
||||
}
|
||||
// 若有存客宝计划,先调 CKB 删除
|
||||
if row.CkbPlanID > 0 {
|
||||
token, err := ckbOpenGetToken()
|
||||
|
||||
Reference in New Issue
Block a user