xsgz 字段提交按前端 Ie() 语义清洗:上传类去 value、多选 join 逗号
从 studentForm-Dx5Fpjfs.js 的 Ie() 看到字段有三条分支:
regions: value = 拼接地名
imageUpload / fileUpload / smoothSignature: o.uploadFiles = 文件列表;不设 value
其余: o.value = Array.isArray(n) ? n.join(",") : n
我们的实现只有一条路——直接写 value,导致两个偏差:
1. 上传类字段(图片/文件/手写签名)被塞了文本 value,前端根本不会这样提交,
且从未提供 uploadFiles。学校端若要求附件,行为不可预期。
2. 多选类字段若 value 是数组,xsgzFlatten 会展开成 fields[0].value[0]
/ fields[0].value[1],学校端按 value 取字符串时读不到;
前端是 join(",") 成单个字符串。
改动:
- 新增 normalizeXsgzFields,按上述三分支净化字段定义:
上传类删除 value(本平台不代传附件,不臆造 uploadFiles),
数组 value 按前端规则 join(",")(跳过 nil 与空串);
- 请假、销假两条提交路径都接入;
- fillXsgzFieldValues 跳过上传类字段,避免把「请假原因」写进附件字段;
- checkXsgzUnfilledRequired 不把上传类必填字段算作缺失
(它们由 uploadFiles 承载,无 value 属正常),避免误拦。
测试:三种上传类型去 value、多选 join、数组含 nil/空串、普通字段不受影响、
空输入不 panic、填充跳过上传类、预检不误拦上传类。
This commit is contained in:
@@ -535,6 +535,8 @@ func executeXsgzLeaveCancel(c *fiber.Ctx, user *model.SysUser, req *ExecuteLeave
|
||||
f["value"] = req.Address
|
||||
}
|
||||
}
|
||||
// 按前端 Ie() 语义清洗:上传类去 value、多选数组 join(",")
|
||||
normalizeXsgzFields(formFields)
|
||||
// 提交前预检:必填项无法自动填写时明确指出,避免含糊失败
|
||||
if missing := checkXsgzUnfilledRequired(formFields); len(missing) > 0 {
|
||||
msg := "该校销假表单还有必填项无法自动填写:" + strings.Join(missing, "、") +
|
||||
|
||||
@@ -612,6 +612,8 @@ func executeXsgzLeaveApply(c *fiber.Ctx, user *model.SysUser, req *ExecuteLeaveA
|
||||
formFields = nil
|
||||
}
|
||||
fillXsgzFieldValues(formFields, req)
|
||||
// 按前端 Ie() 语义清洗:上传类去 value、多选数组 join(",")
|
||||
normalizeXsgzFields(formFields)
|
||||
|
||||
// 提交前预检:学校表单可能包含本平台无法提供值的必填字段
|
||||
// (如"是否离校"单选)。与其提交后得到含糊失败,不如明确指出。
|
||||
@@ -689,6 +691,9 @@ func fillXsgzFieldValues(fields []map[string]interface{}, req *ExecuteLeaveApply
|
||||
// 第一轮:按标签匹配
|
||||
matched := false
|
||||
for _, f := range fields {
|
||||
if xsgzUploadComponentTypes[mapStr(f, "componentType")] {
|
||||
continue // 上传类字段不该被文本值覆盖
|
||||
}
|
||||
label := strings.ToLower(mapStr(f, "fieldZh") + mapStr(f, "fieldName") + mapStr(f, "label"))
|
||||
if strings.Contains(label, "原因") || strings.Contains(label, "事由") || strings.Contains(label, "说明") {
|
||||
f["value"] = req.LeaveReason
|
||||
@@ -729,6 +734,11 @@ func checkXsgzUnfilledRequired(fields []map[string]interface{}) []string {
|
||||
if !required {
|
||||
continue
|
||||
}
|
||||
// 上传类字段由 uploadFiles 承载,没有 value 属正常,
|
||||
// 不应算作「未填写」(本平台不代传附件,由学校端按需处理)。
|
||||
if xsgzUploadComponentTypes[mapStr(f, "componentType")] {
|
||||
continue
|
||||
}
|
||||
if v, ok := f["value"]; ok && v != nil && fmt.Sprintf("%v", v) != "" {
|
||||
continue
|
||||
}
|
||||
@@ -743,3 +753,51 @@ func checkXsgzUnfilledRequired(fields []map[string]interface{}) []string {
|
||||
}
|
||||
return missing
|
||||
}
|
||||
|
||||
// xsgzUploadComponentTypes 前端 Ie() 里走 uploadFiles 分支的组件类型。
|
||||
var xsgzUploadComponentTypes = map[string]bool{
|
||||
"imageUpload": true,
|
||||
"fileUpload": true,
|
||||
"smoothSignature": true,
|
||||
}
|
||||
|
||||
// normalizeXsgzFields 按前端 Ie()(studentForm-Dx5Fpjfs.js)的语义净化字段定义。
|
||||
//
|
||||
// 前端对每个字段按 componentType 分三支处理,直接把表单值映射到提交对象:
|
||||
//
|
||||
// regions: value = 拼接后的地名(含 _regionsDetail / _detail 后缀处理)
|
||||
// imageUpload / fileUpload / smoothSignature:
|
||||
// o.uploadFiles = 文件列表(并 delete content/url),**不设 value**
|
||||
// 其余: o.value = Array.isArray(n) ? n.join(",") : n
|
||||
//
|
||||
// 我们的实现只填了 value,存在两个问题:
|
||||
// 1. 上传类字段被塞了 value(前端不会这样提交),且完全没有 uploadFiles;
|
||||
// 2. 多选类字段若是数组,前端会 join(","),我们直接保留数组,
|
||||
// 经 xsgzFlatten 展开成 fields[n].value[k] 这种学校端读不到的键。
|
||||
//
|
||||
// 本函数只做「清洗」:去掉不该出现的内容,不臆造用户未提供的数据。
|
||||
func normalizeXsgzFields(fields []map[string]interface{}) {
|
||||
for _, f := range fields {
|
||||
ct := mapStr(f, "componentType")
|
||||
if xsgzUploadComponentTypes[ct] {
|
||||
// 上传类:value 不应存在(由 uploadFiles 承载)。
|
||||
// 本平台不代传附件,保持为空即可。
|
||||
delete(f, "value")
|
||||
continue
|
||||
}
|
||||
// 其余:若 value 是数组,按前端规则 join(",")(多选拼接)
|
||||
if arr, ok := f["value"].([]interface{}); ok {
|
||||
parts := make([]string, 0, len(arr))
|
||||
for _, v := range arr {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
s := fmt.Sprintf("%v", v)
|
||||
if s != "" {
|
||||
parts = append(parts, s)
|
||||
}
|
||||
}
|
||||
f["value"] = strings.Join(parts, ",")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,3 +103,86 @@ func TestCheckXsgzUnfilledRequired(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user