钢材信息小程序开发总结(三) ---EggJS
时间:2022-07-23
本文章向大家介绍钢材信息小程序开发总结(三) ---EggJS,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
这次是对后端部分的介绍
一、系统环境介绍
- NodeJS
- MySQL
- Redis
1.NodeJS
JS服务端运行环境
参考资料:
2.MySQL数据库
开源关系型数据库
参考资料:
3.Redis
开源、支持网络、基于内存、可选持久性的键值对存储数据库
参考资料:
二、EggJS 相关插件
- egg-sequelize
- egg-redis
- egg-jwt
- egg-router-plus
- egg-validate
- egg-cors
- egg-view-nunjucks
- egg-zrole
1.egg-sequelize
Sequelize的EggJS插件. Sequelize是MySQL的ORM框架
参考资料:
2.egg-redis
Redis的EggJS插件
参考资料:
3.egg-jwt
EggJS的JWT插件
参考资料:
4.egg-router-plus
EggJS的路由加强插件, 主要用于给路由统一添加前缀
参考资料:
5.egg-validate
EggJS的参数校验插件
参考资料:
6.egg-cors
EggJS的访问控制插件
参考资料:
7.egg-view-nunjucks
EggJS的模板渲染引擎插件, 语法类似 Django 的 jinjia
参考资料:
8.egg-zrole
EggJS的基于Casbin的权限控制插件
参考资料:
三、项目目录
.
├── app.js
├── server.js
├── app
│ ├── router.js
│ ├── controller
│ ├── extend
│ ├── middleware
│ ├── service
│ ├── public
│ ├── view
│ ├── router
│ └── model (自建Sequelize目录)
└── config
├── plugin.js
├── config.default.js
└── config.prod.js
以上目录介绍如下:
- app/router.js 用于配置URL路由规则。
- app/controller/ 用于解析用户输入,处理后返回响应结果。
- app/extend/ 用于框架内部对象的拓展(request,response,context,application)和工具类(helper)的编写。
- app/middleware/ 用于编写中间件。
- app/service/ 用于编写业务逻辑,如数据库操作的封装,api请求的封装等。
- app/public/ 用于放置静态文件。
- app/view/ 用于放置模板文件。
- app/model/ 用于放置数据模型。
- app/router/ 用户放置分离的路由
- app.js 用于自定义启动时的初始化工作。
- server.js 用于使用pm2部署
pm2 start server.js
四、相关业务逻辑
1.分页逻辑
分页中间件, 获取分页及其他查询参数
module.exports = async (ctx, next) => {
const { query } = ctx.request
ctx.validate({
pageField: { type: 'string', required: false },
pageSort: { type: 'enum', values: ['ASC', 'DESC'], required: false },
pageSize: { type: 'string', format: /d+/, required: false },
pageIndex: { type: 'string', format: /d+/, required: false },
}, query)
const {
pageField = 'updated_at',
pageSort = 'DESC',
} = query
// ~~ 用于 Math.floor 的功能, 以及将 undefined, null 转为数字 0
const pageSize = ~~query.pageSize || 20
const pageIndex = ~~query.pageIndex || 1
ctx.page = { pageField, pageSort, pageSize, pageIndex }
return next()
}
求和服务, 获取列表总数
async findCount({ modelName, where = {} }) {
return this.ctx.model[modelName].count({
where
})
}
根据查询参数获取列表数据
async findPage({
modelName,
where = {},
page,
attributes = {},
include = [],
}) {
const {
pagination
} = this.ctx.helper
const {
order,
offset,
limit
} = pagination(page)
return this.ctx.model[modelName].findAll({
where,
order,
offset,
limit,
attributes,
include,
})
}
示例 1.controller, 获取用户日志
async getUserLog() {
const { ctx, app } = this
let { page } = ctx
const { common } = ctx.service
console.log(page)
page = {
...page,
pageField: 'id',
pageSort: 'DESC'
}
const [total, items] = await Promise.all([
common.findCount({ modelName: 'Log' }),
common.findPage({ modelName: 'Log', page }),
])
const res = { total, items, pageSize: page.pageSize, pageIndex: page.pageIndex }
ctx.helper.success({ ctx, res: res })
}
2.配置路由, 引入分页中间件
apiV1Router.get('/log', app.jwt, middleware.pagination, controller.api.user.getUserLog)
2.文件上传
1.后端controller
class UploadController extends Controller {
async index() {
const { ctx } = this
const stream = await ctx.getFileStream()
ctx.helper.success({ctx, res: 'upload ok' + stream})
}
}
2.前端上传文件
<label for="file">upload</label>
<input type="file" id="file">
<script>
var file = document.querySelector('#file')
file.addEventListener('change', function(e) {
var f = e.target.files[0]
var formdata = new FormData()
formdata.append('file', f)
console.log(formdata)
fetch(`http://localhost:7001/upload`, {
method: 'POST',
body: formdata
})
.then(res => res.json())
.then(res => {
e.target.value = null
console.log(res.data)
})
})
</script>
3.小程序登录
https://www.jianshu.com/p/8df2213f6f9d
4.小程序统计数据获取
由于请求获取access_token
的次数有限制, 所以将其存入redis
中缓存
async getWechatToken() {
const {ctx, app} = this
let result = await ctx.helper.request({ctx, url: `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${app.config.weapp.appId}&secret=${app.config.weapp.secret}`})
app.redis.set('weToken', result.data.access_token, 'EX', result.data.expires_in)
return result.data.access_token
}
使用access_token
请求获取前一天的用户画像
注意: 在零点前后数据会获取不到
async getWechatUser() {
const {ctx, app} = this
const yesterday = dayjs().subtract(1, 'day').format("YYYYMMDD")
let token = await app.redis.get('token');
if(token && token !== '') {
const result = await ctx.helper.request({
ctx,
url: `https://api.weixin.qq.com/datacube/getweanalysisappiduserportrait?access_token=${token}`,
method: 'POST',
data: {
begin_date: yesterday,
end_date: yesterday
}
})
return result
} else {
token = await ctx.service.data.getWechatToken()
const result = await ctx.helper.request({
ctx,
url: `https://api.weixin.qq.com/datacube/getweanalysisappiduserportrait?access_token=${token}`,
method: 'POST',
data: {
begin_date: yesterday,
end_date: yesterday
}
})
return result
}
}
5.热门钢材排序
按照新的顺序, 遍历数据库中热门钢材的sort字段
async setSortById(list) {
let arr = []
list.forEach(v => {
arr.push(this.ctx.model.Hot.update(
{sort: v.sort},
{
where: {
id: v.id
}
}
))
})
const result = await Promise.all(arr)
if(Array.isArray(result) && result.length > 0) {
return true
} else {
return false
}
}
6.HTTP请求封装
request: async ({ctx, method = 'GET', url='', data = {}}) => {
const result = await ctx.curl(url, {
method: method,
contentType: 'json',
dataType: 'json',
data: data
})
return result
}
7.响应体封装
/**
* 错误码
*/
errorCode: {
200: '服务器成功返回请求的数据。',
201: '新建或修改数据成功。',
202: '一个请求已经进入后台排队(异步任务)。',
204: '删除数据成功。',
400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
401: '用户没有权限(令牌、用户名、密码错误)。',
403: '用户得到授权,但是访问是被禁止的。',
404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
406: '请求的格式不可得。',
410: '请求的资源被永久删除,且不会再得到的。',
422: '当创建一个对象时,发生一个验证错误。',
500: '服务器发生错误,请检查服务器。',
502: '网关错误。',
503: '服务不可用,服务器暂时过载或维护。',
504: '网关超时。'
}
/**
* 处理成功请求后的响应
*/
success: ({ ctx, code = 200, res = {}, msg }) => {
if (!res) code = 404
ctx.status = 200
ctx.body = {
code: code,
message: msg || ctx.helper.errorCode[code],
data: res
}
}
/**
* 处理失败请求后的响应
*/
fail: ({ ctx, code = 500, res = {}, msg }) => {
ctx.status = 200
ctx.body = {
code: code,
message: msg || ctx.helper.errorCode[code],
data: res
}
}
8.数据库初始化
- 第一次启动, 取消掉
app.js
中注释即可
'use strict';
module.exports = app => {
if (app.config.env === 'local') {
app.beforeStart(async () => {
await app.model.sync({ force: true })
})
}
}
- 之后注释掉
await app.model.sync({ force: true })
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 【译】Rust与智能指针
- 根据坐标在基因组上面拿到碱基序列来设计引物
- 18款Webpack插件,总会有你想要的!
- Linux下如何查看硬件信息?
- 最通俗易懂的一篇文章了解JVM、JRE、JDK的关系是什么?
- 六. CSS 样式补充之 font & background
- 七 .Html的表格
- rollup + typescript 构建 ts 包
- node 写爬虫,原来这么简单
- 计时器 hook
- 自定义eslint 配置包
- 【61期】MySQL行锁和表锁的含义及区别(MySQL面试第四弹)
- 关于死锁你了解多少,通过“让APP随手机壳改变颜色,程序员和产品经理大家”这一事,了解下死锁可好?
- 三阴性乳腺癌表达矩阵探索笔记之GSEA
- 关于Python异常处理,你需要了解的知识点