更新小程序配置,切换API基础地址至本地开发环境。优化用户提交联系方式的逻辑,增加2分钟内限频提示,确保用户体验流畅。调整后端存客宝接口,支持记录用户提交信息并处理频率限制。
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -15,16 +16,31 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"soul-api/internal/config"
|
||||
"soul-api/internal/database"
|
||||
"soul-api/internal/model"
|
||||
)
|
||||
|
||||
// 存客宝 API Key 约定(详见 开发文档/8、部署/存客宝API-Key约定.md):
|
||||
// - 链接卡若(添加卡若好友):使用 CKB_LEAD_API_KEY(.env 配置),未配则用下方 ckbAPIKey
|
||||
// - 其他场景(join/match 等):使用 ckbAPIKey
|
||||
const ckbAPIKey = "fyngh-ecy9h-qkdae-epwd5-rz6kd"
|
||||
const ckbAPIURL = "https://ckbapi.quwanzhi.com/v1/api/scenarios"
|
||||
|
||||
var ckbSourceMap = map[string]string{"team": "团队招募", "investor": "资源对接", "mentor": "导师顾问", "partner": "创业合伙"}
|
||||
var ckbTagsMap = map[string]string{"team": "切片团队,团队招募", "investor": "资源对接,资源群", "mentor": "导师顾问,咨询服务", "partner": "创业合伙,创业伙伴"}
|
||||
|
||||
// ckbSubmitSave 加好友/留资类接口统一落库:记录 action、userId、昵称、用户提交的传参,写入 ckb_submit_records
|
||||
func ckbSubmitSave(action, userID, nickname string, params interface{}) {
|
||||
paramsJSON, _ := json.Marshal(params)
|
||||
_ = database.DB().Create(&model.CkbSubmitRecord{
|
||||
Action: action,
|
||||
UserID: userID,
|
||||
Nickname: nickname,
|
||||
Params: string(paramsJSON),
|
||||
}).Error
|
||||
}
|
||||
|
||||
// ckbSign 与 next-project app/api/ckb/join 一致:排除 sign/apiKey/portrait,空值跳过,按键升序拼接值,MD5(拼接串) 再 MD5(结果+apiKey)
|
||||
func ckbSign(params map[string]interface{}, apiKey string) string {
|
||||
keys := make([]string, 0, len(params))
|
||||
@@ -85,6 +101,20 @@ func CKBJoin(c *gin.Context) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "无效的加入类型"})
|
||||
return
|
||||
}
|
||||
nickname := strings.TrimSpace(body.Name)
|
||||
if nickname == "" && body.UserID != "" {
|
||||
var u model.User
|
||||
if database.DB().Select("nickname").Where("id = ?", body.UserID).First(&u).Error == nil && u.Nickname != nil && *u.Nickname != "" {
|
||||
nickname = *u.Nickname
|
||||
}
|
||||
}
|
||||
if nickname == "" {
|
||||
nickname = "-"
|
||||
}
|
||||
ckbSubmitSave("join", body.UserID, nickname, map[string]interface{}{
|
||||
"type": body.Type, "phone": body.Phone, "wechat": body.Wechat, "name": body.Name,
|
||||
"userId": body.UserID, "remark": body.Remark, "canHelp": body.CanHelp, "needHelp": body.NeedHelp,
|
||||
})
|
||||
ts := time.Now().Unix()
|
||||
params := map[string]interface{}{
|
||||
"timestamp": ts,
|
||||
@@ -195,6 +225,14 @@ func CKBMatch(c *gin.Context) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "请提供手机号或微信号"})
|
||||
return
|
||||
}
|
||||
nickname := strings.TrimSpace(body.Nickname)
|
||||
if nickname == "" {
|
||||
nickname = "-"
|
||||
}
|
||||
ckbSubmitSave("match", body.UserID, nickname, map[string]interface{}{
|
||||
"matchType": body.MatchType, "phone": body.Phone, "wechat": body.Wechat,
|
||||
"userId": body.UserID, "nickname": body.Nickname, "matchedUser": body.MatchedUser,
|
||||
})
|
||||
ts := time.Now().Unix()
|
||||
label := ckbSourceMap[body.MatchType]
|
||||
if label == "" {
|
||||
@@ -279,12 +317,58 @@ func CKBLead(c *gin.Context) {
|
||||
if name == "" {
|
||||
name = "小程序用户"
|
||||
}
|
||||
db := database.DB()
|
||||
var cond []string
|
||||
var args []interface{}
|
||||
if body.UserID != "" {
|
||||
cond = append(cond, "user_id = ?")
|
||||
args = append(args, body.UserID)
|
||||
}
|
||||
if phone != "" {
|
||||
cond = append(cond, "phone = ?")
|
||||
args = append(args, phone)
|
||||
}
|
||||
if wechatId != "" {
|
||||
cond = append(cond, "wechat_id = ?")
|
||||
args = append(args, wechatId)
|
||||
}
|
||||
// 2 分钟内同一用户/手机/微信只能提交一次(与前端限频一致)
|
||||
if len(cond) > 0 {
|
||||
cutoff := time.Now().Add(-2 * time.Minute)
|
||||
var recentCount int64
|
||||
if db.Model(&model.CkbLeadRecord{}).Where(strings.Join(cond, " OR "), args...).Where("created_at > ?", cutoff).Count(&recentCount) == nil && recentCount > 0 {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "您操作太频繁,请2分钟后再试"})
|
||||
return
|
||||
}
|
||||
}
|
||||
// 是否曾留资过(仅用于成功后的提示文案)
|
||||
repeatedSubmit := false
|
||||
if len(cond) > 0 {
|
||||
var existCount int64
|
||||
repeatedSubmit = db.Model(&model.CkbLeadRecord{}).Where(strings.Join(cond, " OR "), args...).Count(&existCount) == nil && existCount > 0
|
||||
}
|
||||
|
||||
paramsJSON, _ := json.Marshal(map[string]interface{}{
|
||||
"userId": body.UserID, "phone": phone, "wechatId": wechatId, "name": body.Name,
|
||||
})
|
||||
_ = db.Create(&model.CkbLeadRecord{
|
||||
UserID: body.UserID,
|
||||
Nickname: name,
|
||||
Phone: phone,
|
||||
WechatID: wechatId,
|
||||
Name: strings.TrimSpace(body.Name),
|
||||
Params: string(paramsJSON),
|
||||
}).Error
|
||||
ts := time.Now().Unix()
|
||||
// 链接卡若:GET + query(便于浏览器测试),传参:name, phone, wechatId, apiKey, timestamp, sign
|
||||
leadKey := ckbAPIKey
|
||||
if cfg := config.Get(); cfg != nil && cfg.CkbLeadAPIKey != "" {
|
||||
leadKey = cfg.CkbLeadAPIKey
|
||||
}
|
||||
params := map[string]interface{}{
|
||||
"timestamp": ts,
|
||||
"source": "小程序-链接卡若",
|
||||
"remark": "首页点击「链接卡若」留资",
|
||||
"name": name,
|
||||
"timestamp": ts,
|
||||
"apiKey": leadKey,
|
||||
}
|
||||
if phone != "" {
|
||||
params["phone"] = phone
|
||||
@@ -292,19 +376,30 @@ func CKBLead(c *gin.Context) {
|
||||
if wechatId != "" {
|
||||
params["wechatId"] = wechatId
|
||||
}
|
||||
params["apiKey"] = ckbAPIKey
|
||||
params["sign"] = ckbSign(params, ckbAPIKey)
|
||||
raw, _ := json.Marshal(params)
|
||||
resp, err := http.Post(ckbAPIURL, "application/json", bytes.NewReader(raw))
|
||||
params["sign"] = ckbSign(params, leadKey)
|
||||
q := url.Values{}
|
||||
q.Set("name", name)
|
||||
q.Set("timestamp", strconv.FormatInt(ts, 10))
|
||||
q.Set("apiKey", leadKey)
|
||||
if phone != "" {
|
||||
q.Set("phone", phone)
|
||||
}
|
||||
if wechatId != "" {
|
||||
q.Set("wechatId", wechatId)
|
||||
}
|
||||
q.Set("sign", params["sign"].(string))
|
||||
reqURL := ckbAPIURL + "?" + q.Encode()
|
||||
resp, err := http.Get(reqURL)
|
||||
if err != nil {
|
||||
fmt.Printf("[CKBLead] 请求存客宝失败: %v\n", err)
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "网络异常,请稍后重试"})
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
b, _ := io.ReadAll(resp.Body)
|
||||
var result struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data interface{} `json:"data"`
|
||||
}
|
||||
_ = json.Unmarshal(b, &result)
|
||||
@@ -313,12 +408,23 @@ func CKBLead(c *gin.Context) {
|
||||
if result.Message == "已存在" {
|
||||
msg = "您已留资,我们会尽快联系您"
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "message": msg, "data": result.Data})
|
||||
if repeatedSubmit {
|
||||
msg = "您已留资过,我们已再次通知卡若,请耐心等待添加"
|
||||
}
|
||||
data := gin.H{}
|
||||
if result.Data != nil {
|
||||
if m, ok := result.Data.(map[string]interface{}); ok {
|
||||
data = m
|
||||
}
|
||||
}
|
||||
data["repeatedSubmit"] = repeatedSubmit
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "message": msg, "data": data})
|
||||
return
|
||||
}
|
||||
errMsg := result.Message
|
||||
if errMsg == "" {
|
||||
errMsg = "提交失败,请稍后重试"
|
||||
}
|
||||
fmt.Printf("[CKBLead] 存客宝返回异常 code=%d message=%s raw=%s\n", result.Code, result.Message, string(b))
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "message": errMsg})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user