优化线索、商机、项目相关开发
This commit is contained in:
@@ -7,10 +7,28 @@ import (
|
||||
|
||||
"server/models"
|
||||
"server/pkg/jwtutil"
|
||||
"server/services"
|
||||
|
||||
beego "github.com/beego/beego/v2/server/web"
|
||||
)
|
||||
|
||||
// resolveUserName 按租户 + 用户ID 解析用户显示名(优先真实姓名,退化到账号,最后退化到登录名)。
|
||||
// 注意:JWT 里的 Username 是登录账号,直接存会导致前端"负责人"显示成账号。
|
||||
func resolveUserName(claims *jwtutil.Claims) string {
|
||||
if claims == nil {
|
||||
return ""
|
||||
}
|
||||
tid := uint64(claims.TenantId)
|
||||
account, name := services.OperationLogUser(&tid, uint64(claims.UserID))
|
||||
if strings.TrimSpace(name) != "" {
|
||||
return strings.TrimSpace(name)
|
||||
}
|
||||
if strings.TrimSpace(account) != "" {
|
||||
return strings.TrimSpace(account)
|
||||
}
|
||||
return claims.Username
|
||||
}
|
||||
|
||||
// CRM 业务管线(线索/商机/项目/回访)公共辅助方法。
|
||||
// related_type 约定:1=线索 2=商机 3=项目
|
||||
|
||||
@@ -98,7 +116,7 @@ func crmWriteLog(tenantID string, relatedType int8, relatedID uint64, action, co
|
||||
operatorID, operatorName := "", ""
|
||||
if claims != nil {
|
||||
operatorID = pipelineUID(claims)
|
||||
operatorName = claims.Username
|
||||
operatorName = resolveUserName(claims)
|
||||
}
|
||||
log := models.TenantCrmOperateLog{
|
||||
TenantID: tenantID,
|
||||
@@ -245,3 +263,117 @@ func syncContactsToCompany(tenantID string, customerID uint64, contacts []models
|
||||
}
|
||||
return inserted, merged
|
||||
}
|
||||
|
||||
// relatedRef 关联对象引用(related_type + related_id)
|
||||
type relatedRef struct {
|
||||
Type int8
|
||||
ID uint64
|
||||
}
|
||||
|
||||
// relatedChain 返回详情页需要展示的关联链路(上游 + 自身),顺序:线索 → 商机 → 项目。
|
||||
// 线索 → [线索]
|
||||
// 商机 → [线索, 商机]
|
||||
// 项目 → [线索, 商机, 项目]
|
||||
// 过程数据(联系人 / 跟进记录 / 附件)只存在创建时的对象上、不做搬迁,
|
||||
// 由上层按链路累积查询,从而在商机里能看到线索阶段的数据,在项目里能看到全部阶段的数据。
|
||||
func relatedChain(tenantID string, relatedType int8, relatedID uint64) []relatedRef {
|
||||
if models.Orm == nil || relatedID == 0 {
|
||||
return []relatedRef{{Type: relatedType, ID: relatedID}}
|
||||
}
|
||||
refs := make([]relatedRef, 0, 3)
|
||||
switch relatedType {
|
||||
case 2: // 商机:来源线索 + 自身
|
||||
var biz models.TenantCrmBusiness
|
||||
if err := models.Orm.QueryTable(new(models.TenantCrmBusiness)).
|
||||
Filter("id", relatedID).Filter("tenant_id", tenantID).
|
||||
Filter("delete_time__isnull", true).One(&biz); err == nil {
|
||||
if biz.ClueID != nil && *biz.ClueID > 0 {
|
||||
refs = append(refs, relatedRef{Type: 1, ID: *biz.ClueID})
|
||||
}
|
||||
}
|
||||
refs = append(refs, relatedRef{Type: 2, ID: relatedID})
|
||||
case 3: // 项目:来源商机 + 商机的来源线索 + 自身
|
||||
var proj models.TenantCrmProject
|
||||
if err := models.Orm.QueryTable(new(models.TenantCrmProject)).
|
||||
Filter("id", relatedID).Filter("tenant_id", tenantID).
|
||||
Filter("delete_time__isnull", true).One(&proj); err == nil {
|
||||
if proj.BusinessID != nil && *proj.BusinessID > 0 {
|
||||
var biz models.TenantCrmBusiness
|
||||
if err2 := models.Orm.QueryTable(new(models.TenantCrmBusiness)).
|
||||
Filter("id", *proj.BusinessID).Filter("tenant_id", tenantID).
|
||||
Filter("delete_time__isnull", true).One(&biz); err2 == nil {
|
||||
if biz.ClueID != nil && *biz.ClueID > 0 {
|
||||
refs = append(refs, relatedRef{Type: 1, ID: *biz.ClueID})
|
||||
}
|
||||
refs = append(refs, relatedRef{Type: 2, ID: biz.ID})
|
||||
}
|
||||
}
|
||||
}
|
||||
refs = append(refs, relatedRef{Type: 3, ID: relatedID})
|
||||
default: // 线索:仅自身
|
||||
refs = append(refs, relatedRef{Type: relatedType, ID: relatedID})
|
||||
}
|
||||
return refs
|
||||
}
|
||||
|
||||
// attachBusinessName 批量填充项目的「来源商机名称」(非表字段,仅用于展示)。
|
||||
func attachBusinessName(tenantID string, list []models.TenantCrmProject) {
|
||||
ids := make([]uint64, 0, len(list))
|
||||
seen := map[uint64]bool{}
|
||||
for _, p := range list {
|
||||
if p.BusinessID != nil && *p.BusinessID > 0 && !seen[*p.BusinessID] {
|
||||
seen[*p.BusinessID] = true
|
||||
ids = append(ids, *p.BusinessID)
|
||||
}
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
return
|
||||
}
|
||||
var rows []models.TenantCrmBusiness
|
||||
_, _ = models.Orm.QueryTable(new(models.TenantCrmBusiness)).
|
||||
Filter("tenant_id", tenantID).
|
||||
Filter("id__in", ids).
|
||||
Filter("delete_time__isnull", true).
|
||||
All(&rows)
|
||||
nameMap := make(map[uint64]string, len(rows))
|
||||
for _, r := range rows {
|
||||
nameMap[r.ID] = r.BusinessName
|
||||
}
|
||||
for i := range list {
|
||||
if list[i].BusinessID != nil {
|
||||
list[i].BusinessName = nameMap[*list[i].BusinessID]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// attachClueName 批量填充商机的「来源线索名称」(非表字段,仅用于展示)。
|
||||
func attachClueName(tenantID string, list []models.TenantCrmBusiness) {
|
||||
ids := make([]uint64, 0, len(list))
|
||||
seen := map[uint64]bool{}
|
||||
for _, b := range list {
|
||||
if b.ClueID != nil && *b.ClueID > 0 && !seen[*b.ClueID] {
|
||||
seen[*b.ClueID] = true
|
||||
ids = append(ids, *b.ClueID)
|
||||
}
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
return
|
||||
}
|
||||
var rows []models.TenantCrmClue
|
||||
_, _ = models.Orm.QueryTable(new(models.TenantCrmClue)).
|
||||
Filter("tenant_id", tenantID).
|
||||
Filter("id__in", ids).
|
||||
Filter("delete_time__isnull", true).
|
||||
All(&rows)
|
||||
nameMap := make(map[uint64]string, len(rows))
|
||||
for _, r := range rows {
|
||||
nameMap[r.ID] = r.ClueName
|
||||
}
|
||||
for i := range list {
|
||||
if list[i].ClueID != nil {
|
||||
list[i].ClueName = nameMap[*list[i].ClueID]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user