功能:完善用户服务关系展示与组织联动
统一平台、气站和配送点后台的服务关系业务名称展示。 新增气站、配送点、服务人员三级联动及暂未分配语义。 后端补充组织归属、人员角色和启用状态的严格校验。 补充前后端测试、项目文档、操作日志及本机日志忽略规则。
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,10 +226,38 @@ func resolveServiceRelation(ctx *gin.Context, request serviceRelationRequest) (u
|
||||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||||
return 0, 0, 0, 0, false
|
||||
}
|
||||
if gasBasicID == 0 && deliveryBasicID == 0 && staffAccountID == 0 ||
|
||||
!common.ValidateOrganizationIDs(gasBasicID, deliveryBasicID, staffAccountID) {
|
||||
if !validateServiceRelationOrganization(gasBasicID, deliveryBasicID, staffAccountID) {
|
||||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||||
return 0, 0, 0, 0, false
|
||||
}
|
||||
return userAccountID, gasBasicID, deliveryBasicID, staffAccountID, true
|
||||
}
|
||||
|
||||
// validateServiceRelationOrganization 读取组织记录并执行服务关系专用的严格归属校验。
|
||||
func validateServiceRelationOrganization(gasBasicID, deliveryBasicID, staffAccountID uint64) bool {
|
||||
var delivery models.DeliveryBasic
|
||||
if deliveryBasicID != 0 {
|
||||
if err := common.ActiveRecords(impl.DBService).First(&delivery, deliveryBasicID).Error; err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
var staff models.StaffAccount
|
||||
if staffAccountID != 0 {
|
||||
if err := common.ActiveRecords(impl.DBService).First(&staff, staffAccountID).Error; err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return serviceRelationOrganizationMatches(gasBasicID, deliveryBasicID, staffAccountID, delivery, staff)
|
||||
}
|
||||
|
||||
// serviceRelationOrganizationMatches 要求配送点、服务人员与当前选择完全一致;全空表示暂未分配。
|
||||
func serviceRelationOrganizationMatches(gasBasicID, deliveryBasicID, staffAccountID uint64, delivery models.DeliveryBasic, staff models.StaffAccount) bool {
|
||||
if deliveryBasicID != 0 && delivery.GasBasicID != gasBasicID {
|
||||
return false
|
||||
}
|
||||
if staffAccountID == 0 {
|
||||
return true
|
||||
}
|
||||
validRole := staff.RoleCode == "installer" || staff.RoleCode == "delivery" || staff.RoleCode == "operations"
|
||||
return validRole && staff.Status == common.StatusEnable && staff.GasBasicID == gasBasicID && staff.DeliveryBasicID == deliveryBasicID
|
||||
}
|
||||
|
||||
@@ -65,3 +65,30 @@ func TestUserAddressUpdateValuesUsesSupportedMap(t *testing.T) {
|
||||
t.Fatalf("update field count = %d, want 5", len(values))
|
||||
}
|
||||
}
|
||||
|
||||
// TestServiceRelationOrganizationMatches 验证暂未分配与严格组织一致性规则。
|
||||
func TestServiceRelationOrganizationMatches(t *testing.T) {
|
||||
if !serviceRelationOrganizationMatches(0, 0, 0, models.DeliveryBasic{}, models.StaffAccount{}) {
|
||||
t.Fatal("全空服务关系应允许保存为暂未分配")
|
||||
}
|
||||
delivery := models.DeliveryBasic{GasBasicID: 10}
|
||||
staff := models.StaffAccount{
|
||||
Entity: models.Entity{Status: 1},
|
||||
RoleCode: "delivery",
|
||||
GasBasicID: 10,
|
||||
DeliveryBasicID: 20,
|
||||
}
|
||||
if !serviceRelationOrganizationMatches(10, 20, 30, delivery, staff) {
|
||||
t.Fatal("完全一致的服务关系被拒绝")
|
||||
}
|
||||
if serviceRelationOrganizationMatches(11, 20, 30, delivery, staff) {
|
||||
t.Fatal("配送点或服务人员气站不一致时不应通过")
|
||||
}
|
||||
if serviceRelationOrganizationMatches(10, 0, 30, models.DeliveryBasic{}, staff) {
|
||||
t.Fatal("服务人员配送点与空选择不一致时不应通过")
|
||||
}
|
||||
staff.Status = 2
|
||||
if serviceRelationOrganizationMatches(10, 20, 30, delivery, staff) {
|
||||
t.Fatal("停用服务人员不应写入服务关系")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user