259 lines
6.1 KiB
Go
259 lines
6.1 KiB
Go
package util
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"model-gateway/model/entity"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"github.com/gogf/gf/v2/encoding/gjson"
|
|
"github.com/gogf/gf/v2/util/gconv"
|
|
tgjson "github.com/tidwall/gjson"
|
|
)
|
|
|
|
// ======================== 响应解析 ========================
|
|
|
|
// ParseAndValidate 解析模型响应,校验必填字段,返回标准 rounds 格式
|
|
func ParseAndValidate(raw map[string]any, requiredFields []string) (map[string]any, error) {
|
|
contentStr := gconv.String(raw[entity.ResponseBody])
|
|
if strings.TrimSpace(contentStr) == "" {
|
|
return raw, fmt.Errorf("字段 %s 为空", entity.ResponseBody)
|
|
}
|
|
|
|
// 过滤控制字符
|
|
contentStr = cleanControlChars(contentStr)
|
|
|
|
var arr []any
|
|
if err := json.Unmarshal([]byte(contentStr), &arr); err != nil {
|
|
return raw, fmt.Errorf("JSON解析失败: %w", err)
|
|
}
|
|
if len(arr) == 0 {
|
|
return raw, fmt.Errorf("解析后数组为空")
|
|
}
|
|
|
|
// 校验必填字段
|
|
if len(requiredFields) > 0 {
|
|
for i, r := range arr {
|
|
round, _ := r.(map[string]any)
|
|
if round == nil {
|
|
continue
|
|
}
|
|
for _, field := range requiredFields {
|
|
if gjson.New(round).Get(field).IsNil() {
|
|
return raw, fmt.Errorf("rounds[%d] 缺少必填字段: %s", i, field)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return map[string]any{"total_rounds": len(arr), "rounds": arr}, nil
|
|
}
|
|
|
|
// ParseStructResult 解析结构化结果
|
|
func ParseStructResult(raw map[string]any, responseBody string) map[string]any {
|
|
contentStr := gconv.String(raw[responseBody])
|
|
if contentStr == "" || contentStr == "0" {
|
|
return map[string]any{
|
|
"total_rounds": 1,
|
|
"rounds": []map[string]any{{responseBody: raw}},
|
|
}
|
|
}
|
|
|
|
if arr := tryParseArray(contentStr); arr != nil {
|
|
return map[string]any{
|
|
"total_rounds": 1,
|
|
"rounds": []map[string]any{{responseBody: arr}},
|
|
}
|
|
}
|
|
|
|
if parsed := tryParseAny(contentStr); parsed != nil {
|
|
return map[string]any{
|
|
"total_rounds": 1,
|
|
"rounds": []map[string]any{{responseBody: parsed}},
|
|
}
|
|
}
|
|
|
|
return map[string]any{
|
|
"total_rounds": 1,
|
|
"rounds": []map[string]any{{responseBody: contentStr}},
|
|
}
|
|
}
|
|
|
|
// ======================== 响应映射 ========================
|
|
|
|
// MapResponsePayload 将模型响应按映射规则转为标准格式
|
|
func MapResponsePayload(mapping map[string]any, result map[string]any) (map[string]any, error) {
|
|
if len(mapping) == 0 {
|
|
return result, nil
|
|
}
|
|
|
|
resultBytes, _ := json.Marshal(result)
|
|
resultStr := string(resultBytes)
|
|
|
|
mapped := make(map[string]any)
|
|
for standardField, modelPath := range mapping {
|
|
path := gconv.String(modelPath)
|
|
if path == "" {
|
|
continue
|
|
}
|
|
|
|
value := tgjson.Get(resultStr, path)
|
|
if !value.Exists() {
|
|
continue
|
|
}
|
|
|
|
if strings.Contains(path, "#") {
|
|
var arr []any
|
|
for _, v := range value.Array() {
|
|
arr = append(arr, v.Value())
|
|
}
|
|
mapped[standardField] = arr
|
|
} else {
|
|
mapped[standardField] = value.Value()
|
|
}
|
|
}
|
|
|
|
return mapped, nil
|
|
}
|
|
|
|
// ValidateAndParseForm 校验表单并转为嵌套 map
|
|
func ValidateAndParseForm(forms []entity.Form) (map[string]any, error) {
|
|
result := gjson.New("{}")
|
|
|
|
for _, form := range forms {
|
|
if form.Key == "" {
|
|
continue
|
|
}
|
|
if form.Required && (form.Value == nil || gconv.String(form.Value) == "") {
|
|
return nil, fmt.Errorf("字段 %s 为必填", form.Label)
|
|
}
|
|
if form.Value == nil {
|
|
continue
|
|
}
|
|
|
|
val, err := validateAndConvert(form)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
_ = result.Set(form.Key, val)
|
|
}
|
|
|
|
return result.Map(), nil
|
|
}
|
|
|
|
// validateAndConvert 验证表单字段并转为标准格式
|
|
func validateAndConvert(form entity.Form) (any, error) {
|
|
val := form.Value
|
|
fc := form.FieldConstraint
|
|
switch form.Type {
|
|
case "string":
|
|
s := gconv.String(val)
|
|
if fc.MaxLength > 0 && len(s) > fc.MaxLength {
|
|
return nil, fmt.Errorf("字段 %s 超过最大长度 %d", form.Label, fc.MaxLength)
|
|
}
|
|
if fc.MinLength > 0 && len(s) < fc.MinLength {
|
|
return nil, fmt.Errorf("字段 %s 不足最小长度 %d", form.Label, fc.MinLength)
|
|
}
|
|
return s, nil
|
|
|
|
case "number":
|
|
f := gconv.Float64(val)
|
|
if fc.Min != nil && f < gconv.Float64(fc.Min) {
|
|
return nil, fmt.Errorf("字段 %s 不能小于 %v", form.Label, fc.Min)
|
|
}
|
|
if fc.Max != nil && f > gconv.Float64(fc.Max) {
|
|
return nil, fmt.Errorf("字段 %s 不能大于 %v", form.Label, fc.Max)
|
|
}
|
|
// 根据 numberType 决定返回 int 还是 float64
|
|
switch fc.NumberType {
|
|
case "float", "positiveFloat", "negativeFloat":
|
|
return f, nil
|
|
default:
|
|
return int(f), nil
|
|
}
|
|
|
|
case "select", "radio":
|
|
v, ok := val.(map[string]any)
|
|
if !ok {
|
|
return nil, fmt.Errorf("字段 %s 格式错误", form.Label)
|
|
}
|
|
return v, nil
|
|
|
|
case "upload":
|
|
var urls []string
|
|
switch v := val.(type) {
|
|
case []any:
|
|
for _, u := range v {
|
|
urls = append(urls, gconv.String(u))
|
|
}
|
|
case []string:
|
|
urls = v
|
|
case string:
|
|
if v != "" {
|
|
urls = []string{v}
|
|
}
|
|
}
|
|
if fc.MaxCount > 0 && len(urls) > fc.MaxCount {
|
|
return nil, fmt.Errorf("字段 %s 上传数量超过上限 %d", form.Label, fc.MaxCount)
|
|
}
|
|
return urls, nil
|
|
|
|
default:
|
|
return gconv.String(val), nil
|
|
}
|
|
}
|
|
|
|
// ======================== 请求工具 ========================
|
|
|
|
// ParseHeadMsgHeaders 从 head_msg 中提取 HTTP 请求头
|
|
func ParseHeadMsgHeaders(headMsg map[string]any) map[string]string {
|
|
if len(headMsg) == 0 {
|
|
return nil
|
|
}
|
|
out := make(map[string]string, len(headMsg))
|
|
for k, v := range headMsg {
|
|
out[k] = gconv.String(v)
|
|
}
|
|
return out
|
|
}
|
|
|
|
// BodyToQuery 将 body 转为 URL 查询参数
|
|
func BodyToQuery(payload map[string]any) (url.Values, error) {
|
|
q := url.Values{}
|
|
for k, v := range payload {
|
|
if v == nil {
|
|
continue
|
|
}
|
|
q.Set(k, gconv.String(v))
|
|
}
|
|
return q, nil
|
|
}
|
|
|
|
// ======================== 内部辅助 ========================
|
|
|
|
func cleanControlChars(s string) string {
|
|
return strings.Map(func(r rune) rune {
|
|
if r < 32 && r != ' ' {
|
|
return -1
|
|
}
|
|
return r
|
|
}, s)
|
|
}
|
|
|
|
func tryParseArray(s string) []any {
|
|
var arr []any
|
|
if err := json.Unmarshal([]byte(s), &arr); err == nil && len(arr) > 0 {
|
|
return arr
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func tryParseAny(s string) any {
|
|
var parsed any
|
|
if err := json.Unmarshal([]byte(s), &parsed); err == nil {
|
|
return parsed
|
|
}
|
|
return nil
|
|
}
|