怎么把name一样的多个字段传到后台_手把手,教你用nodejs搭建后台最小系统(大量图文)系列二...

手把手,教你用nodejs搭建后台最小系统(大量图文)系列二

前段时间利用nodejs封装了一个最小系统,也终于是把很久之前的愿望给实现了,在这里把如何实现分享出来。

本系列文章有两篇,上一篇详见下面传送门:

传送门:《手摸手,教你用nodejs搭建后台最小系统(大量图文)系列一》

项目地址:https://gitee.com/zhkumsg/node-base-demo

eb4d2f8a1dae0a32d62e0da636afcdb9.png

原文再续,书接上一回~~~

8、开发底层模块

》common/SortParam.js

const MType = require("./MType");
const Direction = require("./Direction")
//定义SortParam实体类(排序字段)
const propertys = [
"Type",
"Field",
"SortDirection"
];
class SortParam {
constructor() {
propertys.forEach(name => { this[name] = undefined; });
this.set(arguments[0]);
if (this.Type === undefined) { this.Type = MType.Mstring; }
if (this.Field === undefined) { this.Field = ""; }
if (this.SortDirection === undefined) { this.SortDirection = Direction.ASC; }
}
get(key) {
return this[key];
}
set() {
if (typeof arguments[0] === "object") {
for (let name in arguments[0]) {
if (propertys.indexOf(name) > -1) {
this[name] = arguments[0][name];
}
}
}
}
}
module.exports = SortParam;

这是一个与前面MType和Direction枚举相关的实体类,作用是表明字段采用何种排序方式。 SortParam实体类有三个属性,Type字段类型、Field字段名字、SortDirection排序方式。

其中Type属性是MType的枚举,Field属性是字符串、SortDirection属性是Direction枚举。

new SortParam({ Field: "ZK_ID", Type: MType.Mstring, SortDirection: Direction.ASC });

这表明是新建一个SortParam实例,对字符串类型的ZK_ID字段采用正序排列


》commom/MemoryCondition.js

const MType = require("./MType");
const MLogic = require("./MLogic");
const MOperator = require("./MOperator");
//定义MemoryCondition实体类(查询条件)
const propertys = [
"Type",
"Field",
"Operator",
"value",
"Logic",
"subCondition"
];
class MemoryCondition {
constructor() {
propertys.forEach(name => { this[name] = undefined; });
this.set(arguments[0]);
if (this.Type === undefined) { this.Type = MType.Mstring; }
if (this.Field === undefined) { this.Field = ""; }
if (this.Operator === undefined) { this.Operator = MOperator.Like; }
if (this.Logic === undefined) { this.Logic = MLogic.And; }
}
get(key) {
return this[key];
}
set() {
if (typeof arguments[0] === "object") {
for (let name in arguments[0]) {
if (propertys.indexOf(name) > -1) {
this[name] = arguments[0][name];
}
}
}
}
}
module.exports = MemoryCondition;

查询条件实体类。该实体类有六个参数,其代表意义如下:

MemoryCondition.Type:字段类型(MType枚举)
MemoryCondition.Field:字段名称(字符串)
MemoryCondition.Operator:匹配逻辑(MOperator 枚举)
MemoryCondition.value:值(任意类型)
MemoryCondition.Logic:条件间关系(MLogic枚举)
MemoryCondition.subCondition:子查询(这里用不上,该功能用于多表查询)

比如查询用户表中,用户主键为admin,写法如下(该写法将会经常用)

new MemoryCondition({ Type: MType.Mstring, Field: "ZK_ID", Operator: MOperator.Equal, value: "admin", Logic: MLogic.And });

其中Type属性默认为MType.Mstring,Operator属性默认为MOperator.Like,Logic属性默认为MLogic.And。Field和value为必传字段。

上面的查询条件将会被Single底层翻译成

where ZK_ID = 'admin'

》common/MemoryResult.js

//定义MemoryResult实体类(查询结果)
const propertys = [
"recordcount",
"pagecount",
"result",
"ErrorMsg"
];
class MemoryResult {
constructor() {
propertys.forEach(name => { this[name] = undefined; });
this.set(arguments[0]);
if (this["recordcount"] === undefined) { this["recordcount"] = 0; }
if (this["pagecount"] === undefined) { this["pagecount"] = 0; }
if (this["result"] === undefined) { this["result"] = []; }
if (this["ErrorMsg"] === undefined) { this["ErrorMsg"] = ""; }
}
get(key) {
return this[key];
}
set() {
if (typeof arguments[0] === "object") {
for (let name in arguments[0]) {
if (propertys.indexOf(name) > -1) {
this[name] = arguments[0][name];
}
}
}
}
}
module.exports = MemoryResult;

对于每一个sql搜索(查询),返回的结果都不一定一样,所以我们封装了一个实体类,用于包装返回结果,其包括recordcount查询总数、pagecount当前页码、result结果集(类似于C#下的DataTable,这里是数组)和ErrorMsg错误信息四个属性。

调用通用查询后将返回该实体类。


》common/ServiceClient.js

在前面新建的11个文件中,简单的9个已经介绍完毕,现在正式开始介绍通用查询。在开始介绍通用查询底层前,我们先看一下通用查询的时序图

d9ca81d5e08c8629d0fd7d71435b12fd.png

通用查询时序图

可以看到,ServiceClient主要是做switch判断、Single做sql语句拼接、DataAccess做数据访问。这里先介绍ServiceClient.js

const { TableCfg, QueryModel, Single } = require('./common.module');
const {
ZK_USERINFO,
ZK_PERMITINFO,
ZK_PERMITCONFIG,
ZK_ROLEINFO,
ZK_PARAMINFO,
ZK_INVESTMENT
} = require('../model/model.module');
var ServiceClient = {
/**
* 通用查询
* @param {*} model 实体枚举
* @param {*} condition 条件
* @param {*} size 类型
* @param {*} pagesize 每页条数
* @param {*} pageno 页码
* @param {*} isPager 是否分页
* @param {SortParam} sp 排序
*/
Query(model, condition, size, pagesize, pageno, isPager, sp) {
let result = new Array();
switch (model) {
case QueryModel.ZK_USERINFO: result = this.getqueryPara(ZK_USERINFO); break;
case QueryModel.ZK_PERMITINFO: result = this.getqueryPara(ZK_PERMITINFO); break;
case QueryModel.ZK_PERMITCONFIG: result = this.getqueryPara(ZK_PERMITCONFIG); break;
case QueryModel.ZK_ROLEINFO: result = this.getqueryPara(ZK_ROLEINFO); break;
case QueryModel.ZK_PARAMINFO: result = this.getqueryPara(ZK_PARAMINFO); break;
case QueryModel.ZK_INVESTMENT: result = this.getqueryPara(ZK_INVESTMENT); break;
}
if (result.length > 0) {
return Single.query(result[0], condition, size, pagesize, pageno, isPager, sp, result[1]);
} else {
return Promise.reject("实体枚举错误");
}
},
/**
* 读取配置文件,获取表名以和主键
* @param {*} m 实体类
*/
getqueryPara(m) {
let result = new Array();
if (TableCfg[m.name] != undefined) {
result.push(TableCfg[m.name].name);
result.push(Object.getOwnPropertyNames(new m).indexOf("EB_ISDELETE") > -1 ? "false" : null);
}
return result;
}
};
module.exports = ServiceClient;

该对象中有两个方法,Query和getqueryPara,前者是查询接口,后者是是查找数据表对应情况的方法。

先说getqueryPara方法,它是通过读取TableCfg.js数据库映射文件,找到当前查询的表名和主键,最后在通过Object.getOwnPropertyNames方法实现判断是否存在EB_ISDELETE字段,判断EB_ISDELETE字段的意义是后续默认添加EB_ISDELETE='0'的判断(因为我们的系统默认是做逻辑删除,不做物理删除)。

对于Query方法,它接受model 实体枚举、condition 条件、size 类型、pagesize 每页条数、pageno 页码、isPager 是否分页、sp 排序共七个参数。内部根据查询枚举获取对应表名、主键和EB_ISDELETE字段情况,最后调用Single下的query方法(该方法将返回Promise实体,异步回调)

return Single.query(result[0], condition, size, pagesize, pageno, isPager, sp, result[1]);

PS1:前面没有介绍过_common/common.module.js_这个文件,其实它和_model/model.module.js_一样,都是把当前文件夹的全部文件统一导出。
PS2:Object.getOwnPropertyNames是js原生方法,用于遍历自身属性,相当于OOP下反射


》common/Single.js

910af71b0bce126c66bff29d14dab092.gif

没错,是到最重要的时候了,现在开始终于进入Single的介绍了。
Single.js可以算是通用查询的核心了,在该文件中实现了拼接sql字符串的目标,先大概看一下Single文件。

f58ab3174840f8a8fba9c8372800b5e5.png

Single.query查询方法,生成基础sql语据

/**
* 通用查询
* @param {*} tablename 表名
* @param {*} condition 条件
* @param {*} size
* @param {*} pagesize
* @param {*} pageno
* @param {*} isPager 是否分页
* @param {SortParam} sp 排序
* @param {*} flag 是否有EB_ISDELETE字段
*/
query(tablename, condition, size, pagesize, pageno, isPager, sp, flag) {
let sql = "select * from " + tablename;
if (flag === "false") {
sql += " where EB_ISDELETE = '0'";
}
return this.querySQL(sql, condition, size, pagesize, pageno, isPager, sp, flag);
},

b32491fb5aadafaa155da6b3237823bf.png

Single.getwhere拼接where条件

/**
* 获取条件语句
* @param {*} condition 条件
* @param {*} flag
*/
getwhere(condition, flag) {
let sqlwhereall = [];
//条件判断较多,具体代码见后面的github链接
return sqlwhereall.join("");
}

e79132c829d8bbb197738345662fb6b6.png

Single.querySQL 根据基础sql语句和where条件,再根据分页情况调用数据访问底层

/**
* 执行查询(promise)
* @param {*} sql 基础sql语句
* @param {*} condition 条件
* @param {*} size
* @param {*} pagesize
* @param {*} pageno
* @param {*} isPager
* @param {SortParam} sp
* @param {*} flag
*/
querySQL(sql, condition, size, pagesize, pageno, isPager, sp, flag) {
let ms = new MemoryResult();
sql = sql + this.getwhere(condition, flag);
let promise = new Promise((resolve, reject) => {
if (isPager) {
ds.GetTable("select count(1) as sam from (" + sql + ") as myTemp").then((dt, err) => {
if (err) {
reject(err);
} else {
if (dt.length > 0) {
ms.recordcount = Number.parseInt(dt[0].sam, 10);
ms.pagecount = Math.floor(ms.recordcount / pagesize);
}
ds.GetTable(ds.datapagerSql(pageno, pagesize, sql, sp)).then((dt, err) => {
if (err) {
reject(err);
} else {
ms.result = dt;
resolve(ms);
}
});
}
});
} else {
ds.GetTable(sql).then((dt, err) => {
if (err) {
reject(err);
} else {
ms.result = dt;
resolve(ms);
}
});
}
});
return promise;
},

由于篇幅限制,具体代码这里不再粘贴,详细可见github链接(此处该有掌声)。

到这里,通用查询开发结束(getwhere方法后续将github链接)。

这时候再去执行前面的通用查询就可以正常执行了,可能有小伙伴已经忘了

let condition = []; //查询条件实体类数组
let pagesize = 30; //页数
let pageno = 1; //页码
let sort = new SortParam(); //排序实体类
client.Query(QueryModel.ZK_USERINFO, condition, null, pagesize, pageno, true, sort).then(m => {
console.log(m.result); //分页查询结果
}).catch(err => {
throw err;
})

PS:在app.js下测试,测试前需要把配置和文件开发好,最后记得导入文件

第四步:实现快速增删改操作

经历了前面的通用查询开发后,相信你已经有点累了,然而总开发才完成了四分之一,后面继续下一个四分之一:实现快速增删改操作。

有小伙伴又忍不住要站起来了,嚷嚷着不是用数据访问底层DataAccess的RunQuery不就可以执行增删改了吗?

f561366b29eee82efb6d412f4e28c671.png

这位同学请你坐下!你挡着后面端意大利面的二营长了!

是的,你没有说错,这的确可以,但是,我说过你可以不用写一句sql代码就能实现业务开发。

c8b5b495ff9654964c229980630e5826.png

第五步:优化ServiceClient底层

c8b5b495ff9654964c229980630e5826.png

第六步:开发底层公共方法

c8b5b495ff9654964c229980630e5826.png

9、开发接口路由

c8b5b495ff9654964c229980630e5826.png

10、添加权限判断

c8b5b495ff9654964c229980630e5826.png

11、实现跨域登陆

c8b5b495ff9654964c229980630e5826.png

12、踩坑经历

时间差异、sql事务、参数化查询、global connection、cache、token

c8b5b495ff9654964c229980630e5826.png

13、管理系统开发

c8b5b495ff9654964c229980630e5826.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值