场景描述:

前端是可动态编辑的json schema 数据,编辑成功保存后,传递给服务端是一串json

gorm model 字段类型设置为json.RawMessage 遇到的问题_go数据格式类如下

{
  "type": "object",
  "labelWidth": 120,
  "displayType": "row",
  "properties": {
    "test": {
      "api": "gitlab_name",
      "enum": [
        "a",
        "b",
        "c"
      ],
      "type": "string",
      "props": {
        "showSearch": true,
        "filterOption": true,
        "optionFilterProp": "label"
      },
      "title": "git仓库名",
      "widget": "select",
      "disabled": false,
      "readOnly": false,
      "required": false,
      "enumNames": [
        "早",
        "中",
        "晚"
      ]
    },
    "select_n6IurM": {
      "api": "gitlab_addr",
      "enum": [
        "a",
        "b",
        "c"
      ],
      "type": "string",
      "props": {
        "showSearch": true,
        "filterOption": true,
        "optionFilterProp": "label"
      },
      "title": "git地址",
      "widget": "select",
      "disabled": false,
      "readOnly": false,
      "required": false,
      "enumNames": [
        "早",
        "中",
        "晚"
      ]
    },
    "execution_host": {
      "api": "hostname",
      "enum": [
        "A",
        "B",
        "C",
        "D"
      ],
      "type": "array",
      "items": {
        "type": "string"
      },
      "props": {
        "showSearch": true,
        "filterOption": true,
        "optionFilterProp": "label"
      },
      "title": "目标机器",
      "hidden": false,
      "widget": "multiSelect",
      "disabled": false,
      "readOnly": false,
      "required": false,
      "enumNames": [
        "杭州",
        "武汉",
        "湖州",
        "贵阳"
      ],
      "description": "下拉多选"
    }
  }
}

后端需要存储该字段数据,故使用了json.RawMessage,mysql中字段类型设置为json

model结构体:

type TempInfo struct {
	ID            int            `gorm:"primary_key;AUTO_INCREMENT;column:id" json:"id" form:"id"`
	CreatedAt     time.Time      `gorm:"column:create_time" json:"create_time" form:"create_time"`
	UpdatedAt     time.Time      `gorm:"column:update_time" json:"update_time" form:"update_time"`
	TempName      string         `gorm:"column:temp_name; type: varchar(128)" json:"temp_name" form:"temp_name" binding:"required"`        // 模板名称
	FormStructure datatypes.JSON `gorm:"column:form_structure; type: json" json:"form_structure" form:"form_structure" binding:"required"` // 表单结构
	Creator       string         `gorm:"column:creator; type: varchar(128)" json:"creator" form:"creator"`                                 // 创建者
	Updater       string         `gorm:"column:updater; type:varchar(128)" json:"updater" form:"updater"`                                  // 修改者
	Remarks       string         `gorm:"column:remarks; type: longtext" json:"remarks" form:"remarks"`                                     // 备注
}

可以看到使用了datatypes.json 其实和json.RawMesage是一个道理,只不过使用datatypes的好处在于,数据在使用时无需序列化与反序列化,不然直接使用json.RawMessage存储到数据库里面是bytes格式的

gorm.io/datatypes
表结构信息:
CREATE TABLE `temp_info` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
  `update_time` timestamp NULL DEFAULT NULL COMMENT '更新时间',
  `temp_name` varchar(128) DEFAULT NULL,
  `form_structure` longtext DEFAULT NULL COMMENT 'schema结构体',
  `creator` varchar(128) DEFAULT NULL,
  `remarks` longtext,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT="模版信息";

问题描述:

上面我们可以看到,模版的编辑,是支持拖拽的,也就是每一个表单的位置可随意切换。但前端切换成功后,点击保存,在渲染出来的时候,会发现没有修改成功。

前端提交数据顺序:

{
  "type": "object",
  "properties": {
    "bbb": {
      "title": "单选",
      "type": "string",
      "widget": "select",
      "required": false,
      "disabled": false,
      "readOnly": false,
      "props": {
        "filterOption": true,
        "showSearch": true,
        "optionFilterProp": "label"
      }
    },
    "a": {
      "title": "多选",
      "type": "array",
      "widget": "checkboxes",
      "description": "点击多选",
      "required": false,
      "disabled": false,
      "readOnly": false,
      "items": {
        "type": "string"
      }
    }
  },
  "labelWidth": 120,
  "displayType": "row"
}

后端保存到数据库中的顺序:

{
  "type": "object",
  "properties": {
    "a": {
      "title": "多选",
      "type": "array",
      "widget": "checkboxes",
      "description": "点击多选",
      "required": false,
      "disabled": false,
      "readOnly": false,
      "items": {
        "type": "string"
      }
    },
    "bbb": {
      "title": "单选",
      "type": "string",
      "widget": "select",
      "required": false,
      "disabled": false,
      "readOnly": false,
      "props": {
        "filterOption": true,
        "showSearch": true,
        "optionFilterProp": "label"
      }
    }
  },
  "labelWidth": 120,
  "displayType": "row"
}

最开始以为是前端的组件在传递数据的时候 中间对数据进行了转换导致,但在后端打印日志,以及在存储到mysql之前进行打印,顺序都是正常的,也就是,数据在落到mysql后,字段顺序发生了变化

查看了一下mysql的json字段,为了提高性能,mysql会针对json字段做排序处理,即按照1、2、3...进行排序,如果字段是英文,那么将按照字符长短进行排序

故最后将mysql中的字段改为text,问题解决。即数据库使用json字段对json中的字段排序,位置没有要求的可以使用,对顺序有要求的不建议使用