chore: 清理敏感与开发文档,仅同步代码
- 永久忽略并从仓库移除 开发文档/ - 移除并忽略 .env 与小程序私有配置 - 同步小程序/管理端/API与脚本改动 Made-with: Cursor
This commit is contained in:
240
soul-api/internal/handler/db_ckb_leads.go
Normal file
240
soul-api/internal/handler/db_ckb_leads.go
Normal file
@@ -0,0 +1,240 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"soul-api/internal/database"
|
||||
"soul-api/internal/model"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// DBCKBLeadList GET /api/db/ckb-leads 管理端-CKB线索明细
|
||||
// mode=submitted: ckb_submit_records(join/match 提交)
|
||||
// mode=contact: ckb_lead_records(链接卡若留资,有 phone/wechat)
|
||||
func DBCKBLeadList(c *gin.Context) {
|
||||
db := database.DB()
|
||||
mode := c.DefaultQuery("mode", "submitted")
|
||||
matchType := c.Query("matchType")
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "20"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 100 {
|
||||
pageSize = 20
|
||||
}
|
||||
|
||||
dedup := c.DefaultQuery("dedup", "true")
|
||||
|
||||
if mode == "contact" {
|
||||
q := db.Model(&model.CkbLeadRecord{})
|
||||
var total int64
|
||||
var records []model.CkbLeadRecord
|
||||
if dedup == "true" {
|
||||
subQ := db.Model(&model.CkbLeadRecord{}).
|
||||
Select("MAX(id) as id").
|
||||
Group("COALESCE(NULLIF(user_id,''), COALESCE(NULLIF(phone,''), COALESCE(NULLIF(wechat_id,''), CAST(id AS CHAR))))")
|
||||
q = db.Model(&model.CkbLeadRecord{}).Where("id IN (?)", subQ)
|
||||
}
|
||||
q.Count(&total)
|
||||
if err := q.Order("created_at DESC").Offset((page - 1) * pageSize).Limit(pageSize).Find(&records).Error; err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
out := make([]gin.H, 0, len(records))
|
||||
for _, r := range records {
|
||||
out = append(out, gin.H{
|
||||
"id": r.ID,
|
||||
"userId": r.UserID,
|
||||
"userNickname": r.Nickname,
|
||||
"matchType": "lead",
|
||||
"phone": r.Phone,
|
||||
"wechatId": r.WechatID,
|
||||
"name": r.Name,
|
||||
"createdAt": r.CreatedAt,
|
||||
})
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "records": out, "total": total, "page": page, "pageSize": pageSize})
|
||||
return
|
||||
}
|
||||
|
||||
q := db.Model(&model.CkbSubmitRecord{})
|
||||
if matchType != "" {
|
||||
if matchType == "join" || matchType == "match" {
|
||||
q = q.Where("action = ?", matchType)
|
||||
}
|
||||
}
|
||||
if dedup == "true" {
|
||||
subQ := db.Model(&model.CkbSubmitRecord{}).
|
||||
Select("MAX(id) as id").
|
||||
Group("COALESCE(NULLIF(user_id,''), CAST(id AS CHAR))")
|
||||
if matchType == "join" || matchType == "match" {
|
||||
subQ = subQ.Where("action = ?", matchType)
|
||||
}
|
||||
q = db.Model(&model.CkbSubmitRecord{}).Where("id IN (?)", subQ)
|
||||
}
|
||||
var total int64
|
||||
q.Count(&total)
|
||||
var records []model.CkbSubmitRecord
|
||||
if err := q.Order("created_at DESC").Offset((page - 1) * pageSize).Limit(pageSize).Find(&records).Error; err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
userIDs := make(map[string]bool)
|
||||
for _, r := range records {
|
||||
if r.UserID != "" {
|
||||
userIDs[r.UserID] = true
|
||||
}
|
||||
}
|
||||
ids := make([]string, 0, len(userIDs))
|
||||
for id := range userIDs {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
var users []model.User
|
||||
if len(ids) > 0 {
|
||||
db.Where("id IN ?", ids).Find(&users)
|
||||
}
|
||||
userMap := make(map[string]*model.User)
|
||||
for i := range users {
|
||||
userMap[users[i].ID] = &users[i]
|
||||
}
|
||||
safeNickname := func(u *model.User) string {
|
||||
if u == nil || u.Nickname == nil {
|
||||
return ""
|
||||
}
|
||||
return *u.Nickname
|
||||
}
|
||||
out := make([]gin.H, 0, len(records))
|
||||
for _, r := range records {
|
||||
out = append(out, gin.H{
|
||||
"id": r.ID,
|
||||
"userId": r.UserID,
|
||||
"userNickname": safeNickname(userMap[r.UserID]),
|
||||
"matchType": r.Action,
|
||||
"nickname": r.Nickname,
|
||||
"params": r.Params,
|
||||
"createdAt": r.CreatedAt,
|
||||
})
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "records": out, "total": total, "page": page, "pageSize": pageSize})
|
||||
}
|
||||
|
||||
// CKBPersonLeadStats GET /api/db/ckb-person-leads 每个人物的获客线索统计及明细
|
||||
func CKBPersonLeadStats(c *gin.Context) {
|
||||
db := database.DB()
|
||||
personToken := c.Query("token")
|
||||
|
||||
if personToken != "" {
|
||||
// 返回某人物的线索明细(通过 token → Person → 用 PersonID 和 Token 匹配 CkbLeadRecord.TargetPersonID)
|
||||
var person model.Person
|
||||
if err := db.Where("token = ?", personToken).First(&person).Error; err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "人物不存在"})
|
||||
return
|
||||
}
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "20"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 100 {
|
||||
pageSize = 20
|
||||
}
|
||||
q := db.Model(&model.CkbLeadRecord{}).Where("target_person_id IN ?", []string{person.PersonID, person.Token})
|
||||
var total int64
|
||||
q.Count(&total)
|
||||
var records []model.CkbLeadRecord
|
||||
q.Order("created_at DESC").Offset((page - 1) * pageSize).Limit(pageSize).Find(&records)
|
||||
out := make([]gin.H, 0, len(records))
|
||||
for _, r := range records {
|
||||
out = append(out, gin.H{
|
||||
"id": r.ID,
|
||||
"userId": r.UserID,
|
||||
"nickname": r.Nickname,
|
||||
"phone": r.Phone,
|
||||
"wechatId": r.WechatID,
|
||||
"name": r.Name,
|
||||
"source": r.Source,
|
||||
"createdAt": r.CreatedAt,
|
||||
})
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"personName": person.Name,
|
||||
"records": out,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"pageSize": pageSize,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 无 token 参数:返回所有人物的获客数量汇总
|
||||
type PersonLeadStat struct {
|
||||
TargetPersonID string `gorm:"column:target_person_id"`
|
||||
Total int64 `gorm:"column:total"`
|
||||
}
|
||||
var stats []PersonLeadStat
|
||||
db.Raw("SELECT target_person_id, COUNT(*) as total FROM ckb_lead_records WHERE target_person_id != '' GROUP BY target_person_id").Scan(&stats)
|
||||
|
||||
// 构建 personId/token → Person.Token 的映射,使前端能用 token 匹配
|
||||
var persons []model.Person
|
||||
db.Select("person_id, token").Find(&persons)
|
||||
pidToToken := make(map[string]string, len(persons))
|
||||
for _, p := range persons {
|
||||
pidToToken[p.PersonID] = p.Token
|
||||
pidToToken[p.Token] = p.Token
|
||||
}
|
||||
merged := make(map[string]int64)
|
||||
for _, s := range stats {
|
||||
key := pidToToken[s.TargetPersonID]
|
||||
if key == "" {
|
||||
key = s.TargetPersonID
|
||||
}
|
||||
merged[key] += s.Total
|
||||
}
|
||||
byPerson := make([]gin.H, 0, len(merged))
|
||||
for token, total := range merged {
|
||||
byPerson = append(byPerson, gin.H{"token": token, "total": total})
|
||||
}
|
||||
|
||||
// 同时统计全局(无特定人物的)线索
|
||||
var globalTotal int64
|
||||
db.Model(&model.CkbLeadRecord{}).Where("target_person_id = '' OR target_person_id IS NULL").Count(&globalTotal)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"byPerson": byPerson,
|
||||
"globalLeads": globalTotal,
|
||||
})
|
||||
}
|
||||
|
||||
// CKBPlanStats GET /api/db/ckb-plan-stats 存客宝获客计划统计(基于 ckb_submit_records + ckb_lead_records)
|
||||
func CKBPlanStats(c *gin.Context) {
|
||||
db := database.DB()
|
||||
type TypeStat struct {
|
||||
Action string `gorm:"column:action" json:"matchType"`
|
||||
Total int64 `gorm:"column:total" json:"total"`
|
||||
}
|
||||
var submitStats []TypeStat
|
||||
db.Raw("SELECT action, COUNT(*) as total FROM ckb_submit_records GROUP BY action").Scan(&submitStats)
|
||||
var submitTotal int64
|
||||
db.Model(&model.CkbSubmitRecord{}).Count(&submitTotal)
|
||||
var leadTotal int64
|
||||
db.Model(&model.CkbLeadRecord{}).Count(&leadTotal)
|
||||
withContact := leadTotal // ckb_lead_records 均有 phone 或 wechat
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"data": gin.H{
|
||||
"ckbTotal": submitTotal + leadTotal,
|
||||
"withContact": withContact,
|
||||
"byType": submitStats,
|
||||
"ckbApiKey": "***",
|
||||
"ckbApiUrl": "https://ckbapi.quwanzhi.com/v1/api/scenarios",
|
||||
"docNotes": "",
|
||||
"docContent": "",
|
||||
"routes": gin.H{},
|
||||
},
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user