6bc7ab359c
前端 Ie() 对 regions 组件的处理(studentForm-Dx5Fpjfs.js):
s = (e[fieldId+"_regionsDetail"] ?? e[fieldId+"_detail"])
?.replace(/,/g,",")?.trim() || ""
o.value = 选中值 ? (s ? `${选中值},${s}` : 选中值) : (s || n)
即 value 是「选中值,详情」的拼接串,详情还会把半角逗号换成全角。
本平台不提供地区选择,没有 _regionsDetail/_detail 可拼接,
此前的实现会把「请假原因」当作地区值写进 value,
学校端会收到无法解析的非法地区串。
改动:
- normalizeXsgzFields 对 regions 清空 value(置 "",由 xsgzFlatten 跳过,
不会提交该键),避免残留脏值;
- fillXsgzFieldValues 跳过 regions(新增 xsgzNonTextFillable 统一判定
上传类与地区类),不再把文本写进地区字段。
顺带修掉一个逻辑矛盾:checkXsgzUnfilledRequired 原先对「非文本可填」类型
一律 continue,导致**必填的 regions 字段永远不会被拦下**——
请求会带着空地区发出去,然后被学校端含糊拒绝。
现在只有上传类跳过(它由 uploadFiles 承载,无 value 属正常),
regions 必填无值时正常纳入预检,明确提示用户缺哪个字段。
测试:regions 不被填入请假原因、残留 value 被清空、
必填 regions 触发预检拦下(该用例正是暴露上述矛盾的地方)。
224 lines
8.0 KiB
Go
224 lines
8.0 KiB
Go
package handler
|
||
|
||
import "testing"
|
||
|
||
// fillXsgzFieldValues:优先按标签匹配,未命中时用必填文本字段兜底。
|
||
func TestFillXsgzFieldValues(t *testing.T) {
|
||
req := &ExecuteLeaveApplyRequest{LeaveReason: "感冒发烧需休息"}
|
||
|
||
t.Run("按标签命中原因字段", func(t *testing.T) {
|
||
fields := []map[string]interface{}{
|
||
{"fieldId": "f1", "fieldName": "请假原因", "fieldType": "TEXTAREA", "required": true},
|
||
{"fieldId": "f2", "fieldName": "紧急联系人", "fieldType": "TEXT", "required": true},
|
||
}
|
||
fillXsgzFieldValues(fields, req)
|
||
if fields[0]["value"] != req.LeaveReason {
|
||
t.Fatalf("原因字段未被填充: %v", fields[0])
|
||
}
|
||
if _, ok := fields[1]["value"]; ok {
|
||
t.Fatalf("非原因字段不应被填充: %v", fields[1])
|
||
}
|
||
})
|
||
|
||
t.Run("学校自定义字段名时用必填文本兜底", func(t *testing.T) {
|
||
fields := []map[string]interface{}{
|
||
{"fieldId": "f1", "fieldName": "申请理由说明", "fieldType": "TEXTAREA", "required": true},
|
||
{"fieldId": "f2", "fieldName": "备注", "fieldType": "TEXT", "required": false},
|
||
}
|
||
fillXsgzFieldValues(fields, req)
|
||
// "说明" 命中第一轮
|
||
if fields[0]["value"] != req.LeaveReason {
|
||
t.Fatalf("含'说明'的字段应被填充: %v", fields[0])
|
||
}
|
||
})
|
||
|
||
t.Run("无关键词时必填文本字段兜底", func(t *testing.T) {
|
||
fields := []map[string]interface{}{
|
||
{"fieldId": "f1", "fieldName": "补充信息", "fieldType": "TEXTAREA", "required": true},
|
||
{"fieldId": "f2", "fieldName": "选择项", "fieldType": "RADIO", "required": true},
|
||
}
|
||
fillXsgzFieldValues(fields, req)
|
||
if fields[0]["value"] != req.LeaveReason {
|
||
t.Fatalf("必填文本字段应兜底填充: %v", fields[0])
|
||
}
|
||
if _, ok := fields[1]["value"]; ok {
|
||
t.Fatalf("选择类字段不应猜测填充: %v", fields[1])
|
||
}
|
||
})
|
||
|
||
t.Run("空 fields 与空原因不 panic", func(t *testing.T) {
|
||
fillXsgzFieldValues(nil, req)
|
||
fillXsgzFieldValues([]map[string]interface{}{}, req)
|
||
fillXsgzFieldValues([]map[string]interface{}{{"fieldId": "f1"}}, &ExecuteLeaveApplyRequest{})
|
||
})
|
||
}
|
||
|
||
// checkXsgzUnfilledRequired:找出必填但无值的字段,用于提交前预检。
|
||
func TestCheckXsgzUnfilledRequired(t *testing.T) {
|
||
t.Run("必填有值时不报告", func(t *testing.T) {
|
||
fields := []map[string]interface{}{
|
||
{"fieldId": "f1", "fieldName": "请假原因", "required": true, "value": "感冒"},
|
||
}
|
||
if got := checkXsgzUnfilledRequired(fields); len(got) != 0 {
|
||
t.Fatalf("不应报告缺失,实际: %v", got)
|
||
}
|
||
})
|
||
|
||
t.Run("必填无值时报告字段名", func(t *testing.T) {
|
||
fields := []map[string]interface{}{
|
||
{"fieldId": "f1", "fieldName": "是否离校", "required": true},
|
||
{"fieldId": "f2", "fieldName": "备注", "required": false},
|
||
}
|
||
got := checkXsgzUnfilledRequired(fields)
|
||
if len(got) != 1 || got[0] != "是否离校" {
|
||
t.Fatalf("应只报告「是否离校」,实际: %v", got)
|
||
}
|
||
})
|
||
|
||
t.Run("value 为空串也算缺失", func(t *testing.T) {
|
||
fields := []map[string]interface{}{
|
||
{"fieldId": "f1", "fieldName": "请假事由", "required": true, "value": ""},
|
||
}
|
||
if got := checkXsgzUnfilledRequired(fields); len(got) != 1 {
|
||
t.Fatalf("空串应算缺失,实际: %v", got)
|
||
}
|
||
})
|
||
|
||
t.Run("无 fieldName 时回退 fieldId", func(t *testing.T) {
|
||
fields := []map[string]interface{}{
|
||
{"fieldId": "field_abc", "required": true},
|
||
}
|
||
got := checkXsgzUnfilledRequired(fields)
|
||
if len(got) != 1 || got[0] != "field_abc" {
|
||
t.Fatalf("应回退到 fieldId,实际: %v", got)
|
||
}
|
||
})
|
||
|
||
t.Run("非必填缺失不报告", func(t *testing.T) {
|
||
fields := []map[string]interface{}{
|
||
{"fieldId": "f1", "fieldName": "备注", "required": false},
|
||
}
|
||
if got := checkXsgzUnfilledRequired(fields); len(got) != 0 {
|
||
t.Fatalf("非必填不应报告,实际: %v", got)
|
||
}
|
||
})
|
||
}
|
||
|
||
// normalizeXsgzFields:按前端 Ie() 语义清洗,避免提交学校端读不到的键。
|
||
func TestNormalizeXsgzFields(t *testing.T) {
|
||
t.Run("上传类字段去掉 value", func(t *testing.T) {
|
||
f := map[string]interface{}{
|
||
"fieldId": "f1", "componentType": "imageUpload", "value": "脏数据",
|
||
}
|
||
normalizeXsgzFields([]map[string]interface{}{f})
|
||
if _, ok := f["value"]; ok {
|
||
t.Fatal("上传类字段不应保留 value(前端走 uploadFiles)")
|
||
}
|
||
})
|
||
|
||
t.Run("三种上传类型都处理", func(t *testing.T) {
|
||
for _, ct := range []string{"imageUpload", "fileUpload", "smoothSignature"} {
|
||
f := map[string]interface{}{"fieldId": "x", "componentType": ct, "value": "v"}
|
||
normalizeXsgzFields([]map[string]interface{}{f})
|
||
if _, ok := f["value"]; ok {
|
||
t.Fatalf("%s 应去掉 value", ct)
|
||
}
|
||
}
|
||
})
|
||
|
||
t.Run("多选数组 join 成逗号串", func(t *testing.T) {
|
||
f := map[string]interface{}{
|
||
"fieldId": "f2", "componentType": "checkbox",
|
||
"value": []interface{}{"a", "b", "c"},
|
||
}
|
||
normalizeXsgzFields([]map[string]interface{}{f})
|
||
if got := f["value"]; got != "a,b,c" {
|
||
t.Fatalf("多选应 join 为 a,b,c,实际 %v", got)
|
||
}
|
||
})
|
||
|
||
t.Run("数组中的 nil 与空串被跳过", func(t *testing.T) {
|
||
f := map[string]interface{}{
|
||
"fieldId": "f3", "componentType": "checkbox",
|
||
"value": []interface{}{"a", nil, "", "b"},
|
||
}
|
||
normalizeXsgzFields([]map[string]interface{}{f})
|
||
if got := f["value"]; got != "a,b" {
|
||
t.Fatalf("应跳过 nil 与空串,实际 %v", got)
|
||
}
|
||
})
|
||
|
||
t.Run("普通字符串字段不受影响", func(t *testing.T) {
|
||
f := map[string]interface{}{
|
||
"fieldId": "f4", "componentType": "TEXT", "value": "感冒",
|
||
}
|
||
normalizeXsgzFields([]map[string]interface{}{f})
|
||
if got := f["value"]; got != "感冒" {
|
||
t.Fatalf("普通字段不应被改动,实际 %v", got)
|
||
}
|
||
})
|
||
|
||
t.Run("空输入不 panic", func(t *testing.T) {
|
||
normalizeXsgzFields(nil)
|
||
normalizeXsgzFields([]map[string]interface{}{})
|
||
})
|
||
}
|
||
|
||
// fillXsgzFieldValues 不应把请假原因写进上传类字段。
|
||
func TestFillSkipsUploadFields(t *testing.T) {
|
||
fields := []map[string]interface{}{
|
||
{"fieldId": "f1", "componentType": "imageUpload", "fieldName": "请假原因附件", "required": true},
|
||
}
|
||
req := &ExecuteLeaveApplyRequest{LeaveReason: "感冒"}
|
||
fillXsgzFieldValues(fields, req)
|
||
if _, ok := fields[0]["value"]; ok {
|
||
t.Fatalf("上传类字段不应被填充 value,实际 %v", fields[0]["value"])
|
||
}
|
||
}
|
||
|
||
// 上传类必填字段不应触发「必填未填」拦截。
|
||
func TestCheckUnfilledSkipsUpload(t *testing.T) {
|
||
fields := []map[string]interface{}{
|
||
{"fieldId": "f1", "componentType": "imageUpload", "fieldName": "附件", "required": true},
|
||
{"fieldId": "f2", "componentType": "TEXT", "fieldName": "请假原因", "required": true, "value": "感冒"},
|
||
}
|
||
if missing := checkXsgzUnfilledRequired(fields); len(missing) != 0 {
|
||
t.Fatalf("上传类不应算缺失,实际: %v", missing)
|
||
}
|
||
}
|
||
|
||
// regions 地区字段:前端 value 是「选中值,详情」拼接串,本平台无此数据,
|
||
// 不能把请假原因写进去(会产生非法地区串)。
|
||
func TestRegionsFieldNotFilledWithReason(t *testing.T) {
|
||
fields := []map[string]interface{}{
|
||
{"fieldId": "r1", "componentType": "regions", "fieldName": "请假原因", "required": false},
|
||
}
|
||
req := &ExecuteLeaveApplyRequest{LeaveReason: "感冒"}
|
||
fillXsgzFieldValues(fields, req)
|
||
if v, ok := fields[0]["value"]; ok && v != "" {
|
||
t.Fatalf("regions 字段不应被填入请假原因,实际 %v", v)
|
||
}
|
||
}
|
||
|
||
// regions 字段残留的非法 value 应被清空。
|
||
func TestNormalizeRegionsClearsValue(t *testing.T) {
|
||
f := map[string]interface{}{
|
||
"fieldId": "r1", "componentType": "regions", "value": "山东省,泰安市,泰山区",
|
||
}
|
||
normalizeXsgzFields([]map[string]interface{}{f})
|
||
if got := f["value"]; got != "" {
|
||
t.Fatalf("regions 的 value 应被清空,实际 %v", got)
|
||
}
|
||
}
|
||
|
||
// regions 必填但没有可用数据时,应被预检拦下(而不是发出去被学校端拒绝)。
|
||
func TestRegionsRequiredTriggersPrecheck(t *testing.T) {
|
||
fields := []map[string]interface{}{
|
||
{"fieldId": "r1", "componentType": "regions", "fieldName": "籍贯", "required": true},
|
||
}
|
||
missing := checkXsgzUnfilledRequired(fields)
|
||
if len(missing) != 1 || missing[0] != "籍贯" {
|
||
t.Fatalf("必填 regions 无值应被预检拦下,实际: %v", missing)
|
||
}
|
||
}
|