使用此框架可以快速搭建一套可用的node服务,带有可选装模块如mysql、导出xlsx文件、解析xlsx文件、文件上传、定时任务等。
- sudo npm install -g light-node-serv
- 在命令行执行 light-node-serv single -p your-project-name
- 根据需要选装不同的模块
- 在新建的工程根目录下执行 yarn dev 或 npm run dev,可在本地运行node服务,默认端口8977,在修改代码后会自动编译运行。
- 服务启动后,可向http://127.0.0.1:8977发起post请求,结果如下
请求参数:
// 请求参数 { "FN": "test", "param": ["tom", 18] } // 请求结果 { "code": "0000", "msg": "The service is running successfully", "data": { "name": "tom", "age": 18 }, "process": "" } - 服务启动后在工程目录会自动创建pm2log文件夹,包含pm2-err-*.log和pm2-out-*.log,报错输出在err日志,用户自定义console输出在out日志,建议开发中使用tail -f pm2-out-*.log来监控日志查看调试输出
- 在根目录下执行yarn build 或 npm run build,将生成dist文件夹,包含app.js、process.json和report.html文件,其中report.html文件是打包结果分析文件(可删除),其他两个文件为业务功能文件,使用pm2 start process.json可运行打包后的服务。
- 框架需要使用pm2-logrotate来管理日志输出,安装命令:pm2 install pm2-logrotate@2.2.0
- 这是分发请求的模块,在单服务文件模式下(single),该模块将所有请求转发到/services/service.ts服务中。在多服务模式下(multiple,暂未开发完成),根据请求中的SN转发到不同的服务中
- 该文件是补丁程序,主要重写了console方法,加入了输出控制及标记
- 定义了通用返回值模型,请求方法和uuid方法
- 数据库操作模块,自动创建连接池,提供执行普通sql和事务的方法
- index.ts
// 初始化数据库连接池 Mysql.init([process.env.MYSQL_DATABASE]); // 此处使用process.json中配置的数据库 - service.ts
import { Mysql } from '@/modules/optional/mysql'; import { PoolConnection } from 'mysql'; // 普通sql查询 async testSql(_name: string, _age: number) { const query_sql = 'select * from your-table where 1'; var query_result: any = await Mysql.doSql(<string>process.env.MYSQL_DATABASE, query_sql).catch((err) => { return err; }); if (query_result.err) { return ResponseResult.ERROR('查询报错'); } return ResponseResult.SUCCESS('查询成功', query_result.data); } // 事务操作 async testTransaction() { const r1 = Math.ceil(Math.random() * 10); const sql1 = `insert into school(school_id, school_name) values("id-${r1}", "name-${r1}")`; const sql2 = `insert into student(name, age, school_id) value("sname-${r1}", "${r1}", "id-${r1}")`; const connections: PoolConnection[] = []; try { const result1: any = await Mysql.doTransaction(<string>process.env.MYSQL_DATABASE, sql1); if (result1.err) { throw new Error('sql1执行失败,回滚'); } else { connections.push(<PoolConnection>result1.instance); } const result2: any = await Mysql.doTransaction(<string>process.env.MYSQL_DATABASE, sql2); if (result2.err) { throw new Error('sql2执行失败,回滚'); } else { connections.push(<PoolConnection>result2.instance); } // 提交 const commitResult = await Mysql.transaCommit(connections); return ResponseResult.SUCCESS('事务执行成功', commitResult); } catch (error) { Mysql.transaRollback(connections); return ResponseResult.ERROR('事务执行失败'); } }
- index.ts
- 目前提供数据以xlsx文档导出,请求地址http://127.0.0.1:8977/exportfile,参数{"order": "e1"},这里的order参数可以自定义,前后端约定好可以互相识别意图就OK
- httpStarter.ts
import xlsx from 'node-xlsx'; import { exportFile } from '@/modules/optional/exportFile'; // 接收导出文件请求 可以使用表单请求该接口 this.router.post('/exportfile', async (ctx, _next) => { exportFile.worker(ctx.request.body).then( (res: any) => { if (res.code === '0000') { // 直接下载文件 const file = xlsx.build([{ name: res.data.name, data: res.data.data }]); ctx.response.set('Content-Type', 'application/vnd.openxmlformats') ctx.response.set("Content-Disposition", "attachment; filename=" + encodeURIComponent(res.data.name)) ctx.response.body = file; } else { console.error(SystemService.parseFileService, '文件导出失败', res); ctx.response.body = ResponseResult.ERROR('文件导出失败', res); } } ).catch((error) => { console.error(SystemService.parseFileService, '文件导出失败', error); ctx.response.body = ResponseResult.ERROR('文件导出失败'); }) });
- httpStarter.ts
- 解析xlsx文件模块,请求地址http://127.0.0.1:8977/parsexlsx,参数{"order": "p1"},这里的order参数可以自定义,前后端约定好可以互相识别意图就OK
- httpStarter.ts
// 接收excel文件并解析 - 未保存 this.router.post('/parsexlsx', koaBody({ multipart: true, formidable: { maxFileSize: maxFileSize } }), async (ctx, _next) => { const tempFile = <any>ctx.request.files; const uuid = UUID(); console.log(SystemService.parseFileService, '请求ID:' + uuid ,'解析文件请求', ctx.request.body, tempFile); parseXlsx.work(ctx.request.body, tempFile).then( (res) => { const result = ResponseResult.SUCCESS('success', res); console.log(SystemService.parseFileService, '请求ID:' + uuid ,'解析文件结果', result); ctx.response.body = result; }, (err) => { console.error(SystemService.parseFileService, '请求ID:' + uuid ,'解析文件失败', err); ctx.response.body = ResponseResult.ERROR_UNKNOWN('读取文件失败'); } ) });
- httpStarter.ts
- 接收文件上传模块,上传目录默认是/public/upload/,请求地址http://127.0.0.1:8977/upload
- httpStarter.ts
/** * 处理文件上传请求 * 因为全局使用会和bodyparse中间件冲突 */ uploadFile.init(); // 创建public/upload目录 this.router.post('/upload', koaBody({ multipart: true, // json: false, formidable: { maxFileSize: maxFileSize } }), async (ctx, _next) => { const tempFile = <any>ctx.request.files; await uploadFile.work(ctx.request.body, tempFile).then( (res) => { ctx.response.body = ResponseResult.SUCCESS('文件上传成功', res); } ).catch( (error) => { console.error(SystemService.uploadFileService, ' 上传文件服务错误 ', error); // 返回信息 ctx.response.body = ResponseResult.ERROR_UNKNOWN('文件上传失败', null, '上传文件服务失败'); } ); });
- httpStarter.ts
- 执行定时任务模块,定时使用node-schedule模块
- index.ts
// 开启定时任务 ScheduleJob.startSchedule();
- index.ts
- Luxiandong
- Yangfengming