package controllers import ( "encoding/json" "fmt" "io" "strings" "time" "server/models" "server/pkg/jwtutil" "github.com/beego/beego/v2/client/orm" beego "github.com/beego/beego/v2/server/web" ) // BackendCrmPoolController 客户公海控制器 // 公海语义:in_pool=1 的客户处于公海中(租户共享,无个人负责人); // 移入公海会置 in_pool=1 并释放负责人,领取/分配/移出会置 in_pool=0 并绑定负责人。 type BackendCrmPoolController struct { beego.Controller } func (c *BackendCrmPoolController) poolClaims() (*jwtutil.Claims, error) { auth := c.Ctx.Request.Header.Get("Authorization") if auth == "" { return nil, fmt.Errorf("未登录") } parts := strings.SplitN(auth, " ", 2) if len(parts) != 2 || parts[0] != "Bearer" { return nil, fmt.Errorf("认证信息格式错误") } claims, err := jwtutil.ParseToken(parts[1]) if err != nil { return nil, fmt.Errorf("无效的token") } if claims.UserType != "backend" { return nil, fmt.Errorf("无权访问") } return claims, nil } func (c *BackendCrmPoolController) poolJsonErr(httpStatus, bizCode int, msg string) { c.Ctx.Output.SetStatus(httpStatus) c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg} _ = c.ServeJSON() } func (c *BackendCrmPoolController) poolOk(data interface{}) { c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": data} _ = c.ServeJSON() } // List GET /backend/crm/pool/list 公海列表(无负责人、租户内共享) func (c *BackendCrmPoolController) List() { claims, err := c.poolClaims() if err != nil { c.poolJsonErr(401, 401, err.Error()) return } page, _ := c.GetInt("page", 1) pageSize, _ := c.GetInt("pageSize", 20) keyword := strings.TrimSpace(c.GetString("keyword")) customerType := strings.TrimSpace(c.GetString("customer_type")) customerLevel := strings.TrimSpace(c.GetString("customer_level")) startTime := strings.TrimSpace(c.GetString("start_time")) endTime := strings.TrimSpace(c.GetString("end_time")) if page < 1 { page = 1 } if pageSize < 1 || pageSize > 100 { pageSize = 20 } cond := orm.NewCondition(). And("tenant_id", fmt.Sprintf("%d", claims.TenantId)). And("delete_time__isnull", true). And("in_pool", 1) if keyword != "" { kw := orm.NewCondition(). Or("customer_name__contains", keyword). Or("contact_person__contains", keyword). Or("contact_phone__contains", keyword) cond = cond.AndCond(kw) } if customerType != "" { cond = cond.And("customer_type", customerType) } if customerLevel != "" { cond = cond.And("customer_level", customerLevel) } if startTime != "" { cond = cond.And("pool_in_time__gte", startTime) } if endTime != "" { cond = cond.And("pool_in_time__lte", endTime+" 23:59:59") } qs := models.Orm.QueryTable(new(models.TenantCrmCustomer)).SetCond(cond) total, _ := qs.Count() var list []models.TenantCrmCustomer if total > 0 { _, _ = qs.OrderBy("-pool_in_time").Offset((page - 1) * pageSize).Limit(pageSize).All(&list) } c.poolOk(map[string]interface{}{ "list": list, "total": total, "page": page, "pageSize": pageSize, }) } // Move POST /backend/crm/pool/move 移入公海:释放负责人,记录原负责人与原因 func (c *BackendCrmPoolController) Move() { claims, err := c.poolClaims() if err != nil { c.poolJsonErr(401, 401, err.Error()) return } var p struct { IDs []uint64 `json:"ids"` Reason string `json:"reason"` } raw, _ := io.ReadAll(c.Ctx.Request.Body) if err := json.Unmarshal(raw, &p); err != nil { c.poolJsonErr(400, 400, "参数错误") return } if len(p.IDs) == 0 { c.poolJsonErr(400, 400, "请选择要移入公海客户") return } reason := strings.TrimSpace(p.Reason) if reason == "" { c.poolJsonErr(400, 400, "请填写移入原因") return } now := time.Now() updated := 0 for _, id := range p.IDs { var cust models.TenantCrmCustomer err := models.Orm.QueryTable(new(models.TenantCrmCustomer)). Filter("id", id). Filter("tenant_id", fmt.Sprintf("%d", claims.TenantId)). Filter("delete_time__isnull", true). One(&cust) if err != nil { continue } // 记录原负责人(移入公海前的负责人),再清空负责人并置入公海 cust.LastOwnerName = cust.OwnerUserName cust.OwnerUserID = "" cust.OwnerUserName = "" cust.InPool = 1 cust.PoolReason = reason cust.PoolInTime = &now cust.UpdateTime = now if _, err := models.Orm.Update(&cust, "last_owner_name", "owner_user_id", "owner_user_name", "in_pool", "pool_reason", "pool_in_time", "update_time"); err == nil { updated++ } } c.poolOk(map[string]interface{}{"updated": updated}) } // Claim POST /backend/crm/pool/claim 领取:将公海客户归属为当前登录用户 func (c *BackendCrmPoolController) Claim() { claims, err := c.poolClaims() if err != nil { c.poolJsonErr(401, 401, err.Error()) return } var p struct { IDs []uint64 `json:"ids"` } raw, _ := io.ReadAll(c.Ctx.Request.Body) if err := json.Unmarshal(raw, &p); err != nil { c.poolJsonErr(400, 400, "参数错误") return } if len(p.IDs) == 0 { c.poolJsonErr(400, 400, "请选择客户") return } now := time.Now() uid := fmt.Sprintf("%d", claims.UserID) updated := 0 for _, id := range p.IDs { var cust models.TenantCrmCustomer err := models.Orm.QueryTable(new(models.TenantCrmCustomer)). Filter("id", id). Filter("tenant_id", fmt.Sprintf("%d", claims.TenantId)). Filter("delete_time__isnull", true). One(&cust) if err != nil { continue } cust.OwnerUserID = uid cust.OwnerUserName = resolveUserName(claims) cust.InPool = 0 cust.PoolInTime = nil cust.PoolReason = "" cust.UpdateTime = now if _, err := models.Orm.Update(&cust, "owner_user_id", "owner_user_name", "in_pool", "pool_in_time", "pool_reason", "update_time"); err == nil { updated++ } } c.poolOk(map[string]interface{}{"updated": updated}) } // Assign POST /backend/crm/pool/assign 分配:将公海客户分配给指定负责人 func (c *BackendCrmPoolController) Assign() { claims, err := c.poolClaims() if err != nil { c.poolJsonErr(401, 401, err.Error()) return } var p struct { IDs []uint64 `json:"ids"` OwnerUserID interface{} `json:"owner_user_id"` OwnerUserName string `json:"owner_user_name"` } raw, _ := io.ReadAll(c.Ctx.Request.Body) if err := json.Unmarshal(raw, &p); err != nil { c.poolJsonErr(400, 400, "参数错误") return } if len(p.IDs) == 0 { c.poolJsonErr(400, 400, "请选择客户") return } ownerID := strings.TrimSpace(fmt.Sprintf("%v", p.OwnerUserID)) if ownerID == "" { c.poolJsonErr(400, 400, "请指定负责人") return } now := time.Now() updated := 0 for _, id := range p.IDs { var cust models.TenantCrmCustomer err := models.Orm.QueryTable(new(models.TenantCrmCustomer)). Filter("id", id). Filter("tenant_id", fmt.Sprintf("%d", claims.TenantId)). Filter("delete_time__isnull", true). One(&cust) if err != nil { continue } cust.OwnerUserID = ownerID cust.OwnerUserName = strings.TrimSpace(p.OwnerUserName) cust.InPool = 0 cust.PoolInTime = nil cust.PoolReason = "" cust.UpdateTime = now if _, err := models.Orm.Update(&cust, "owner_user_id", "owner_user_name", "in_pool", "pool_in_time", "pool_reason", "update_time"); err == nil { updated++ } } c.poolOk(map[string]interface{}{"updated": updated}) }