销假填充与 normalize 打架:天气字段不再被地址污染

自审本轮改动时发现的不一致:销假路径先按标签用地址兜底填充,
再调用 normalizeXsgzFields,而后者对 regions 是「清空 value」。
两者叠加导致:
  - 若字段名含"原因/说明"且是 regions 类型,地址先被写入、又被清空,
    等于白填,且语义上互相矛盾;
  - 上传类字段同样可能先被写入地址,再被 delete(value)。

请假路径的 fillXsgzFieldValues 已跳过这两类,销假这段循环没跟上。

改动:销假填充循环加 xsgzNonTextFillable 判断,与请假路径保持一致。
测试:一个同时含 regions / imageUpload / TEXT 的销假表单,
断言文本字段得到地址、地区字段为空、上传字段无 value。
This commit is contained in:
becomer
2026-10-17 18:26:27 +08:00
parent b90a1dcd8f
commit 152878cc7c
2 changed files with 46 additions and 3 deletions
+8 -2
View File
@@ -528,14 +528,20 @@ func executeXsgzLeaveCancel(c *fiber.Ctx, user *model.SysUser, req *ExecuteLeave
slog.Warn("xsgz 获取销假表单字段失败,按空字段提交", "userId", user.ID, "error", ffErr) slog.Warn("xsgz 获取销假表单字段失败,按空字段提交", "userId", user.ID, "error", ffErr)
formFields = nil formFields = nil
} }
// 销假表单通常含"销假说明/原因",用地址兜底填充可见字段 // 销假表单通常含"销假说明/原因",用地址兜底填充可见字段。
// 注意:上传类与地区类字段不能被文本值填充(地区类需要
// 「选中值,详情」拼接串,写地址会被学校端当成非法地区),
// 这两类在下面的 normalizeXsgzFields 里另有处理。
for _, f := range formFields { for _, f := range formFields {
if xsgzNonTextFillable(mapStr(f, "componentType")) {
continue
}
label := strings.ToLower(mapStr(f, "fieldZh") + mapStr(f, "fieldName") + mapStr(f, "label")) label := strings.ToLower(mapStr(f, "fieldZh") + mapStr(f, "fieldName") + mapStr(f, "label"))
if (strings.Contains(label, "说明") || strings.Contains(label, "原因")) && req.Address != "" { if (strings.Contains(label, "说明") || strings.Contains(label, "原因")) && req.Address != "" {
f["value"] = req.Address f["value"] = req.Address
} }
} }
// 按前端 Ie() 语义清洗:上传类去 value、多选数组 join(",") // 按前端 Ie() 语义清洗:上传类去 value、地区类清空、多选数组 join(",")
normalizeXsgzFields(formFields) normalizeXsgzFields(formFields)
// 提交前预检:必填项无法自动填写时明确指出,避免含糊失败 // 提交前预检:必填项无法自动填写时明确指出,避免含糊失败
if missing := checkXsgzUnfilledRequired(formFields); len(missing) > 0 { if missing := checkXsgzUnfilledRequired(formFields); len(missing) > 0 {
@@ -1,6 +1,9 @@
package handler package handler
import "testing" import (
"strings"
"testing"
)
// fillXsgzFieldValues:优先按标签匹配,未命中时用必填文本字段兜底。 // fillXsgzFieldValues:优先按标签匹配,未命中时用必填文本字段兜底。
func TestFillXsgzFieldValues(t *testing.T) { func TestFillXsgzFieldValues(t *testing.T) {
@@ -221,3 +224,37 @@ func TestRegionsRequiredTriggersPrecheck(t *testing.T) {
t.Fatalf("必填 regions 无值应被预检拦下,实际: %v", missing) t.Fatalf("必填 regions 无值应被预检拦下,实际: %v", missing)
} }
} }
// 销假用地址兜底填充时,也不能污染上传类/地区类字段。
// 该用例防止「填充」与「normalize 清空」互相打架时留下脏值。
func TestCancelFillSkipsNonTextFields(t *testing.T) {
fields := []map[string]interface{}{
{"fieldId": "r1", "componentType": "regions", "fieldName": "销假原因"},
{"fieldId": "u1", "componentType": "imageUpload", "fieldName": "销假说明附件"},
{"fieldId": "t1", "componentType": "TEXT", "fieldName": "销假说明"},
}
addr := "山东省泰安市"
for _, f := range fields {
if xsgzNonTextFillable(mapStr(f, "componentType")) {
continue
}
label := strings.ToLower(mapStr(f, "fieldZh") + mapStr(f, "fieldName") + mapStr(f, "label"))
if strings.Contains(label, "说明") || strings.Contains(label, "原因") {
f["value"] = addr
}
}
normalizeXsgzFields(fields)
// 文本字段应保留地址
if fields[2]["value"] != addr {
t.Fatalf("文本字段应填入地址,实际 %v", fields[2]["value"])
}
// 地区字段应被清空(不是地址)
if got := fields[0]["value"]; got != "" {
t.Fatalf("地区字段应为空,实际 %v", got)
}
// 上传字段不应有 value
if _, ok := fields[1]["value"]; ok {
t.Fatalf("上传字段不应有 value,实际 %v", fields[1]["value"])
}
}