更新若干功能

This commit is contained in:
hero920103 committed 2026-10-21 17:45:50 +08:00
1 parent 892ba5886d
commit 79d5ae6b66
45 files changed
+6755 -38

No files matched your search

+80
View File
@@ -348,6 +348,10 @@ func (c *BackendCrmBusinessController) ChangeStage() {
pipelineErr(&c.Controller, 400, 400, "商机已转化并锁定,不能流转阶段")
return
}
if biz.Status == 3 {
pipelineErr(&c.Controller, 400, 400, "商机已废弃,请先恢复为「跟进中」再流转阶段")
return
}
// 阶段未变化:直接幂等返回,不写日志
if strings.TrimSpace(biz.Stage) == stage {
pipelineOk(&c.Controller, map[string]interface{}{"id": biz.ID, "stage": biz.Stage})
@@ -364,6 +368,78 @@ func (c *BackendCrmBusinessController) ChangeStage() {
pipelineOk(&c.Controller, map[string]interface{}{"id": biz.ID, "stage": stage})
}
// ChangeStatus POST /backend/crm/business/:id/status
// 商机状态流转:1跟进中 / 3已废弃。
// 「已转化项目(2)」不走本接口,须用「转项目」完成(会锁定商机)。
func (c *BackendCrmBusinessController) ChangeStatus() {
claims, err := pipelineClaims(&c.Controller)
if err != nil {
code := identity.Status(err)
pipelineErr(&c.Controller, code, code, 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 struct {
Status int8 `json:"status"`
}
if err := json.Unmarshal(raw, &p); err != nil {
pipelineErr(&c.Controller, 400, 400, "参数错误")
return
}
if p.Status != 1 && p.Status != 3 {
pipelineErr(&c.Controller, 400, 400, "仅支持流转为「跟进中 / 已废弃」,转为已转化项目请使用「转项目」")
return
}
tenantID := pipelineTenantID(claims)
var biz models.TenantCrmBusiness
if err := models.Orm.QueryTable(new(models.TenantCrmBusiness)).
Filter("id", id).Filter("tenant_id", tenantID).
Filter("delete_time__isnull", true).One(&biz); err != nil {
pipelineErr(&c.Controller, 404, 404, "商机未找到")
return
}
// 数据范围校验:非管理员仅能操作本人(负责人或创建人)的数据
if !canAccessCrmRecord(claims, biz.OwnerUserID, biz.CreateUserID) {
pipelineErr(&c.Controller, 403, 403, "无权操作该商机数据")
return
}
if biz.Locked == 1 || biz.Status == 2 {
pipelineErr(&c.Controller, 400, 400, "商机已转化并锁定,不能流转状态")
return
}
// 状态未变化:直接幂等返回,不写日志
if biz.Status == p.Status {
pipelineOk(&c.Controller, map[string]interface{}{"id": biz.ID, "status": biz.Status})
return
}
if _, err := models.Orm.QueryTable(new(models.TenantCrmBusiness)).
Filter("id", id).Filter("tenant_id", tenantID).
Update(map[string]interface{}{"status": p.Status, "update_time": time.Now()}); err != nil {
pipelineErr(&c.Controller, 500, 500, "状态更新失败: "+err.Error())
return
}
crmWriteLog(tenantID, 2, biz.ID, "status",
fmt.Sprintf("商机状态:%s → %s", businessStatusName(biz.Status), businessStatusName(p.Status)), claims)
pipelineOk(&c.Controller, map[string]interface{}{"id": biz.ID, "status": p.Status})
}
// businessStatusName 商机状态文案(用于操作日志):1跟进中 / 2已转化项目 / 3已废弃
func businessStatusName(s int8) string {
switch s {
case 2:
return "已转化项目"
case 3:
return "已废弃"
default:
return "跟进中"
}
}
// businessStageName 商机阶段文案(用于操作日志):1初步接洽 … 5赢单 / 6输单
func businessStageName(s string) string {
switch strings.TrimSpace(s) {
@@ -480,6 +556,10 @@ func (c *BackendCrmBusinessController) Convert() {
pipelineErr(&c.Controller, 400, 400, "商机已转化,不能重复转化")
return
}
if biz.Status == 3 {
pipelineErr(&c.Controller, 400, 400, "商机已废弃,请先恢复为「跟进中」再转项目")
return
}
projectName := strings.TrimSpace(p.ProjectName)
if projectName == "" {