优化项目管理

This commit is contained in:
2026-09-13 22:31:42 +08:00
parent 2f6cdbb3cd
commit a8c09c1db5
30 changed files with 1654 additions and 346 deletions
+31 -1
View File
@@ -25,7 +25,8 @@ type cluePayload struct {
Source string `json:"source"`
OwnerUserID string `json:"owner_user_id"`
OwnerUserName string `json:"owner_user_name"`
Industry string `json:"industry"`
Industry string `json:"industry"`
Amount float64 `json:"amount"` // 预计金额
NextContactTime string `json:"next_contact_time"`
ClueLevel string `json:"clue_level"`
ContactPerson string `json:"contact_person"`
@@ -61,6 +62,8 @@ func (c *BackendCrmClueController) List() {
tenantID := pipelineTenantID(claims)
cond := orm.NewCondition().And("tenant_id", tenantID).And("delete_time__isnull", true)
// 数据范围:平台/租户管理员可见全部,其他用户仅本人(负责人或创建人)
cond = crmApplyOwnerScope(cond, claims)
if keyword != "" {
kw := orm.NewCondition().
Or("clue_name__contains", keyword).
@@ -114,6 +117,11 @@ func (c *BackendCrmClueController) Detail() {
pipelineErr(&c.Controller, 404, 404, "线索未找到")
return
}
// 数据范围校验:非管理员仅能操作本人(负责人或创建人)的数据
if !canAccessCrmRecord(claims, clue.OwnerUserID, clue.CreateUserID) {
pipelineErr(&c.Controller, 403, 403, "无权操作该线索数据")
return
}
pipelineOk(&c.Controller, clue)
}
@@ -161,6 +169,7 @@ func (c *BackendCrmClueController) Create() {
OwnerUserID: ownerID,
OwnerUserName: ownerName,
Industry: strings.TrimSpace(p.Industry),
Amount: p.Amount,
NextContactTime: parsePipelineDateTime(p.NextContactTime),
ClueLevel: level,
ContactPerson: strings.TrimSpace(p.ContactPerson),
@@ -176,6 +185,11 @@ func (c *BackendCrmClueController) Create() {
CreateTime: now,
UpdateTime: now,
}
// 唯一性校验:同租户内不允许重复创建同名线索(重复数据请联系管理员转移跟进)
if crmNameDuplicated(new(models.TenantCrmClue), clue.TenantID, "clue_name", clue.ClueName) {
pipelineErr(&c.Controller, 409, 409, "该线索名称已存在(可能由其他同事创建),如需继续跟进请联系管理员转移")
return
}
id, err := models.Orm.Insert(&clue)
if err != nil {
pipelineErr(&c.Controller, 500, 500, "创建失败: "+err.Error())
@@ -212,6 +226,11 @@ func (c *BackendCrmClueController) Update() {
pipelineErr(&c.Controller, 404, 404, "线索未找到")
return
}
// 数据范围校验:非管理员仅能操作本人(负责人或创建人)的数据
if !canAccessCrmRecord(claims, clue.OwnerUserID, clue.CreateUserID) {
pipelineErr(&c.Controller, 403, 403, "无权操作该线索数据")
return
}
if clue.Locked == 1 {
pipelineErr(&c.Controller, 400, 400, "线索已转化并锁定,不能编辑")
return
@@ -231,6 +250,7 @@ func (c *BackendCrmClueController) Update() {
clue.OwnerUserName = strings.TrimSpace(p.OwnerUserName)
}
clue.Industry = strings.TrimSpace(p.Industry)
clue.Amount = p.Amount
clue.NextContactTime = parsePipelineDateTime(p.NextContactTime)
if strings.TrimSpace(p.ClueLevel) != "" {
clue.ClueLevel = strings.TrimSpace(p.ClueLevel)
@@ -272,6 +292,11 @@ func (c *BackendCrmClueController) Delete() {
pipelineErr(&c.Controller, 404, 404, "线索未找到")
return
}
// 数据范围校验:非管理员仅能操作本人(负责人或创建人)的数据
if !canAccessCrmRecord(claims, clue.OwnerUserID, clue.CreateUserID) {
pipelineErr(&c.Controller, 403, 403, "无权操作该线索数据")
return
}
if clue.Locked == 1 || clue.Status == 2 {
pipelineErr(&c.Controller, 400, 400, "线索已转化并锁定,不能删除")
return
@@ -340,6 +365,11 @@ func (c *BackendCrmClueController) Convert() {
pipelineErr(&c.Controller, 404, 404, "线索未找到")
return
}
// 数据范围校验:非管理员仅能操作本人(负责人或创建人)的数据
if !canAccessCrmRecord(claims, clue.OwnerUserID, clue.CreateUserID) {
pipelineErr(&c.Controller, 403, 403, "无权操作该线索数据")
return
}
if clue.Locked == 1 || clue.Status == 2 {
pipelineErr(&c.Controller, 400, 400, "线索已转化,不能重复转化")
return