package controllers import ( "encoding/json" "io" "strconv" "strings" "time" "server/models" "github.com/beego/beego/v2/client/orm" beego "github.com/beego/beego/v2/server/web" ) // BackendCrmProjectController CRM 项目管理 type BackendCrmProjectController struct { beego.Controller } type projectPayload struct { ProjectName string `json:"project_name"` ProjectNo string `json:"project_no"` CustomerID uint64 `json:"customer_id"` CustomerName string `json:"customer_name"` OwnerUserID string `json:"owner_user_id"` OwnerUserName string `json:"owner_user_name"` Industry string `json:"industry"` Amount float64 `json:"amount"` Status int8 `json:"status"` StartDate string `json:"start_date"` EndDate string `json:"end_date"` ContactPerson string `json:"contact_person"` ContactPosition string `json:"contact_position"` ContactPhone string `json:"contact_phone"` Address string `json:"address"` Remark string `json:"remark"` } // List GET /backend/crm/project/list func (c *BackendCrmProjectController) List() { claims, err := pipelineClaims(&c.Controller) if err != nil { pipelineErr(&c.Controller, 401, 401, err.Error()) return } page, _ := c.GetInt("page", 1) pageSize, _ := c.GetInt("pageSize", 20) if page < 1 { page = 1 } if pageSize < 1 || pageSize > 100 { pageSize = 20 } keyword := strings.TrimSpace(c.GetString("keyword")) status := strings.TrimSpace(c.GetString("status")) ownerID := strings.TrimSpace(c.GetString("owner_user_id")) tenantID := pipelineTenantID(claims) cond := orm.NewCondition().And("tenant_id", tenantID).And("delete_time__isnull", true) if keyword != "" { kw := orm.NewCondition(). Or("project_name__contains", keyword). Or("project_no__contains", keyword). Or("customer_name__contains", keyword) cond = cond.AndCond(kw) } if status != "" { cond = cond.And("status", status) } if ownerID != "" { cond = cond.And("owner_user_id", ownerID) } qs := models.Orm.QueryTable(new(models.TenantCrmProject)).SetCond(cond) total, _ := qs.Count() var list []models.TenantCrmProject if total > 0 { _, _ = qs.OrderBy("-id").Offset((page - 1) * pageSize).Limit(pageSize).All(&list) } pipelineOk(&c.Controller, map[string]interface{}{ "list": list, "total": total, "page": page, "pageSize": pageSize, }) } // Detail GET /backend/crm/project/:id func (c *BackendCrmProjectController) Detail() { claims, err := pipelineClaims(&c.Controller) if err != nil { pipelineErr(&c.Controller, 401, 401, err.Error()) return } id, _ := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64) if id == 0 { pipelineErr(&c.Controller, 400, 400, "无效的ID") return } var proj models.TenantCrmProject err = models.Orm.QueryTable(new(models.TenantCrmProject)). Filter("id", id).Filter("tenant_id", pipelineTenantID(claims)). Filter("delete_time__isnull", true).One(&proj) if err != nil { pipelineErr(&c.Controller, 404, 404, "项目未找到") return } pipelineOk(&c.Controller, proj) } // Create POST /backend/crm/project func (c *BackendCrmProjectController) Create() { claims, err := pipelineClaims(&c.Controller) if err != nil { pipelineErr(&c.Controller, 401, 401, err.Error()) return } raw, _ := io.ReadAll(c.Ctx.Request.Body) var p projectPayload if err := json.Unmarshal(raw, &p); err != nil { pipelineErr(&c.Controller, 400, 400, "参数错误") return } if strings.TrimSpace(p.ProjectName) == "" { pipelineErr(&c.Controller, 400, 400, "项目名称不能为空") return } tenantID := pipelineTenantID(claims) var custIDVal *uint64 customerName := strings.TrimSpace(p.CustomerName) if p.CustomerID > 0 { var cust models.TenantCrmCustomer if err := models.Orm.QueryTable(new(models.TenantCrmCustomer)). Filter("id", p.CustomerID).Filter("tenant_id", tenantID). Filter("delete_time__isnull", true).One(&cust); err == nil { cid := cust.ID custIDVal = &cid customerName = cust.CustomerName } } status := p.Status if status == 0 { status = 1 } now := time.Now() proj := models.TenantCrmProject{ TenantID: tenantID, ProjectName: strings.TrimSpace(p.ProjectName), ProjectNo: strings.TrimSpace(p.ProjectNo), CustomerID: custIDVal, CustomerName: customerName, OwnerUserID: firstNonEmpty(p.OwnerUserID, pipelineUID(claims)), OwnerUserName: firstNonEmpty(p.OwnerUserName, claims.Username), Industry: strings.TrimSpace(p.Industry), Amount: p.Amount, Status: status, StartDate: parsePipelineDate(p.StartDate), EndDate: parsePipelineDate(p.EndDate), ContactPerson: strings.TrimSpace(p.ContactPerson), ContactPosition: strings.TrimSpace(p.ContactPosition), ContactPhone: strings.TrimSpace(p.ContactPhone), Address: strings.TrimSpace(p.Address), Remark: p.Remark, CreateUserID: pipelineUID(claims), CreateTime: now, UpdateTime: now, } id, err := models.Orm.Insert(&proj) if err != nil { pipelineErr(&c.Controller, 500, 500, "创建失败: "+err.Error()) return } crmWriteLog(tenantID, 3, uint64(id), "create", "创建项目:"+proj.ProjectName, claims) pipelineOk(&c.Controller, map[string]interface{}{"id": id}) } // Update PUT /backend/crm/project/:id func (c *BackendCrmProjectController) Update() { claims, err := pipelineClaims(&c.Controller) if err != nil { pipelineErr(&c.Controller, 401, 401, err.Error()) return } id, _ := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64) if id == 0 { pipelineErr(&c.Controller, 400, 400, "无效的ID") return } raw, _ := io.ReadAll(c.Ctx.Request.Body) var p projectPayload if err := json.Unmarshal(raw, &p); err != nil { pipelineErr(&c.Controller, 400, 400, "参数错误") return } tenantID := pipelineTenantID(claims) var proj models.TenantCrmProject if err := models.Orm.QueryTable(new(models.TenantCrmProject)). Filter("id", id).Filter("tenant_id", tenantID). Filter("delete_time__isnull", true).One(&proj); err != nil { pipelineErr(&c.Controller, 404, 404, "项目未找到") return } if strings.TrimSpace(p.ProjectName) == "" { pipelineErr(&c.Controller, 400, 400, "项目名称不能为空") return } proj.ProjectName = strings.TrimSpace(p.ProjectName) proj.ProjectNo = strings.TrimSpace(p.ProjectNo) if p.CustomerID > 0 { var cust models.TenantCrmCustomer if err := models.Orm.QueryTable(new(models.TenantCrmCustomer)). Filter("id", p.CustomerID).Filter("tenant_id", tenantID). Filter("delete_time__isnull", true).One(&cust); err == nil { cid := cust.ID proj.CustomerID = &cid proj.CustomerName = cust.CustomerName } } else if strings.TrimSpace(p.CustomerName) != "" { proj.CustomerName = strings.TrimSpace(p.CustomerName) } if strings.TrimSpace(p.OwnerUserID) != "" { proj.OwnerUserID = strings.TrimSpace(p.OwnerUserID) } if strings.TrimSpace(p.OwnerUserName) != "" { proj.OwnerUserName = strings.TrimSpace(p.OwnerUserName) } proj.Industry = strings.TrimSpace(p.Industry) proj.Amount = p.Amount if p.Status != 0 { proj.Status = p.Status } proj.StartDate = parsePipelineDate(p.StartDate) proj.EndDate = parsePipelineDate(p.EndDate) proj.ContactPerson = strings.TrimSpace(p.ContactPerson) proj.ContactPosition = strings.TrimSpace(p.ContactPosition) proj.ContactPhone = strings.TrimSpace(p.ContactPhone) proj.Address = strings.TrimSpace(p.Address) proj.Remark = p.Remark proj.UpdateTime = time.Now() if _, err := models.Orm.Update(&proj); err != nil { pipelineErr(&c.Controller, 500, 500, "更新失败: "+err.Error()) return } crmWriteLog(tenantID, 3, proj.ID, "update", "更新项目:"+proj.ProjectName, claims) pipelineOk(&c.Controller, map[string]interface{}{"id": proj.ID}) } // Delete DELETE /backend/crm/project/:id func (c *BackendCrmProjectController) Delete() { claims, err := pipelineClaims(&c.Controller) if err != nil { pipelineErr(&c.Controller, 401, 401, err.Error()) return } id, _ := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64) if id == 0 { pipelineErr(&c.Controller, 400, 400, "无效的ID") return } tenantID := pipelineTenantID(claims) var proj models.TenantCrmProject if err := models.Orm.QueryTable(new(models.TenantCrmProject)). Filter("id", id).Filter("tenant_id", tenantID). Filter("delete_time__isnull", true).One(&proj); err != nil { pipelineErr(&c.Controller, 404, 404, "项目未找到") return } if !canDeleteCrmRecord(claims, proj.CreateUserID) { pipelineErr(&c.Controller, 403, 403, "只有创建人、租户管理员或平台管理员可以删除该项目") return } now := time.Now() _, err = models.Orm.QueryTable(new(models.TenantCrmProject)). Filter("id", id).Filter("tenant_id", tenantID). Update(map[string]interface{}{"delete_time": now, "update_time": now}) if err != nil { pipelineErr(&c.Controller, 500, 500, "删除失败: "+err.Error()) return } pipelineOk(&c.Controller, nil) }