功能:完善用户服务关系展示与组织联动

统一平台、气站和配送点后台的服务关系业务名称展示。
新增气站、配送点、服务人员三级联动及暂未分配语义。
后端补充组织归属、人员角色和启用状态的严格校验。
补充前后端测试、项目文档、操作日志及本机日志忽略规则。
This commit is contained in:
czl231
2026-08-12 00:37:58 +08:00
parent a37b147f5f
commit 6bcde94388
24 changed files with 682 additions and 27 deletions

View File

@@ -19,6 +19,13 @@ type staffListFilters struct {
WorkStatus string
}
type staffOrganizationFilters struct {
GasIdentity string
DeliveryIdentity string
GasUnassigned bool
DeliveryUnassigned bool
}
// parseStaffListFilters 将工作人员列表查询参数收敛为闭集条件,拒绝含糊或冲突值。
func parseStaffListFilters(roleCode, roleCodes, status, workStatus string) (staffListFilters, bool) {
filters := staffListFilters{}
@@ -61,6 +68,34 @@ func parseStaffListFilters(roleCode, roleCodes, status, workStatus string) (staf
return filters, true
}
// parseStaffOrganizationFilters 解析服务关系下拉使用的精确组织范围,并拒绝相互冲突的条件。
func parseStaffOrganizationFilters(gasIdentity, deliveryIdentity, gasUnassigned, deliveryUnassigned string) (staffOrganizationFilters, bool) {
filters := staffOrganizationFilters{
GasIdentity: strings.TrimSpace(gasIdentity),
DeliveryIdentity: strings.TrimSpace(deliveryIdentity),
}
if strings.Contains(filters.GasIdentity, ",") || strings.Contains(filters.DeliveryIdentity, ",") {
return staffOrganizationFilters{}, false
}
parseUnassigned := func(value string) (bool, bool) {
value = strings.TrimSpace(value)
if value == "" {
return false, true
}
return value == "1", value == "1"
}
var ok bool
filters.GasUnassigned, ok = parseUnassigned(gasUnassigned)
if !ok {
return staffOrganizationFilters{}, false
}
filters.DeliveryUnassigned, ok = parseUnassigned(deliveryUnassigned)
if !ok || filters.GasIdentity != "" && filters.GasUnassigned || filters.DeliveryIdentity != "" && filters.DeliveryUnassigned {
return staffOrganizationFilters{}, false
}
return filters, true
}
// ListStaff 查询服务人员分页列表,并应用调用方显式声明的角色与在岗状态条件。
func ListStaff(ctx *gin.Context) {
filters, ok := parseStaffListFilters(
@@ -73,6 +108,16 @@ func ListStaff(ctx *gin.Context) {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
organizationFilters, ok := parseStaffOrganizationFilters(
ctx.Query("gas_basic_identities"),
ctx.Query("delivery_basic_identities"),
ctx.Query("gas_basic_unassigned"),
ctx.Query("delivery_basic_unassigned"),
)
if !ok {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
page, size := common.PageSize(ctx)
var list []models.StaffAccount
var total int64
@@ -86,6 +131,24 @@ func ListStaff(ctx *gin.Context) {
if filters.WorkStatus != "" {
query = query.Where("work_status = ?", filters.WorkStatus)
}
if organizationFilters.GasIdentity != "" {
query = query.Where(
"gas_basic_id IN (SELECT id FROM gas_basic WHERE identity = ? AND status <> ?)",
organizationFilters.GasIdentity,
common.StatusArchived,
)
} else if organizationFilters.GasUnassigned {
query = query.Where("gas_basic_id = 0")
}
if organizationFilters.DeliveryIdentity != "" {
query = query.Where(
"delivery_basic_id IN (SELECT id FROM delivery_basic WHERE identity = ? AND status <> ?)",
organizationFilters.DeliveryIdentity,
common.StatusArchived,
)
} else if organizationFilters.DeliveryUnassigned {
query = query.Where("delivery_basic_id = 0")
}
query = common.ApplyKeywordFilter(ctx, query, &models.StaffAccount{})
if err := query.Count(&total).Error; err != nil {
infra.Response.Error(ctx, err)

View File

@@ -53,3 +53,25 @@ func TestStaffListFiltersRejectAmbiguousOrUnknownValues(t *testing.T) {
}
}
}
// TestStaffOrganizationFilters 验证服务人员可以按已分配组织或未分配状态精确过滤。
func TestStaffOrganizationFilters(t *testing.T) {
filters, ok := parseStaffOrganizationFilters("gas-a", "delivery-a", "", "")
if !ok || filters.GasIdentity != "gas-a" || filters.DeliveryIdentity != "delivery-a" {
t.Fatalf("组织标识过滤解析失败:%#v", filters)
}
filters, ok = parseStaffOrganizationFilters("", "", "1", "1")
if !ok || !filters.GasUnassigned || !filters.DeliveryUnassigned {
t.Fatalf("未分配过滤解析失败:%#v", filters)
}
for _, test := range [][4]string{
{"gas-a", "", "1", ""},
{"", "delivery-a", "", "1"},
{"gas-a,gas-b", "", "", ""},
{"", "", "true", ""},
} {
if _, valid := parseStaffOrganizationFilters(test[0], test[1], test[2], test[3]); valid {
t.Fatalf("冲突或非法组织过滤被接受:%#v", test)
}
}
}