express实现

本文详细介绍了使用Express构建MVC结构的应用,包括控制器、模型和视图的创建,以及SequelizeORM进行数据库操作。同时,讨论了中间件的实现、配置管理、数据表设计和API接口的编写,涉及到用户登录、文件上传和页面管理等场景。
摘要由CSDN通过智能技术生成

express

npm install -g express-generator

创建express项目

express --view=hjs myapp

数据表的设计
第一类是应用类,商城活动页面,门店,其他的小程序
id name application_string
第二类是页面层
id application / name / cover / is_able / page_string / update_time create_time / creator share_desc
第三类组件详情
id / page_id / defin_id / sort / component_string / name
用户
id / user_name / password

什么是mvc
model-view-controller 用于应用程序的分层开发
model 业务流程,处理过程对其他层来时是黑箱操作,业务功能的实现,模型接收视图请求的数据,对数据库操作,给controller层返回结果。
controller,模型和视图之上,能接收从接口传递的数据和参数,将校验和参数的处理,往view层数据输出。

创建controller文件夹
创建model文件夹
创建view文件
routes 获取

controller中间件的实现
extend
controller.js

// 在路由中拿到controller对象,中的方法
// controller mvc架构封装
// 中间件的相关逻辑
// 获取node核心模块
const fs = require('fs') // 文件操作方法
const path = require('path') // 处理路径

class Controller {
	constructor(req,res,app) {
		this.req = req
		this.res = res
		this.app = app
	}
}
export.Controller = Controller // 对外暴露

// 接收传递进来的路径
// 返回:对象
const loadController = controllerPath => {
	const ControllerMap = {}
	const files = fs.readdirSync(controllerPath) // 数组列表
	files.forEach(file => {
	// 状态信息
	const fileState = fs.statSync(path.join(controllerPath,file))
	// 判断当前文件是目录还是文件,如果为true,为目录,false,是文件
	if (fileState.isDirectory()) {
		ControllerMap[file] = {
			isDir:true,
			...loadController(path.join(controllerPath,file))
	}
	} else {
	// 是文件
		const controllerName = file.substr(0,file.lastIndexOf('.')) // 获取文件名称
		ControllerMap[controllerName] = {
			isDir:false,
			value:require(path.join(controllerPath,file))
		}
	}
})
return  ControllerMap
}
const errMsg = message = ({code:1,message,data:null})
// 对象拦截器
const createProxy = (controllerMap,app) => {
	return new Proxy(controllerMap), {
		get(target,name) {
			if (target[name]) {
				if (!target[name].isDir) {
					return new Proxy(target[name])// 具体某个文件的内容 {	get(target,name) {
			return (req,res) => {
							if(Object.getOwnPropertyNames(target,value.prototype).includes(name)){
				new target.value(req,res,qpp)[name]()
				} else {
					res.send(errMsg('method not exist'))
				}}}}
				// 如果是文件目录
				} else {
					return createProxy(target[name],app)
							}
			// 名称不存在
			} else {
				return new Proxy({},{
					get() {
					return (req,res) => {
						res.sed(errMsg('controller not exist')
					}
}
})
}
			}
		}
	}
}

const controllerMap = loadController(path.join(_dirname,'../controller')) 

//_dirname:根目录
module.export = app => {
	app.controller = createProxy(controllerMap ,app)
	return(req,res,next) => {
		next()
	}
}
// 获取文件目录下的所有文件,读取文件目录信息,proxy处理,返回proxy对象。
// 通过.的方式获取方法

service中间件的实现


extend中写index.js

// 对外暴露
const controller = require('./controller ')
const service = require('./service ')

module.export = {
	controller,
	service 
}

app.js

const {controller,service} = require('./extend')
// 添加中间件
app.use(controller)
app.use(service)
// 给app绑定两个属性,直接通过文件名访问下面的方法

config配置
config.env.dev.js

// 
module.exports = {}

6.10
从8:00开始
npm install @bigbighu/altas@0.0.1
config.env.prod.js
config.js

const dev = require('./config.env.dev')
const prod = require('./config.env.prod')
const common = require('@bigbighu/altas')

const config = {
	dev,
	prod
}

// 添加common
for (let i in config) {
	config[i]['common'] = common
}

module.export = config

index.js

const config = require('./config')
const env = config[process.env.NODE_ENV] || config.dev

module.exports = env

sequelize介绍
sequelize
ORM通过对对象和数据库关系的描述。
对数据库操作,调用api,不用管sql,数据库增删改查,调用正确的api

npm install sequelize@5.22.3

modules下面的indx

// 引入核心模块
const fs = require('fs')
// 数据模型定义 
// 1. sequelize连接数据库
const config = require(__dirname + '/../config').db
// 数据库信息,
const {database,username,password} = config
//排除其他属性
const _ = require('lodash') // js工具库
const sequelize =  new Sequelize(database,username,password,_.pick(config,['port','dialect','host'])) // 传入其他的信息
// 2. 读取moduel,添加到对象
cosnt db = {}
// 获取当前文件名
const basename = path.basename(__filename)
fs.readddirSync(_dirname)
	.filter(file => {
	return(file.indexOf('.') !== 0 && file !== basename && file.slice(-3) === '.js' )
})
	.forEach(file => {
		// 获取每一个对象
		const model = sequelize['import'](path.join(__dirname,file))
		db[model.name] = model // 模型的对象的集合	
	})
// 创建关联关系
Object.keys(db).forEach(modelName => {
	if (db[modelName].associate) {
	db[modelName].associate(db)
	}
})
db.sequelize  = sequelize 
db.Sequelize = Sequelize
module.export = db

npm install lodash
pick, 选中的对象,1个是对象,2是选中的属性
config.env.dev.js

// 
module.exports = {
	// 数据库的配置
	db: {
		database:'cms',
		host:'127.0.0.1',
		port:3306,
		username:'root',
		password:'',
		dialect:'mysql' // 数据库类型
	}
}

安装mysql,通过navicat连接数据库

Sequelize中的模型

应用层模型的创建
application.js

module.exports = (sequelize,Datatypes) => {
// 模型的创建 ,表的名称,表的描述
const application = sequelize.define('application',{
	id: {
	type:Datatypes.INTEGER // 校验
	allowNull:false // 不允许为null
	primaryKey:true // 是否为主键
	autoIncrement: true // 是否递增
	},
	name: {
		type:Datatypes.STRING(128)
		allowNull:false
	},
	application_string: {
		type:Datatypes.STRING(256)
		allowNull:true
	},
	// 模型的配置
	{
	freezeTableName:true // 
	timestamps:false // 创建事件
	}
})
	return application 
} 

page模型的创建
page.js

const dayjs = require('dayjs')
module.export = (sequelize,Datatypes) => {
	const page = sequelize.define('page',{
	// 列的相关描述
	id: {
		type:Datatypes.INTEGER // 校验
		allowNull:false // 不允许为null
		primaryKey:true // 是否为主键
		autoIncrement: true // 是否递增
	},
	// page表和应用表关联
	application_id: {
		type:Datatypes.INTEGER 
		allowNull:false
	},
	name:{
		type:Datatypes.STRING(128)
		allowNull:false
	},
	cover: {
		type:Datatypes.STRING(100)
		allowNull:false
	},
	page_type: {
		type:Datatypes.INTEGER 
	},
	is_abled: {
		type:Datatypes.INTEGER 
	},
	page_string: {
		type:Datatypes.STRING(256)
		allowNull:false
		// get和set监听
		get() {
			const page_string = this.getDataValue('page_string ')
			return page_string ? JSON.parse(page_string)
		}
	},
	create_time: {
		type:Datatypes.DATE,
		get() {
			const create_time = this.getDataValue('create_time')
			return create_time ? dayjs(create_time).format('YYYY-MM-DD HH:mm:ss'): ''
		}
	},
		update_time: {
		type:Datatypes.DATE,
		get() {
			const update_time = this.getDataValue('update_time')
			return update_time ? dayjs(update_time).format('YYYY-MM-DD HH:mm:ss'): ''
		}
	},
	creator: {
		type:Datatypes.STRING(10)// 校验
		allowNull:false
	},
	share_desc: {
		type:Datatypes.STRING(100)// 校验
	},
	share_image: {
		type:Datatypes.STRING(100)// 校验
	}
	},
	{
	freezeTableName:true // 
	timestamps:false // 创建事件
	})
	return page
}

npm install dayjs // 时间插件,对格式、时间计算

components.js

module.exports = (sequelize,Datatypes) => {
	const components = sequelize.define('components',{
		id: {
			type:Datatypes.INTEGER // 校验
			allowNull:false // 不允许为null
			primaryKey:true // 是否为主键
			autoIncrement: true // 是否递增
	},
	page_id: {
		type:DataTypes.INTEGER,
		allowNull:false 
	},
	component_string: {
		type:DataTypes.STRING(500)
		allowNull:false
		get() {
			const str = this.getDataValue('component_string')
			return str ? JSON.parse(str) : ''
		}
	},
	sort: {
		type:DataTyper.INTEGER,
		allowNull:false
	},
	name:{
		type:DataTyper.STRING(20),
		allowNull:false
	}
	},
	{
	freezeTableName:true // 
	timestamps:false // 创建事件
	})
	return components;
}

user.js 用户模型

module.exports = (sequelize,Datatypes) => {
	const user = sequelize.define('user',{
		id: {
			type:Datatypes.INTEGER // 校验
			allowNull:false // 不允许为null
			primaryKey:true // 是否为主键
			autoIncrement: true // 是否递增
	},
	user_name {
		type: Datatypes.STRING(20)
		allowNull:false
	},
	password:{
		type: Datatypes.STRING(100)
		allowNull:false
	}
	},
	{
	freezeTableName:true // 
	timestamps:false // 创建事件
	});
	return user
}

cms数据表关联关系
一对一关系: a和b
belongsto
hasmany 一对多:
多对多

application

application.associate = models => {
	application.hasMany(models.page,{
	// 外键
	foreignKey:'application_id'
	})
	
}

page.js

page.associate = models => {
	// 应用表page属于
	page.belongsTo(models.application,{
		foreighKey:'application_id'
	})
	// 当前页面,所有组件,通过page_id
	page.hasMany(models.components,{
	foreignKey:'page_id'
	})
}

components.js

components.associate = models => {
	components.belongsTo(models.page,{
		foreignKey:'page_id'
	})
}

应用,组件,页面表有关联关系。user没有

添加热更新:
修改了代码,添加console.log() 保存后不会重新启动
更改后,改写的代码能够立即生效
npm i nodemon@2.0.7 -S

start 加 " nodemon --exec node ./bin/www"

baseController封装
cms和h5两类控制器,校验的时候,校验实例的创建,都存放在公共的类中。
业务特征
base.js

//  extend中的class 
const Controller = require('../extend/controller').Controller
const Parameter = require('parameter')
// 生成Parameter 实例
const parameter = new Parameter({validateRoot:true})
class BaseController extends Controller {
	response(data) {
		return this.res.send(data)
	}
	// 接口正常响应
	success(data = {}, code = 10000,message = 'success') {
		return this.res.send({
			code,
			data,
			message
		})
	}
	error(data = null, code = -1,message = 'error') {
		return this.res.send({
			code,
			data,
			message
		})
	}
	// 对前端传进来的参数初步的校验
	validate(rules,data) {
		try {
		// 拿到Parameter 
		return parameter.validate(rules,data)
		} catch (e) {
		return e.message
		}
	}
	getValidate (value) {
		return value.map(v => {
			return `${v.field} ${v.message}`
		}).join(';')
	}
} 
module.exports = BaseController 

npm i parameter

baseService
业务层面,继承base文件,定义状态,公共方法和属性
base.js

const Sequelize = require('sequelize')
const models = require('../models')
class BaseService {
	static model = models
	static Sequelize  = Sequelize 
	
	static success(data,message,code) {
		return {
			code: code || 10000,
			message: message || 'success', // 默认
			data
		}
	}
	static error (data = null, message,code = -1) {
		return {
			data,
			message,
			code
		}
	}
}
module.exports = BaseService 

node启动前,调用中间件

const {controller,service} = require('./extend')
app.use(controller(app))
app.use(controller(service))

创建新增页面数据的接口

首先定义路由
routes 下的index

// 路由有2种,router.get/use
module.exports = (app,config) => {
	router.use(`/${config.common.cmsApi}`,require('./cms')(app,config))
	return router
}

cms.js
定义所有路由

const express = require('express')
const router = express.Router()

module.export = (app) => {
	// 所有的路由
	const { controller } = app
	// 注册接口,cms定义在controller下,调用方法
	router.post('/addPageJson', controller.cms.addPageJson)
	// router文件定义接口路径
	router.post('/updateCmsJson',controller.cms.updateCmsJson)
	return router

	// 
}

controller下
cms.js

const BaseController = require('./base')
class CmsController extends BaseController  {
	async addPageJson () {
		const {req} = this
		const validate = this.validate({
			name: {required:true,type:'string'}
		}, req.body)
		if (validate) { 
			return this.error(this.getValidate(validate))
		}
		// 校验通过,往数据库添加数据
		const result = await this.app.service.cms.addPageJson(req.body)
		return this.response(result) // 返回接口信息
		
	}
}
module.exports = CmsController 

service
cms.js

const _ = require('lodash') // 排除对象
const dayjs = require('dayjs')
class CMSService extends BaseService {
	static async addPageJson(data) {
		const { model } = this 
		// 分段插入,不能保证插入成功,因此创建一个事物,如果有报错,需要将整个事物回滚
		const t = await model.sequelize.transaction()
		// 新增页面数据
		try { 
		const { name, cover,shareDesc,shareImage,componentList,creator = ""} = data
		cosnt pageString = _.omit(data,['name','cover','componentList','shareDesc','shareImage'])
		// 当前页面数据
	const insert_page =	await model.page.create({
			application_id: 1,
			name,
			cover,
			page_type: 0,
			is_abled:0,
			page_string:JSON.stringify(pageString),
			create_time:dayjs.format('YYYY-MM-DD HH:mm:ss')
			update_time:dayjs.format('YYYY-MM-DD HH:mm:ss')
			creator,
			shareDesc,
			shareImage
		})
		// 组件数据
		const componentData = componentList.map(data => {
			const {sort} = data
			return {
				page_id:insert_page.id,
				component_string:JSON.stringify(data.data)
				name:data.name,
				sort
			}
		})
		// 批量插入
		await model.components.bulkCreate(componentData)
		await t.commit() // 事物的commit
		return this.success({
			id: insert_page.id
		})
		} catch(e) {
			// 回滚
			await t.rollback()
			return this.error('接口调用失败')
		}
	} 
}
module.exports = CMSService

npm i mysql2@2.2.5

cosnt BaseService = require('./base')

使用postman,模拟端口

const routes = require('./routes')
const config = require('./config')
app.use(routes(app,config))

controller编辑页面数据接口

// 2. controller下的cms.js
async updateCmsJson () {
	const {req} = this
	const data = {
		id: req.body.id
	}
	const validate = this.validate({
		id: {required:true,type:'number'}
	})
	if (validate) {
		return this.error(this.getValidate(validate))
	}
	// 调用service,传递参数
	await this.app.service.cms.updateCmsJson(req.body)
}

service下的cms.js

static async updateCmsJson(data) {
	const { model } = this
	const t = await model.sequelize.transaction()
	const updateId = data.id
	try {
		const {name,cover,shareDesc,shareImage,componentList} = data
		cosnt pageString = _.omit(data,['name','cover','componentList','shareDesc','shareImage'])
		await model.page.update({
			name,
			cover,
			page_string: JSON.stringify(pageString)
			update_time:dayjs().formate('YYYY-MM-DD HH:mm:ss')
			share_desc:shareDesc
			share_image:shareImage
		}, {
			where: {
			id:updateId // 找到表的数据进行更新
			}
		})
		await model.components.destroy({
			// 传入查询条件
			where: {
				page_id:updateId
			}
		}) // 删除对应的api用destroy
		const componentData = componentList.map(data => {
			const { sort,name } = data
			const componentString = _.omit(data,['sort']) // 获取除了sort外的字段
			return {
				page_id: updateId,
				component_string:JSON.stringify(componentString.data),
				sort,
				name
			}
		})
		await model.components.bulkCreate(componentData)
		await t.commit()
	} catch(err) {
		await t.rooback()
		return this.error('调用接口失败')
	}
} 

获取详情页面接口
cms.js

// 1. 路由
router.get('/getPageJson',controller.cms.getPageJson)

controller 定义 getPageJson

async getPageJson () {
	const {req} = this // 获取当前请求
	// get请求,参数拼接在url地址上 获取到参数
	const {id} = req.query // url地址取id
	const result = await this.app.service.cms.getPageData({
		id,type:'cms'
	})
	return this.success(result)
}

service
cms.js

static async getPageData(data) {
	const { model } = this
	const { id, type } = data
	try {
	
		// 对page表和组件表进行关联查询
		// 获取某一条数据
	const res  = await model.page.findOne({
			include:[
			{
				model:model.components
			}
			],
			where: {
				id // id查找页面,页面和组件进行关联,查询结果是页面下所有组件数据
			},
			order:[[sequelize.col('sort'),'asc']]
		})
		// 对数据进行处理
		let result = {}
		if (res) {
			const {
				name,
				color,
				bgColor,
				cover,
				components,
				share_desc,
				share_image,
				page_string
			} = res
			// 数据组装
			const pageData = {
				id: res.id,
				name,
				color,
				bgColor,
				cover,
				shareDesc:share_image,
				shareImage:share_image,
				backgroundColor:page_string.backgroundColor,
				backgroundImage:page_string.backgroundImage
			}
			const componentList = []
			components.forEach((component_string,name,id) => {
				// 创建componentData
				componentData = _.pick(component_string,['id','name'])
				componentData.data = _.omit(component_string,['id','name'])
				componentData.name = name
				componentData.id = id
				if (type == 'cms') {
					componentList.push(componentData)
				} else {
					// h5页面,生效时间内显示,外不显示
					if (component_string.validTime.length) {
						if (dayjs(component_string.validTime[0]).isBetween(new Data.getTime(),component_string.validTime[1])) {
													  componentList.push(componentData)
				//如果不存在,就显示
						} else {
																			  componentList.push(componentData)

						}
					}
				}
			})
			result = {
				...pageData,
				componentList
			}
			return result
		}
		
	} catch (err) {
	return this.error('接口调用失败')
	}
}

h5 获取页面详情数据接口

router.get('/getH5PageJson',controller.cms.getH5PageJson)

controller

async getH5PageJson() {
		const {req} = this // 获取当前请求
	// get请求,参数拼接在url地址上 获取到参数
	const {id} = req.query // url地址取id
	const result = await this.app.service.cms.getPageData({
		id,type:'h5'
	})
		return this.success(result)
}

用户管理列表的接口

router.get('/getPageList',controller.cms.getPageList)

controller

// 列表接口 需要1.分页功能,2.接收前端传来的form查询条件
async getPageList() {
	const { req } = this
	const data = {
		pageNum:Number(req.query.pageNum),
		pageSize:Number(req.query.pageSize),
		name:req.query.name,
		id:req.query.id,
		creator:req.query.creator,
		is_abled:req.query.isAbled,
		startData:req.query.startData,
		endData:req.query.endData
	},
	const validate = this.validate({
		pageNum: {required:true,type:'number'},
		pageSize:{required:true,type:'number'}
	},data) {
		if (validate ) {
			return this.error(this.getValidate(validate))
		}
		const result = await this.app.service.cms.getPageList(data)
		return this.success(result)
	}
}

service

static async getPageList() {
	const { pageNum,pageSize,name,id,creator,is_abled,startData,endData} = data
	const {Op} = this.Sequelize
	try{
		const where = {}
		if (name) {
			where.name = {
				// 模糊查询,前后模糊
				[Op.like]: `%${name}%` 
			}
		}
		if (id) {
			where.id = id
		}
		if (creator) {
			where.creator = creator
		}
		if (is_abled) {
			where.is_abled = is_abled
		}
		if (startData || endData) {
			where.create_time = {
				[Op.lt]:startData,
				[Op.gt]:endData
			}
		}
		const page = await this.model.page.findAndCountAll({
			where,
			offset: (pageNum - 1) * pageSize,//当前查询的数量
			limit:pageSize,//查询的条数
			order:[['updata_time','desc']]
		}) // 接收查询条件,返回查询条数
		return {
			list:page.rows,
			total:page.count,
			pageNum,
			pageSize
		}
	} catch(err) {
		return null
	}
}

活动页的删除接口

router.post('/deletePage',controller.cms.deletePage)

controller

async deletePage () {
	const { req } = this
	const { id } = req.body
	const validate = this.validate({
		id: {required:true,type:'Number'}
	},{id}) 
	if (validate) {
		return this.error(this.getValidate(validata))
	}
	const result = await this.app.service.cms.deletePage(id)
	return this.success(result)
	
}

service

static async deletePage(id) {
	const {model} = this
	try {
		await model.components.destroy({
			where: {
				page_id: id
			}
		})
		await model.page.destroy({
			where: {
				id
			}
		})
		return {}
	} catch(err) {
		return null
	}
}

文件上传接口
routes

router.post('/file/uplosd',contrller.cms.fileUpload)

controller

const ftp = require('../utils/ftp')
async fileUpload () {
// 获取当前文件,文件上传服务器,返回图片地址
	const { req } = this
	const from = formidable({multiples:true})
	form.parse(req, (err,fields,files) => {
		if (err) {
			return
		}
		ftp(files.file.filepath,files.file.originalFilename)
		return this.success(`http://43.129.13.3/${files.file.originalFilename}`)
		
	})
}

安装插件,获取前端数据
npm i formidable@2.0.1
npm i ftp@0.3.10 连接ftp服务器
调用ftp,工具方法定义在utils


登录接口

router.post('/login',controller.cms.login)

controller

async login () {
	const {req} = this
	const {username,password} = req.body
	const data = {
		username,
		password
	}
	const validate = this.validate({
		username:{required:true,type:'string'},
		password:{required:true,type:'string'},
	},data)
		if (validate) {
			return this.error(this.getValidate(validate))
		}
	const result = await this.app.service.cms.login(data)
	if (result) {
		return this.success(result)
	} else {
		return this.error('账号密码错误')
	}
}

service

const { createHmac } = require('crypto')
static async login () {
	// 用户传递的密码, 通过加密方式和数据库
	const { username, password } = data
	const {model} = this
	try {
		const user = await model.user.findOne({
			where: {
				user_name:username
			}
		})
		const secret = 'cmsSecret'
		const hash = createHmac('sha256',secret)
					 .update(password)
					 .digest('hex')
		if (hash === user.password) {
		return {
			token: hash,
			username: user.user_name
			}
		}
		return null 
	} catch(err) {
		return null
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值