当前位置: 首页 > news >正文

低代码编辑平台后台实现

背景

之前做过一个前端低代码编辑平台,可以实现简单的移动端页面组件拖拽编辑:
https://github.com/li-car-fei/react-visual-design

最近基于C++的oatpp框架实现了一下后台。使用oatpp框架做web后台开发时,发现按照官方的示例使用的话,很难在多表多控制视图模式下进行开发,因此修改了一些它的使用方法,以面向对象的方式对各个部分管理,高效开发。

github链接:https://github.com/li-car-fei/oatpp-low-code
求求 star~~

其中,对于低代码平台中核心功能,实现思路如下所述。

主要思路

  • 运用 oatpp++ 搭建后端服务程序

  • Restful API 接口 (Swagger 提供接口示例文档)

  • 将低代码的逻辑通过四个表实现

    • user 表:用户信息,一个用户对应多个project
    • project 表:某个低代码编辑图的基础信息;一个project对应多个component
    • component 表:组件表,储存组件类别、属性、序号等;一个component对应0个或多个childComponnet;
    • childComponent 表:子组件表,存储子组件属性、序号等;
  • 对 oatpp++ CRUD 的改写:

    • 数据 DTO 有共用字段的采用类的继承方式定义
   /**
* 组件记录的 DTO
*/
class ComponentRecordDto : public oatpp::DTO {DTO_INIT(ComponentRecordDto, DTO)DTO_FIELD(Int32, record_id);DTO_FIELD(Int32, project_id, "project_id");DTO_FIELD(Enum<ComponentType>::AsString, component_type);DTO_FIELD(Int32, components_index);DTO_FIELD(String, currentDate);DTO_FIELD(String, header, "header");DTO_FIELD(String, title, "title");DTO_FIELD(String, content, "content");DTO_FIELD(String, mode, "mode");DTO_FIELD(String, src, "src");DTO_FIELD(String, link, "link");DTO_FIELD(String, label1, "label1");DTO_FIELD(String, label2, "label2");DTO_FIELD(String, backgroundColor, "backgroundColor");
};/**
* 组件记录 包括了子组件数组 数据 的 DTO
*/
class ComponentTransferDto : public ComponentRecordDto {DTO_INIT(ComponentTransferDto, ComponentRecordDto)DTO_FIELD(Vector<oatpp::Object<ChildComponentTranserDto>>, childs, "childs");
};
  • Db 部分,每个表的SQL操作分别定义为一个类,随后挂载到一个总的 Db 类中,使用共享的数据库连接池和语句执行器excuator
/*** 总的 DB 类,管理所有的 数据相关操作类
*/
class Db {
public:Db(const std::shared_ptr<oatpp::orm::Executor>& executor): userDb(std::make_shared<UserDb>(executor)),projectDb(std::make_shared<ProjectDb>(executor)),componentDb(std::make_shared<ComponentDb>(executor)),childComponentDb(std::make_shared<ChildComponentDb>(executor)){std::cout << "Db start creating" << std::endl;oatpp::orm::SchemaMigration migration(executor);migration.addFile(1 /* start from version 1 */, DATABASE_MIGRATIONS "/001_init.sql");// TODO - Add more migrations here.migration.migrate(); // <-- run migrations. This guy will throw on error.auto version = executor->getSchemaVersion();OATPP_LOGD("Db", "Migration - OK. Version=%lld.", version);};std::shared_ptr<UserDb> userDb;std::shared_ptr<ProjectDb> projectDb;std::shared_ptr<ComponentDb> componentDb;std::shared_ptr<ChildComponentDb> childComponentDb;
};
  • service 方面;

    • 定义一个基础服务类,存储 Db 与所有服务共用的成员

    • 每个表的服务定义一个service,虚继承基础服务类(保证后续派生类只有一个Db连接);提供每个表自己独立的服务

    • 所有的 service,都继承自基础服务类;即共用一个数据库连接池,共用一套集成好的 SQL 语句接口;

    • 两个表相关的服务,且有层次关系的,如组件和子组件,则采用继承方式实现;使得组件服务可以调用子组件服务;

    • 多个表相关的服务,如整个 project 相关的服务,另外新建一个 service 类,继承所需的服务对应的类实现;

    • 只需直接继承 BaseService 的类是虚继承即可,就可保证所有类中只有一份 Db;

/*** BaseService* 声明 Status 后续子类都用于HTTP状态的码的设定* 保存 std::shared_ptr<Db> m_database,用于数据库相关操作* 直接继承BaseService的类需要是 virtual 继承,则可保证所有相关Service类中都只有一份Db
*/
class BaseService {/* protected ,子类可以访问到,外界不可以访问 */protected:typedef oatpp::web::protocol::http::Status Status;std::shared_ptr<Db> m_database;public:BaseService(std::shared_ptr<Db> database) : m_database(database) {};
};/* 子组件相关服务 */
class ChildComponentService : virtual public BaseService {public:ChildComponentService(std::shared_ptr<Db> database) : BaseService(database) {};/* others... */
};/* 组件相关服务 */
class ComponentService : public ChildComponentService {public:/*** 虚继承中,需要由最上层派生类调用基类的构造函数* (普通继承则不用,调用直接继承的基类的构造即可)*/ComponentService(std::shared_ptr<Db> database) : BaseService(database), ChildComponentService(database) {};/* others... */
};
  • controller 方面,与 service 提供的服务对应起来,定义HTTP接口的信息;

    • 每个 controller 都接收指向 Db 的共享指针,以传给对应的 service 成员;

    • controller 继承自 oatpp++ 提供的对象,需要从环境中找到序列化/反序列化器objectMapper用于其初始化

    • 定义一个将所有controller进行统一管理的 MyController,将所有实例(只需一个)挂载到里面,并对外提供统一的controller相关操作接口,如router配置;

/*** 通过一个 MyController 将所有的 数据库相关的 Controller 包含起来,* 统一完成 原本在App.cpp 中的 路由的配置
*/class MyController {public:/* 构造函数 : 一个database对象构建所有 Controller */MyController(std::shared_ptr<Db> database) : userController(UserController::createShared(database)),projectController(ProjectController::createShared(database)),componentController(ComponentController::createShared(database)),childComponentController(ChildComponentController::createShared(database)),uploadController(UploadController::createShared()){};/* 静态函数,MyController 只需要一个实例即可 */static std::shared_ptr<MyController> createShared(std::shared_ptr<Db> database){return std::make_shared<MyController>(database);}/* 通过 MyController 完成 所有 子Controller 的路由配置 */void setRouter(oatpp::web::server::api::Endpoints& docEndpoints,std::shared_ptr<oatpp::web::server::HttpRouter> router){docEndpoints.append(router->addController(userController)->getEndpoints());docEndpoints.append(router->addController(projectController)->getEndpoints());docEndpoints.append(router->addController(componentController)->getEndpoints());docEndpoints.append(router->addController(childComponentController)->getEndpoints());docEndpoints.append(router->addController(uploadController)->getEndpoints());};/* 所有的 Controller */std::shared_ptr<UserController> userController;std::shared_ptr<ProjectController> projectController;std::shared_ptr<ComponentController> componentController;std::shared_ptr<ChildComponentController> childComponentController;std::shared_ptr<UploadController> uploadController;
};
  • 另外运用oatpp++的文件上传接口提供图片上传,用于AI模型识别;识别后调用projectComplete的服务存储完整低代码视图信息;
ENDPOINT_INFO(upload) {info->summary = "upload image to parse";}
ENDPOINT("POST", "/upload", upload, REQUEST(std::shared_ptr<IncomingRequest>, request)) {oatpp::data::stream::FileOutputStream fileOutputStream(UPLOADFILEPATH);request->transferBodyToStream(&fileOutputStream); // transfer body chunk by chunkreturn createResponse(Status::CODE_200, "OK");
}

Interceptor Auth 鉴权

JWT 模式,对 header 中的 json web token (userId_ )进行加解密确认;

AuthController 中配置哪些接口不需要鉴权,其余的接口都要增加 JWT 的 Header 参数设置;

请求接口接收后需要 Interceptor 模式进行加解密确认;

接口示例

在这里插入图片描述

相关文章:

低代码编辑平台后台实现

背景 之前做过一个前端低代码编辑平台&#xff0c;可以实现简单的移动端页面组件拖拽编辑&#xff1a; https://github.com/li-car-fei/react-visual-design 最近基于C的oatpp框架实现了一下后台。使用oatpp框架做web后台开发时&#xff0c;发现按照官方的示例使用的话&#…...

c++汉诺塔问题

汉诺塔问题是一个经典的递归问题。基本规则是&#xff0c;给定三个柱子和一些不同大小的盘子&#xff0c;开始时所有盘子按大小顺序堆叠在第一个柱子上&#xff0c;目的是将所有盘子移动到第三个柱子上&#xff0c;并且在移动过程中只能在柱子之间移动一个盘子&#xff0c;并且…...

前端---CSS的样式汇总

文章目录 CSS的样式元素的属性设置字体设置文字的粗细设置文字的颜色文本对齐文本修饰文本缩进行高设置背景背景的颜色背景的图片图片的属性平铺位置大小 圆角矩形 元素的显示模式行内元素和块级元素的转化弹性布局水平方向排列方式&#xff1a;justify-content垂直方向排序方式…...

android适配鸿蒙系统开发

将一个Android应用迁移到鸿蒙系统需要进行细致的工作&#xff0c;因为两者之间存在一些根本性的差异&#xff0c;涉及到代码、架构、界面等多个方面的修改和适配。以下是迁移工作可能涉及的一些主要方面&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专…...

golang学习笔记——select 判断语句

判断语句 Go 语言提供了以下几种条件判断语句&#xff1a; 语句描述if 语句if 语句 由一个布尔表达式后紧跟一个或多个语句组成。if…else 语句if 语句 后可以使用可选的 else 语句, else 语句中的表达式在布尔表达式为 false 时执行。if 嵌套语句你可以在 if 或 else if 语句…...

FLMix: 联邦学习新范式——局部和全局的结合

文章链接&#xff1a;Federated Learning of a Mixture of Global and Local Models 发表期刊&#xff08;会议&#xff09;: ICLR 2021 Conference&#xff08;机器学习顶会&#xff09; 目录 1. 背景介绍2. 传统联邦学习3. FL新范式理论逻辑重要假设解的特性 本博客从优化函…...

为什么嵌入式没有35岁危机?

为什么嵌入式没有35岁危机? 在当今数字化时代&#xff0c;IT行业变化迅速&#xff0c;技术的更新迭代速度惊人。然而&#xff0c;有一个技术领域却能够在这个竞争激烈的行业中稳步前行&#xff0c;而且不受35岁危机所困扰&#xff0c;那就是嵌入式技术。 嵌入式技术是指将计算…...

PostgreSQL设置主键从1开始自增

和MySQL不同&#xff0c;在 PostgreSQL 中&#xff0c;设置主键从1开始自增并重新开始自增是通过序列&#xff08;sequence&#xff09;来实现的。以下是步骤&#xff1a; 步骤1&#xff1a;创建一个序列 CREATE SEQUENCE your_table_id_seqSTART 1INCREMENT 1MINVALUE 1MAXV…...

Vue数据绑定

在我们Vue当中有两种数据绑定的方法 1.单向绑定 2.双向绑定 让我为大家介绍一下吧&#xff01; 1、单向绑定(v-bind) 数据只能从data流向页面 举个例子&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"…...

js写轮播图,逐步完善

目录 1、自动轮播 2、点击更换 3、自动播放加左右箭头点击切换 4、完整版轮播图 1、自动轮播 用定时器setInterval()来写&#xff0c;可以实现自动播放 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><met…...

算法-链表-简单-相交、反转、回文、环形、合并

记录一下算法题的学习5 在写关于链表的题目之前&#xff0c;我们应该熟悉回忆一下链表的具体内容 什么是链表&#xff1a; 链表&#xff08;Linked list&#xff09;是一种常见的基础数据结构&#xff0c;是一种线性表&#xff0c;但是并不会按线性的顺序存储数据&#xff0c…...

【500强 Kubernetes 课程】第3章 运行docker容器

一 - 三 &#xff0c;docker基础操作见 第2章7节 四、docker部署web网站 1、安装 nginx &#xff08;适合场景&#xff1a;学习 - 略&#xff09; 2、docker 安装 nginx Stage 1 &#xff1a;docker hub 上 搜索 nginx 镜像 Stage 2&#xff1a;拉取官方镜像 Stage 3&…...

Python中表格插件Tabulate的用法

目录 一、引言 二、Tabulate插件安装与导入 三、Tabulate基本用法 1、创建表格&#xff1a; 2. 格式化表格&#xff1a; 3. 表格转置&#xff1a; 4、合并单元格&#xff1a; 5、指定每列的格式&#xff1a; 6、指定每行的格式&#xff1a; 7、使用自定义表格格式&am…...

缺陷分级(过程质量bug分级)

缺陷按照其影响的严重程度&#xff0c;从高到低分成5级&#xff0c;分别为致命&#xff08;Blocker&#xff09;、严重&#xff08;Critical&#xff09;、一般&#xff08;Major&#xff09;、轻微&#xff08;Minor&#xff09;以及建议&#xff08;Enhancement&#xff09;。…...

pycharm/vscode 配置black和isort

Pycharm blackd Pycharm中有插件可以实现后台服务运行black&#xff1a;BlackConnect 安装 在python中安装blackd 配置 Pycharm isort pycharm中&#xff0c;isort没有插件&#xff0c;暂使用外部工具实现&#xff0c;外部工具也可添加快捷键实现快捷对文件、文件夹进行fo…...

python列出本地文件路径

按照之前的设想&#xff0c;如果要罗列出本地文件的列表&#xff0c;那不是需要不断的判断文件夹里面的文件夹吗&#xff1f;或者需要使用递归函数本身&#xff0c;才能达到目的吧&#xff1f;没想到使用pop这个函数就可以了。pop是取出元素&#xff0c;那列表里就少了一个&…...

在JavaScript中检查一个数字是否是另一个数字的倍数

使用%模数运算符 为了检查一个数字是否是另一个数字的倍数&#xff0c;我们可以使用JavaScript中的% modulo运算符。 modulo% 操作符返回第一个数字在第二个数字上的余数&#xff0c;例如&#xff1a;10 % 2 0 &#xff0c;所以如果我们得到一个余数0 &#xff0c;那么给定的数…...

计算机网络五层协议的体系结构

计算机网络中两个端系统之间的通信太复杂&#xff0c;因此把需要问题分而治之&#xff0c;通过把一次通信过程中涉及的所有问题分层归类来进行研究和处理 体系结构是抽象的&#xff0c;实现是真正在运行的软件和硬件 1.实体、协议、服务和服务访问点 协议必须把所有不利条件和…...

MySQL 运算符二

逻辑运算符 逻辑运算符用来判断表达式的真假。如果表达式是真&#xff0c;结果返回 1。如果表达式是假&#xff0c;结果返回 0。 运算符号作用NOT 或 !逻辑非AND逻辑与OR逻辑或XOR逻辑异或 1、与 mysql> select 2 and 0; --------- | 2 and 0 | --------- | 0 | -…...

【SA8295P 源码分析】121 - MAX9295A 加串器芯片手册分析 及初始化参数分析

【SA8295P 源码分析】121 - MAX9295A 加串器芯片手册分析 及初始化参数分析 一、MAX9295A 芯片特性1.1 GPIO 引脚说明1.2 功能模块框图1.3 时序分析1.3.1 GMSL2 Lock Time:25 ms1.3.2 视频初始化延时:1.1ms + 17000 x t(PCLK)1.3.3 High-Speed Data Transmission in Bursts1.…...

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

模型参数、模型存储精度、参数与显存

模型参数量衡量单位 M&#xff1a;百万&#xff08;Million&#xff09; B&#xff1a;十亿&#xff08;Billion&#xff09; 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的&#xff0c;但是一个参数所表示多少字节不一定&#xff0c;需要看这个参数以什么…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

el-switch文字内置

el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

tree 树组件大数据卡顿问题优化

问题背景 项目中有用到树组件用来做文件目录&#xff0c;但是由于这个树组件的节点越来越多&#xff0c;导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多&#xff0c;导致的浏览器卡顿&#xff0c;这里很明显就需要用到虚拟列表的技术&…...

大数据学习(132)-HIve数据分析

​​​​&#x1f34b;&#x1f34b;大数据学习&#x1f34b;&#x1f34b; &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 用力所能及&#xff0c;改变世界。 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4…...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

2023赣州旅游投资集团

单选题 1.“不登高山&#xff0c;不知天之高也&#xff1b;不临深溪&#xff0c;不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...