# 后端集成说明
# 需求
- 鲁班H5 后端(Node.js) -> Java(Spring Boot)/PHP 等
# 解决方案
目前鲁班H5的后端是基于 Node.js 写的,社区的很多小伙伴反馈,希望能够提供 Spring Boot 或者 PHP 等其它语言驱动的后端API。因为精力有限,我们提供了如下的解决方案,来尝试解决这个需求:
提供基于 Swagger UI 的后端API 文档 (opens new window),小伙伴只要参照参照 API 文档,使用 Spring Boot 或者 其它语言实现相同的API即可
社区有小伙伴贡献了基于 Spring Boot 的API项目,如下,大家可以根据自己的需求,跟进这两个项目,非常欢迎提 PR 或 相关 issue:
- WIP: Working in Progress 迭代中
- 版权声明:鲁班H5 版权所有,禁止任何形式的转载和抄袭,侵权必究
# 数据库表
# Work 表:H5 作品
# 建表语句
DROP TABLE IF EXISTS `work`;
CREATE TABLE `work` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL COMMENT '标题',
`description` longtext COMMENT '描述',
`cover_image_url` longtext,
`pages` json DEFAULT NULL,
`publish` tinyint(1) NOT NULL DEFAULT '0',
`template` tinyint(1) NOT NULL DEFAULT '0',
`created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8;
# 字段说明
{
// 标题
"title": {
"type": "string"
},
// 描述信息
"description": {
"type": "text"
},
// 封面图链接
"cover_image_url": {
"type": "text"
},
// 页面数据,主要部分
"pages": {
"type": "json"
},
// 创建时间
"created_at": {
"type": "date"
},
// 更新时间
"updated_at": {
"type": "date"
},
// 是否已经发布
"is_publish": {
"type": "boolean"
},
// 是否是模板
"is_template": {
"type": "boolean"
},
// 关联的表单
"workforms": {
"collection": "workform",
"via": "work"
}
}
# WorkForm:表单收集表
# 建表语句
DROP TABLE IF EXISTS `work_forms`;
CREATE TABLE `work_forms` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`form` longtext,
`work_id` bigint(20) NOT NULL,
PRIMARY KEY (`id`),
KEY `work_id` (`work_id`),
CONSTRAINT `work_forms_ibfk_1` FOREIGN KEY (`work_id`) REFERENCES `work` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
WorkForm Table 结构
{
formJSON: JSON, // 一条表单记录
workID: 外键 // 关联的作品ID,外键
}
# 表单提交记录 Example:
[
{
workId: 1,
formJSON: {
"11": "张三", // 姓名
"22": 18, // 年龄
"33": "工程师" // 角色
}
},
{
workId: 1,
formJSON: {
"11": "张四", // 姓名
"22": 18, // 年龄
"33": "工程师" // 角色
}
},
{
workId: 2,
formJSON: {
"11": "王五", // 姓名
"22": 18, // 年龄
"33": "工程师" // 角色
}
},
]
{
"formJSON": {}, // "type": "json",
"workId" // 关联的 workId,外键
}
#
解释:Work 与 FormCollection 的关系是 一对多的关系,一个作品可以有很多关联的表单统计记录。
# 接口实现思路 + 伪代码
Node.js 代码实现:https://github.com/ly525/luban-h5/blob/dev/back-end/h5-api/api/work/controllers/Work.js (opens new window)
{
"routes": [
{
// 图片代理,防止跨域
// 主要是请求一些跨域图片的时候,先走API,API负责请求图片,用来解决跨域问题
// 代码关键实现:return http.request(request.get.imgURL)
"method": "GET",
"path": "/works/cors-proxy",
"handler": "Work.corsProxy",
"config": {
"policies": []
}
},
{
// 所有作品
"method": "GET",
"path": "/works",
"handler": "Work.find",
"config": {
"policies": []
}
},
{
// 统计作品总数
"method": "GET",
"path": "/works/count",
"handler": "Work.count",
"config": {
"policies": []
}
},
{
// 获取某个作品的详情数据
// 伪代码:return HTTPJsonResponse(WorkModel.findOne(workId).toJSON())
"method": "GET",
"path": "/works/:id",
"handler": "Work.findOne",
"config": {
"policies": []
}
},
{
// 创建作品
"method": "POST",
"path": "/works",
"handler": "Work.create",
"config": {
"policies": []
}
},
{
// 更新作品
"method": "PUT",
"path": "/works/:id",
"handler": "Work.update",
"config": {
"policies": []
}
},
{
// 删除某个作品
"method": "DELETE",
"path": "/works/:id",
"handler": "Work.delete",
"config": {
"policies": []
}
},
{
// 预览某个作品
// workJSON = WorkModel.findOne(workId).toJSON()
// return render('作品模板.jsp', workJSON)
"method": "GET",
"path": "/works/preview/:id",
"handler": "Work.previewOne",
"config": {
"policies": []
}
},
{
//
/**
* 提交某个作品的表单
* 思路:
比如页面中有
输入框:姓名,UUID为:11...
输入框:年龄,UUID为:22...
单选框:角色,UUID为:33...
在表单提交的时候,其实提交到后端的数据方式如下:
1. URL 为 /submit-form/:workId/
2. payload 是一个 JSON:
{
"11": "张三", // 姓名
"22": 18, // 年龄
"33": "工程师" // 角色
}
后端在接收到数据之后
1. 会向 WorkForm 表中插入一条记录,主要存储在其 form 字段中(JSON 类型字段)
2. 根据 workId 找到 Work,并绑定到该记录的work字段上(外键)
*/
// Node.js 参考代码如下:
// https://github.com/ly525/luban-h5/blob/dev/back-end/h5-api/api/work/controllers/Work.js#L16-L24
"method": "POST",
"path": "/works/form/submit/:id",
"handler": "Work.submitForm",
"config": {
"policies": []
}
},
{
/*
* 查询某个作品的表单统计数据
* 思路
1. 根据 workId 获得 Work
2. work.pages.find(所有的表单类型元素)
3. 构造 Object:uuid2NameMap,即 UUID 和元素名称的映射,比如 11 代表姓名,22代表年龄这样
举个栗子:
这是 Work 记录:
{
"11": "姓名",
"22": 年龄,
"33": "角色"
}
4. 我们再从服务器中找出来刚才提交的和这个 Work 绑定的所有表单,其中一条记录栗子如下:
其中一条表单记录:
{
"11": "张三", // 姓名
"22": 18, // 年龄
"33": "工程师" // 角色
}
5. 这样,我们把两条记录结合下,就会得到如下结果:
{
"姓名": "张三",
"年龄": 18,
"角色": "工程师"
}
其中的一条记录,我们就生成完毕了。
同样的办法,所有的表单记录都可以生成了,我们把这个返回给前端,前端就可以渲染表单数据了
Response Demo:
---------------------
{
formRecords: [
{
workId: 1,
formJSON: {
"11": "张三", // 姓名
"22": 18, // 年龄
"33": "工程师" // 角色
}
},
{
workId: 1,
formJSON: {
"11": "张三", // 姓名
"22": 18, // 年龄
"33": "工程师" // 角色
}
},
],
uuidToName: {
"11": "姓名",
"22": 年龄,
"33": "角色"
}
}
---------------------
*/
// Node.js 参考代码如下:
// https://github.com/ly525/luban-h5/blob/dev/back-end/h5-api/api/work/controllers/Work.js#L25-L55
"method": "GET",
"path": "/works/form/query/:id",
"handler": "Work.queryFormsOfOneWork",
"config": {
"policies": []
}
},
{
// 设置某个作品为模板
// 伪代码:
// work = WorkModel.findOne(workId).toJSON()
// newTemplateWork = work.clone()
// newTemplateWork.isTemplate = true // 不是模版,是正常作品
// result = WorkModel.create(newTemplateWork)
// return result
// Node.js 参考代码如下:
// https://github.com/ly525/luban-h5/blob/dev/back-end/h5-api/api/work/controllers/Work.js#L56-L63
"method": "POST",
"path": "/works/set-as-template/:id",
"handler": "Work.setAsTemplate",
"config": {
"policies": []
}
},
{
// 基于某个模版创建新作品
// 伪代码:
// templateWork = WorkModel.findOne(templateId).toJSON()
// newWork = templateWork.clone()
// newWork.title = newTitle || ''
// newWork.desc = newDesc || ''
// newWork.isTemplate = false // 不是模版,是正常作品
// result = WorkModel.create(newWork)
// return result
// Node.js 参考代码如下:
// https://github.com/ly525/luban-h5/blob/dev/back-end/h5-api/api/work/controllers/Work.js#L64-L71
"method": "POST",
"path": "/works/use-template/:id",
"handler": "Work.useTemplate",
"config": {
"policies": []
}
},
]
}
# 贡献代码或自行开发
# 前提准备
- 了解鲁班H5 编辑器的后端 Model
- 了解鲁班H5 编辑器的后端 接口