SCF构建小型服务端并结合uni-app

举报
代码哈士奇 发表于 2021/06/15 17:27:48 2021/06/15
【摘要】 SCF构建小型服务端并结合uni-app

推荐使用UniCloud 但是如果你实在不想用uniCloud
那么这篇文章带你使用mysql类型云数据库以及云函数来实现用户系统
使用SCF构建小型服务端并结合uni-app()小程序

我们这里手写了一个nodejs环境下的用户体系
使用了之前写的一个数据库连接插件dmhq-mysql-pool比较垃圾 凑合用
文档地址为 https://github.com/dmhsq/dmhsq-mysql-pool/blob/main/README.md
也使用了md5 npm install js-md5

这里使用邮箱发送验证码
先在本地写好 再上传云函数

配置数据库连接

安装 npm install dmhsq-mysql-pool

新建一个文件db.js

const database = require("dmhsq-mysql-pool");
const configs = {
	host: 'xxxx',
	port: 'xxxx',
	user: 'xxxx',
	password: 'xxxx',
	database: "xxxx"
}
let user = new database(configs).table("user")
let codes = new database(configs).table("email")
module.exports = {
	user,
	codes
};

用户数据表名 user
在这里插入图片描述

验证码表 名email 由于只用到邮箱验证码
在这里插入图片描述

配置邮箱发送模块

这里的user 和 pass 为STMP获取 在各大邮箱的设置可以找到
邮箱转发服务 npm install nodemailer
nodemailer文档

const nodemailer = require('nodemailer')

const transporter = nodemailer.createTransport({
	service: 'qq', // no need to set host or port etc.
	auth: {
		user: 'xxxxx@qq.com',
		pass: 'xxxxxxx'
	}
});

const sendCode = async (email,code,time) => {
	let message = {
		from: "验证码<xxxxx@qq.com>",
		to: email,
		subject: "验证码服务",
		html: `<html>
					<head>
						<meta charset="utf-8">
						<title></title>
					</head>
					<body>
						<div>
							<p style="font-size: 20px;">欢迎您使用,您的验证码为 
							<span style="color: blue;font-size: 30px;font-weight: 800;">${code}</span> ,有效时间为${time/60}分钟, 请勿泄露并及时验证</p>
						
							<div style="margin-top: 50px;"></div>
							<p style="color: red;font-size: 25px;">请勿回复</p>
						</div>
					</body>
				</html>`
	};
	await transporter.sendMail(message)
	return {
			code:0,
			msg:"邮件已发送,如果没有收到,请检查邮箱"
		}
}

module.exports = {sendCode};

编写简单用户体系

const {
	user,
	codes
} = require("./db.js");
const {
	sendCode
} = require("./email.js");
const md5 = require("js-md5")

//注册模块
const sign = async (username, password) => {
	const dfp = password
	password = md5(password);
	let isH = await user.where({username}).get();
	if(isH.data.length>0){
		return {
			code: 5742,
			msg: "用户名已被占用",
		}
	}
	const _id = md5(Math.random().toString(36)).substr(0, 10);
	let res = await user.add({
		username,
		password,
		_id
	}).get();
	let rsp = {
		code: 5741,
		msg: "注册失败",
	}
	if (res.code == 0) {
		let userRes = await login(username, dfp);
		rsp = {
			code: 0,
			msg: "注册成功"
		}
		if (userRes.code == 0) {
			rsp.data = userRes.userInfo
		}
	}
	return rsp;
}

//登陆模块
const login = async (username, password) => {
	password = md5(password)

	let res = await user.where({
		username,
		password
	}).get()
	if (!res.data.length) {
		return {
			code: 9001,
			msg: "用户名或者密码错误"
		}
	} else {
		let token = md5(md5(Math.random().toString(36)) + md5(Math.random().toString(36)));
		const tokenExpired = parseInt(Date.parse(new Date()).toString().substr(0, 10)) + 72000;
		const last_login_time = parseInt(Date.parse(new Date()).toString().substr(0, 10));
		let qres = await user.updata({
			token_expired: tokenExpired,
			token,
			last_login_time
		}).where({username}).get();
		if (qres.code == 0) {
			return {
				code: 0,
				userInfo: {
					token,
					tokenExpired,
					username
				}
			}
		} else {
			return {
				code: 9002,
				msg: "登陆失败",
				data: qres
			}
		}

	}
}

//邮箱发送模块
const sendEmailCode = async (email, type) => {
	const randomStr = '00000' + Math.floor(Math.random() * 1000000)
	const code = randomStr.substring(randomStr.length - 6);
	let time = 3600
	const check_time = parseInt(Date.parse(new Date()).toString().substr(0, 10)) + time;
	let res = {}
	res = await sendCode(email, code, time)
	if (res.code == 0) {
		await codes.add({
			email,
			code,
			check_time,
			state: 0,
			type
		}).get();
	} else {
		res = {
			code: 4046,
			msg: "发送失败"
		}
	}
	return res
}


//验证码校验
const checkCode = async (email, code, type) => {
	let result = await codes.where({
		email,
		code,
		type
	}).sort({
		check_time: "DESC"
	}).get();
	let data = result.data;
	let res = {}
	if (data.length == 0) {
		res = {
			code: 4048,
			msg: "验证码错误"
		}
	} else {
		data = data[0]
		const check_times = parseInt(Date.parse(new Date()).toString().substr(0, 10));
		if (data.state == 0 & data.check_time > check_times) {
			await codes.updata({
				state: 1
			}).where({
				email
			}).get()
			res = {
				code: 0,
				msg: "验证通过"
			}
		} else if (data.check_time < check_times) {
			res = {
				code: 4044,
				msg: "验证码失效"
			}
		} else if (data.state == 1) {
			res = {
				code: 4045,
				msg: "验证码已经验证"
			}
		} else {
			res = {
				code: 4048,
				msg: "验证码错误"
			}
		}
	}
	return res;
}


//邮箱绑定
const bind = async (username, email, code) => {
	const check_code = await checkCode(email, code, "bind");
	const check_user = await user.where({
		username,
		email
	}).get();
	let res = {}
	if (check_user.data.length > 0) {
		res = {
			code: 74174,
			msg: "用户已经绑定邮箱"
		}
	} else {
		if (check_code.code == 0) {
			const datas = await user.updata({
					email
				}).
				where({
					username
				}).get();
			if (datas.code == 0) {
				res = {
					code: 0,
					msg: "绑定成功"
				}
			}
		}else{
			res = check_code
		}
	}
	return res;
}

//邮箱解除绑定
const unbind = async (username, email, code) => {
	const check_code = await checkCode(email, code, "unbind");
	const check_user = await user.where({
		username,
		email
	}).get();
	let res = {}
	if (check_user.data.length == 0) {
		res = {
			code: 74175,
			msg: "用户还未绑定邮箱"
		}
	} else {
		if (check_code.code == 0) {
			const datas = await user.updata({
					email: ""
				}).
				where({
					username
				}).get();
			if (datas.code == 0) {
				res = {
					code: 0,
					msg: "解除绑定成功"
				}
			}
		}else{
			res = check_code
		}
	}
	return res;
}

//邮箱校检登录
const checkCodeLogin = async (email, code) => {
	const ress = await checkCode(email, code, "login")
	const isH = await user.where({email}).get();
	if(isH.data.length==0){
		return {
			code:9003,
			msg:"非法邮箱(邮箱未绑定用户)"
		}
	}
	if (ress.code == 0) {
		let token = md5(md5(Math.random().toString(36)) + md5(Math.random().toString(36)));
		const tokenExpired = parseInt(Date.parse(new Date()).toString().substr(0, 10)) + 72000;
		const last_login_time = parseInt(Date.parse(new Date()).toString().substr(0, 10));
		let qres = await user.updata({
			token_expired: tokenExpired,
			token,
			last_login_time
		}).where({
			email
		}).get();
		if (qres.code == 0) {
			res = {
				code: 0,
				userInfo: {
					token,
					tokenExpired,
					email
				}
			}
		} else {
			res = {
				code: 9002,
				msg: "登陆失败",
				data: qres
			}
		}
	}
	return res;
}

//token校检
const checkToken = async (token) => {
	const reqs = await user.where({
		token
	}).get();
	let res = {}
	if (reqs.data.length > 0) {
		const userInfos = reqs.data[0]
		const check_time = userInfos.token_expired;
		const now_time = parseInt(Date.parse(new Date()).toString().substr(0, 10));
		if (check_time > now_time) {
			res = {
				code: 0,
				userInfo: {
					username: userInfos.username
				}
			}
		} else {
			res = {
				code: 7412,
				msg: "token过期"
			}
		}
	} else {
		res = {
			code: 7417,
			msg: "token非法"
		}
	}
	return res;
}

module.exports = {
	sign,
	login,
	sendEmailCode,
	checkCode,
	bind,
	unbind,
	checkCodeLogin,
	checkToken
}

编写主程序

const userCenter = require("./user.js")

index.main_handler = async (event, context) => {

	let noCheckAction = ['sign', 'checkToken', 'login', 'checkCode', 'loginByEmail', 'emailCode']
	let params = event.queryString;
	let res = {}
	const {
		action
	} = params
	if (noCheckAction.indexOf(action) === -1) {
		if (!params.token) {
			res = {
				code: 401,
				msg: '缺少token'
			}
			return res;
		}else{
			let datas = await userCenter.checkToken(params.token)
			if (datas.code != 0) {
				res = datas
				return res;
			}else{
				params.username = datas.userInfo.username;
			}
		}
		
	}
	switch (action) {
		case "sign": {
			const {
				username,
				password
			} = params;
			res = await userCenter.sign(username, password);
			break;
		}
		case "login": {
			const {
				username,
				password
			} = params;
			res = await userCenter.login(username, password)
			break;
		}
		case "emailCode": {
			const {
				email,
				type
			} = params;
			res = await userCenter.sendEmailCode(email, type)
			break;
		}
		case "checkCode": {
			const {
				email,
				code,
				type
			} = params;
			res = await userCenter.checkCode(email, code, type)
			break;
		}
		case "bind": {
			const {
				username,
				email,
				code
			} = params;
			res = await userCenter.bind(username, email, code)
			break;
		}
		case "unbind": {
			const {
				username,
				email,
				code
			} = params;
			res = await userCenter.unbind(username, email, code)
			break;
		}
		case "loginByEmail": {
			const {
				email,
				code
			} = params;
			res = await userCenter.checkCodeLogin(email, code)
			break;
		}
		case "checkToken": {
			const {
				token
			} = params;
			res = await userCenter.checkToken(token)
			break;
		}
		default: {
			res = {
				code: 403,
				msg: "非法访问"
			};
			break;
		}
	}
	

	return res;
}

创建云函数

在这里插入图片描述

注意这里的执行方法
在这里插入图片描述
选择我们的项目文件夹
在这里插入图片描述
上传文件夹

部署

创建触发器

在这里插入图片描述
在这里插入图片描述

点击api名称管理
在这里插入图片描述

编辑触发器
在这里插入图片描述

关闭集成响应

在这里插入图片描述

测试

触发器 拿到请求地址
在这里插入图片描述
测试注册
在这里插入图片描述

做个小程序

这里使用 uni-app做微信小程序

由于我们只用了 用户模块 那么我们就整合用户模块

页面很简单 登录 注册 邮箱登录 邮箱绑定 邮箱解绑
在这里插入图片描述
在这里插入图片描述
页面代码

<template>
	<view class="content">
		<view v-if="is_us">
			<input v-model="username" placeholder="用户名" />
			<input v-model="password" type="password" placeholder="密码" />
			<text @click="is_us=false">邮箱验证码登录</text>
		<button @click="login()">登录</button>
		<button @click="register()">注册</button>
		</view>
		<view v-if="!is_us">
			<input v-model="email" placeholder="邮箱" />
			<input v-model="code" placeholder="验证码" />
			<text @click="is_us=true">账号密码登录</text>
			<button @click="sendEmail('login')">发送验证码</button>
		<button @click="loginEm()">登录</button>
		</view>
		<view>
			<view>用户名:{{userInfo.username}}</view>
			<view>token过期时间:{{userInfo.tokenExpired | timeDel }}</view>
			<view>token:{{userInfo.token}}</view>
		</view>
		<view v-if="userInfo.token!=''">
			<input v-model="email" placeholder="邮箱" />
			<input v-model="code" placeholder="验证码" />
			<button @click="sendEmail('bind')">发送绑定验证码</button>
			<button @click="sendEmail('unbind')">发送解绑验证码</button>
			<button @click="bindEm()">绑定</button>
			<button @click="unbindEm()">解绑</button>
		</view>
		<view>
			<view>{{userInfoG}}</view>
			<button @click="getUserInfo()">获取信息</button>
		</view>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				title: 'Hello',
				is_us: true,
				username:"",
				password:"",
				email:"",
				code:"",
				userInfo: {
					username: "未登录",
					token:"",
					tokenExpired:""
				},
				userInfoG:{}
			}
		},
		filters:{
			timeDel(val) {
				if(!val){
					return ""
				}
				var date = new Date(val*1000); //时间戳为10位需*1000,时间戳为13位的话不需乘1000
				var Y = date.getFullYear() + '-';
				var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
				var D = (date.getDate() < 10 ? '0' + date.getDate() : date.getDate()) + ' ';
				var h = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':';
				var m = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()) + ':';
				var s = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds();
				return Y + M + D + h + m + s;
			}
		},
		onLoad() {

		},
		methods: {
			req(action,other){
				return new Promise(resolve=>{
					uni.request({
						method:'POST',
						url:`xxx/userCenter?action=${action}&${other}`,
						success:res=>{
							resolve(res.data)
						}
					})
				})
			},
			getUserInfo(){
				let tokens = uni.getStorageSync('token')
				this.req('checkToken',`token=${tokens}`).then(res=>{
					uni.showToast({
						title:res.msg,
						icon:'none'
					})
					this.userInfoG = JSON.stringify(res)
				})
			},
			register(){
				this.req('sign',`username=${this.username}&password=${this.password}`).then(res=>{
					uni.showToast({
						title:res.msg,
						icon:'none'
					})
					if(res.code==0){
						let userInfo = res.data
						uni.setStorageSync("token",userInfo.token)
						uni.setStorageSync("tokenExpired",userInfo.tokenExpired)
						this.userInfo = userInfo
					}
				})
			},
			login(){
				this.req('login',`username=${this.username}&password=${this.password}`).then(res=>{
					console.log(res)
					uni.showToast({
						title:res.msg,
						icon:'none'
					})
					if(res.code==0){
						let userInfo = res.userInfo
						uni.setStorageSync("token",userInfo.token)
						uni.setStorageSync("tokenExpired",userInfo.tokenExpired)
						this.userInfo = userInfo
					}
				})
			},
			sendEmail(type){
				this.req('emailCode',`email=${this.email}&type=${type}`).then(res=>{
					uni.showToast({
						title:res.msg,
						icon:'none'
					})
				})
			},
			loginEm(){
				this.req('loginByEmail',`email=${this.email}&code=${this.code}`).then(res=>{
					console.log(res)
					uni.showToast({
						title:res.msg,
						icon:'none'
					})
					if(res.code==0){
						let userInfo = res.userInfo
						uni.setStorageSync("token",userInfo.token)
						uni.setStorageSync("tokenExpired",userInfo.tokenExpired)
						this.userInfo = userInfo
					}
				})
			},
			bindEm(){
				let tokens = uni.getStorageSync('token')
				this.req('bind',`username=${this.username}&email=${this.email}&code=${this.code}&token=${tokens}`).then(res=>{
					console.log(res)
					uni.showToast({
						title:res.msg,
						icon:'none'
					})
				})
			},
			unbindEm(){
				let tokens = uni.getStorageSync('token')
				this.req('unbind',`username=${this.username}&email=${this.email}&code=${this.code}&token=${tokens}`).then(res=>{
					console.log(res)
					uni.showToast({
						title:res.msg,
						icon:'none'
					})
				})
			}
		}
	}
</script>

<style>
	.content {
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
	}

	.logo {
		height: 200rpx;
		width: 200rpx;
		margin-top: 200rpx;
		margin-left: auto;
		margin-right: auto;
		margin-bottom: 50rpx;
	}

	.text-area {
		display: flex;
		justify-content: center;
	}

	.title {
		font-size: 36rpx;
		color: #8f8f94;
	}
</style>

测试

注册

在这里插入图片描述

登录

在这里插入图片描述

获取个人信息

在这里插入图片描述

绑定/解除绑定邮箱

在这里插入图片描述

邮箱验证码登录

没有绑定则邮箱非法
在这里插入图片描述

数据库状态

在这里插入图片描述
在这里插入图片描述
【本文正在参与“100%有奖|我的Serverless 实战”征稿活动】,活动地址:https://marketing.csdn.net/p/15940c87f66c68188cfe5228cf4a0c3f

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。