Skip to content
uniCloud云对象使用及其相关操作

创建项目

新建项目-勾选uniCloud-阿里云-vue3-点创建

创建云对象以及基础讲解

右键uniCloud- 点击关联云服务空间 - 选择服务空间 - 右键cloudfunctions - 创建云对象 - 起名(haha) - 默认模版 - 确定 - 将下面代码粘贴进去

js
// 云对象内部定义响应工具函数
function createResponse(code, data, message = null) {
	return {
		code, // 1: 成功, -1: 失败
		data, // 返回数据
		message // 错误信息
	};
}

module.exports = {
	_before: function() { // 通用预处理器
		this.startTime = Date.now()
	},
	/**
	 * method1方法描述
	 * @param {string} param1 参数1描述
	 * @returns {object} 返回值描述
	 */
	method1(params) {
		// 参数校验,如无参数则不需要
		if (!params) {
			throw createResponse(-1, null, '参数不能为空')
		}
		// 业务逻辑
		const httpInfo = this.getHttpInfo();
		const method = httpInfo; // 请求方法(大写字符串)
		console.log('method', method) // 拿到请求方法,请求方式,请求参数,header头数据
		// 返回结果
		return createResponse(1, params)
	},
	// 创建用户(仅允许 POST)
	createUser(params) {
		const method = this.getHttpInfo().httpMethod;
		if (method !== 'POST') {
			throw createResponse(-1, null, '仅支持 POST 请求'); // 使用自定义错误格式
		}
		// 业务逻辑
		return createResponse(1, params)
	},
	// 获取用户(允许 GET)
	getUser(params) {
		const method = this.getHttpInfo().httpMethod;
		if (method !== 'GET') {
			throw createResponse(-1, null, '仅支持 GET 请求');
		}
		return createResponse(1, params)
	},
	// 统一后置处理函数
	_after(error, message) {
		if (error) {
			throw error
		}
		message.timeCost = Date.now() - this.startTime
		console.log('message', message); // 计算方法耗时时间
		return message
	}
}
  • createResponse全局统一状态管理
  • _before请求前置处理器,在这里存一下开始请求的时间
  • method1 默认方法
  • this.getHttpInfo() 获取请求的信息,如请求头,请求参数,请求方法名,请求方式等
  • _after 统一后置处理函数,可以在响应时做下处理,如计算耗时时间等等

JWT的生成和校验以及md5加密示例

在当前云对象的目录下打开控制台,运行如下命令

js
npm install jsonwebtoken
npm install md5

右键cloudfunctions - 创建云对象 - 起名(loginAndUser) - 默认模版 - 确定 - 将下面代码粘贴进去

js
const jwt = require('jsonwebtoken');
const secretKey = 'your-secret-key-123456'; // 安全密钥(建议存储在云函数环境变量)
const md5 = require('md5');

async function validateUser(username, password) {
	// 1. 查询用户是否存在(这里假数据,假设用户存在)
	const passwords = md5('123456')
	// 2. 验证密码(需与注册时的加密方式一致)
	const hashedPassword = md5(password).toString(); // 示例:SHA256加密
	if (passwords !== hashedPassword) {
		return false;
	}
	// 3. 返回用户信息
	return true;
}

module.exports = {
	_before: function() { // 通用预处理器

	},
	// 登录生成JWT
	async login(params) {
		const {
			username,
			password
		} = params
		const user = await validateUser(username, password);
		if (!user) {
			throw {
				code: 401,
				message: '用户名或密码错误'
			};
		}
		// 2. 生成 JWT
		const payload = {
			userId: 1, // 用户id
			exp: Math.floor(Date.now() / 1000) + (30) // 过期时间:24小时
		};
		const token = jwt.sign(payload, secretKey);
		return {
			code: 1,
			data: {
				token
			}
		};
	},

	// 查询接口(需要验证jwt才能查询)
	async getUserInfo(params) {
		try {
			// 1. 从请求头获取token
			const httpInfo = this.getHttpInfo();
			const authHeader = httpInfo.headers.token;
			console.log('authHeader', authHeader);
			if (!authHeader || !authHeader.startsWith('Bearer ')) {
				throw {
					code: 401,
					message: '未提供有效的Token'
				};
			}

			const token = authHeader.split(' ')[1];

			// 2. 验证并解析JWT
			const decoded = jwt.verify(token, secretKey);
			// 这里可以添加额外的业务验证逻辑,比如检查用户是否存在

			// 3. 返回用户信息(示例数据)
			return {
				code: 1,
				data: {
					userId: decoded.userId,
					username: 'testuser',
					// 其他用户信息...
				}
			};

		} catch (error) {
			// 错误处理
			let message = '身份验证失败';
			if (error.name === 'TokenExpiredError') {
				message = 'Token已过期,请重新登录';
			} else if (error.name === 'JsonWebTokenError') {
				message = '无效的Token';
			}
			// 返回自定义错误格式
			throw {
				code: -1,
				message: message
			};
		}
	}
}

云对象URL化

右键cloudfunctions - 上传所有的云函数 - 右键cloudfunctions - 打开web控制台 - 云函数云对象 - 对象列表 - 任意找到一个云函数 - 点详情 - 下拉有个云函数URL化 - 点编辑 - 后面输入框输入'/ + 云对象名称' - 打开apifox - 快捷请求 - 请求这个地址然后再加上'/ + 云对象里面具体方法' - 点请求就可以访问到这个函数了

云对象的方法的参数params就是get请求到的参数,如果要查询post请求或者其他请求的参数可以通过this.getHttpInfo()拿到具体信息

在uniapp前端代码访问云对象

这样的好处是不用上传一次去apifox请求一次才能看到数据,可以通过前端代码触发查看对应效果

任意找到一个.vue文件,代码如下:

js
<template>
	<view class="content" @click="sendData">调用云对象方法</view>
</template>

<script setup>
	const loginAndUser = uniCloud.importObject('loginAndUser') // 参数是云对象
	async function sendData() {
		const res = await loginAndUser.login({
			username: '萧',
			password: '123456'
		}) // 调用云对象的方法,也可以传参
		console.log(res);
	}
</script>

<style>
	.content {
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		width: 100%;
		background-color: bisque;
	}
</style>

云公共函数

查看此官方文档

数据库相关

单表增删改查

数据库表设计

js
// 文档教程: https://uniapp.dcloud.net.cn/uniCloud/schema
{
	"bsonType": "object",
	"required": [],
	"permission": {
		"read": true,
		"create": true,
		"update": true,
		"delete": true
	},
	"properties": {
		"_id": {
			"description": "ID,系统自动生成"
		},
		"name": {
			"title": "姓名",
			"bsonType": "string",
			"description": "请输入姓名",
			"trim": "start",
			"minLength": 3,
			"maxLength": 20
		},
		"age": {
			"title": "年龄",
			"bsonType": "int",
			"description": "请输入年龄",
			"trim": "start"
		},
		"gender": {
			"title": "性别",
			"bsonType": "int",
			"description": "用户性别:0保密,1男,2女",
			"defaultValue": 0,
			"enum": [{
				"text": "保密",
				"value": 0
			}, {
				"text": "男",
				"value": 1
			}, {
				"text": "女",
				"value": 2
			}]
		},
		"hobby":{
			"title": "爱好",
			"description": "个人爱好"
		},
		"detial":{
			"title": "详细地址",
			"description": "个人详细地址"
		},
		"created_time":{
			"bsonType": "timestamp",
			"defaultValue": {
				"$env": "now"
			}
		},
		"updated_time": {
			"bsonType": "timestamp",
			"forceDefaultValue": {
				"$env": "now"
			}
		},
		"book_id":{
			"title": "书籍id",
			"description": "书籍表外键id",
			"bsonType": "string",
			"foreignKey": "books._id"
		}
	}
}

右键上传表

JQL方法调用顺序

增删改查

js
const db = uniCloud.databaseForJQL()
const dbSQL = db.collection("default") // 数据库表名
const dbCmd = db.command // 操作符
module.exports = {
	_before: function() { // 通用预处理器
	},
	// 数据库操作
	// 增
	async insertUser() {
		// 单条新增数据
		// let res = await db.collection('default').add({
		// 	name:'消极僧人',
		// 	age:12
		// })

		// 批量新增数据
		let res = await dbSQL.add([{
			name: '消极僧人',
			age: 12,
			hobby: ['篮球', '睡觉'],
			detial: {
				address: "南京"
			}
		}, {
			name: '啊哈哈哈',
			age: 30,
			hobby: ['足球', '跳绳'],
			detial: {
				address: "北京"
			}
		}, {
			name: '你好你好',
			age: 19,
			hobby: ['羽毛球', '杠铃'],
			detial: {
				address: "周口"
			}
		}])

		// 新增失败会抛出异常可以使用try...catch或者.then和.catch捕获到
		return res
	},
	// 删
	async deleteUser() {
		// 通过doc根据id删除
		// let res = await dbSQL.doc("67c7b439bd02205f7bbd0432").remove()

		// 条件查询根据查询到的结果删除(批量删除也可单个删除,满足条件就行)
		// let res = await dbSQL.where("gender == 0").remove()

		// 删除该表所有数据
		// 注意:数据量很多的情况下这种方式删除会超时,但是数据仍会全部删除掉
		let res = await dbSQL.where({
			_id: dbCmd.neq(null) // 将_id非空的删除,也就是删除所有的表数据
		}).remove()

		// 删除成功或者失败可以打印res.affectedDocs如果为0就是删除失败
		return res
	},
	// 改
	async updateUser() {
		// 单条修改
		// let res = await dbSQL.where({
		// 		_id: '67c7b5e07ae708a34691c3ea'
		// 	})
		// 	.update({
		// 		name: '修改后的', // 指定修改某字段数据
		// 	});

		// 更新数组时,以数组下标作为key即可,比如以下示例将数组hoddy内下标为0的值修改为乒乓球
		// 这里注意,如果schema定义了hobby类型为array的话会报错,这里建议就是hobby就只定义字段不加类型校验了
		// let res = await dbSQL.where({
		// 		_id: '67c7c01eeef9cbda9c66882c'
		// 	})
		// 	.update({
		// 		hobby: {
		// 			0: "乒乓球"
		// 		},
		// 		detial:{
		// 			address:'北京' // 将对象的address属性的数据改成北京
		// 		}
		// 	})

		// 批量更新(满足条件的所有数据都会更新)
		// let res = await dbSQL.where({
		// 		age: dbCmd.gt(10)
		// 	})
		// 	.update({
		// 		hobby: {
		// 			0: "乒乓球"
		// 		}
		// 	})
		return res
	},
	// 查
	async getUser() {
		// 查询所有数据
		// let res = dbSQL.get()

		// 查询数据总条数,total字段就是
		// let res = dbSQL.count()

		// 分页查询
		// 查询指定条数(如下只查询两条数据)
		// let res = await dbSQL.limit(2).get()

		// 查询指定条数并跳过多少条(就是跳过前两条,从第三条开始查询数据)
		// skip公式为: skip((pageNum - 1) * pageSize)  pageNum是当前页码,pageSize是每页数量
		// 例如查询每页3条数据,从第2页开始,则limit(3).skip((2 - 1) * 3)
		// let res = await dbSQL.limit(3).skip(3).get()

		// 正序倒序查询
		// 如下就是根据id升序排列
		// let res = await dbSQL.orderBy('_id', 'asc').get()
		// 如下就是根据id降序排列
		// let res = await dbSQL.orderBy('_id','desc').get()

		// 过滤字段(如下代码只返回name字段数据和_id字段数据)
		// let res = await dbSQL.field({
		// 	"name": true
		// }).get()

		// where查询(name为你好你好并且创建时间为1741143064501的数据)
		// let res = await dbSQL.where("name=='你好你好'&&created_time==1741143064501").get()

		// 正则表达式模糊搜索
		let keyword = '你'
		let res = await dbSQL.where({
			name: new RegExp(keyword, "ig") // 不区分大小写和全局匹配,参数一就是正则表达式
		}).get()
		return res
	}
}

联表查询(一对一)

数据库表设计

表一(default)

js
// 文档教程: https://uniapp.dcloud.net.cn/uniCloud/schema
{
	"bsonType": "object",
	"required": [],
	"permission": {
		"read": true,
		"create": true,
		"update": true,
		"delete": true
	},
	"properties": {
		"_id": {
			"description": "ID,系统自动生成"
		},
		"name": {
			"title": "姓名",
			"bsonType": "string",
			"description": "请输入姓名",
			"trim": "start",
			"minLength": 3,
			"maxLength": 20
		},
		"age": {
			"title": "年龄",
			"bsonType": "int",
			"description": "请输入年龄",
			"trim": "start"
		},
		"gender": {
			"title": "性别",
			"bsonType": "int",
			"description": "用户性别:0保密,1男,2女",
			"defaultValue": 0,
			"enum": [{
				"text": "保密",
				"value": 0
			}, {
				"text": "男",
				"value": 1
			}, {
				"text": "女",
				"value": 2
			}]
		},
		"hobby":{
			"title": "爱好",
			"description": "个人爱好"
		},
		"detial":{
			"title": "详细地址",
			"description": "个人详细地址"
		},
		"created_time":{
			"bsonType": "timestamp",
			"defaultValue": {
				"$env": "now"
			}
		},
		"updated_time": {
			"bsonType": "timestamp",
			"forceDefaultValue": {
				"$env": "now"
			}
		},
		"book_id":{
			"title": "书籍id",
			"description": "书籍表外键id",
			"bsonType": "string",
			"foreignKey": "books._id"
		}
	}
}

表二(books)

js
// 文档教程: https://uniapp.dcloud.net.cn/uniCloud/schema
{
	"bsonType": "object",
	"required": ["name"],
	"permission": {
		"read": true,
		"create": true,
		"update": true,
		"delete": true
	},
	"properties": {
		"_id": {
			"description": "ID,系统自动生成"
		},
		"name":{
			"title": "书籍名称",
			"bsonType": "string",
			"trim": "start"
		},
		"author_name":{
			"title": "书籍名称",
			"bsonType": "string",
			"trim": "start"
		},
		"created_time":{
			"bsonType": "timestamp",
			"defaultValue": {
				"$env": "now"
			}
		}
	}
}

查询代码

js
const db = uniCloud.databaseForJQL()
const dbSQL = db.collection("default") // 数据库表名
const dbSQL2 = db.collection("books") // 数据库表名
module.exports = {
	_before: function() { // 通用预处理器
	},
	// 连表查询
	async getUser() {
		// 主表
		const article = dbSQL.getTemp()
		// 副表
		const comment = dbSQL2.getTemp()

		let res = await db.collection(comment, article).get()
		return res
	}
}

一对多和多对多实现思路

一对多

  • 从一个表找出id,拿到id去另一个表找相关联的数据

多对多

  • 做一个中间表,先查第一个表找到外键值,通过这个值找到中间表对应的第二个表的外键,通过这个外键找到第二个表的数据

事务开启提交和回滚

如银行取钱的案例,当一个人付款成功给第二个人加的时候失败了,这时候要做异常捕获回滚数据,如下示例代码

表结构

js
// 文档教程: https://uniapp.dcloud.net.cn/uniCloud/schema
{
	"bsonType": "object",
	"required": [],
	"permission": {
		"read": true,
		"create": true,
		"update": true,
		"delete": true
	},
	"properties": {
		"_id": {
			"description": "ID,系统自动生成"
		},
		"name":{
			"title": "用户名",
			"description": "用户名"
		},
		"price":{
			"title": "余额",
			"description": "用户余额"
		}
	}
}

新增数据

js
{
    "name": "张三",
    "price": 1000
}
js
{
    "name": "李四",
    "price": 1000
}

具体开启和回滚事务代码

全局开启

js
const db = uniCloud.databaseForJQL()
const dbs = uniCloud.database()
const dbCmd = dbs.command // 因为事务只能通过uniCloud.database()开启和回滚,则事务所在的操作符也要是它下面的
const dbSQL = db.collection("price") // 数据库表名
module.exports = {
	async _before() { // 通用预处理器
		// 1. 创建事务实例(需声明涉及的表)
		this.transaction = await dbs.startTransaction()
	},
	// 添加数据
	async addPrice() {
		let res = await dbSQL.add([{
			name: '张三',
			price: 1000
		}, {
			name: '李四',
			price: 1000
		}])
		return res
	},
	// 修改数据(模拟交易)
	async updatePrice() {
		try {
			// 查询数据
			let res = await this.transaction.collection('price').doc("67c7e7e77c8de46bafa428a9").get()
			console.log('res', res);
			let res2 = await this.transaction.collection('price').doc("67c7e7e77c8de46bafa428aa").get()
			console.log('res && res2', res, res2);
			// 判断用户存在
			if (res && res2) {
				// 给第一个余额减100,给第二个余额加100
				const updateAAARes = await this.transaction.collection('price').doc('67c7e7e77c8de46bafa428a9')
					.update({
						price: dbCmd.inc(-100)
					})
				const updateAAARes2 = await this.transaction.collection('price').doc('67c7e7e77c8de46bafa428aa')
					.update({
						price: dbCmd.inc(100)
					})
				// 查询如果第一个余额小于0则抛出异常回滚事务
				const aaaEndRes = await this.transaction.collection('price').doc('67c7e7e77c8de46bafa428a9')
					.get()
				if (aaaEndRes.data.price < 0) {
					// 回滚事务
					await this.transaction.rollback()
					return false
				}
				// 提交事务
				await this.transaction.commit()
				return true
			}
		} catch (err) {
			console.error('err', err);
			// 转账过程出现了问题回滚事务
			await this.transaction.rollback()
			return false
		}
	},
	// 查询数据
	async getPrice() {
		let res = await dbSQL.get()
		return res
	},
	async _after(error, result) {
		if (error) {
			console.log('error', error);
			// 发生错误回滚事务
			await this.transaction.rollback()
		}
		return result
	}
}

只在修改余额逻辑上面添加事务机制

js
const db = uniCloud.databaseForJQL()
const dbs = uniCloud.database()
const dbCmd = dbs.command // 因为事务只能通过uniCloud.database()开启和回滚,则事务所在的操作符也要是它下面的
const dbSQL = db.collection("price") // 数据库表名
module.exports = {
	async _before() {},
	// 添加数据
	async addPrice() {
		let res = await dbSQL.add([{
			name: '张三',
			price: 1000
		}, {
			name: '李四',
			price: 1000
		}])
		return res
	},
	// 修改数据(模拟交易)
	async updatePrice() {
		// 1. 创建事务实例(需声明涉及的表)
		const transaction = await dbs.startTransaction()
		try {
			// 查询数据
			let res = await transaction.collection('price').doc("67c7e7e77c8de46bafa428a9").get()
			console.log('res', res);
			let res2 = await transaction.collection('price').doc("67c7e7e77c8de46bafa428aa").get()
			console.log('res && res2', res, res2);
			// 判断用户存在
			if (res && res2) {
				// 给第一个余额减100,给第二个余额加100
				const updateAAARes = await transaction.collection('price').doc('67c7e7e77c8de46bafa428a9')
					.update({
						price: dbCmd.inc(-100)
					})
				const updateAAARes2 = await transaction.collection('price').doc('67c7e7e77c8de46bafa428aa')
					.update({
						price: dbCmd.inc(100)
					})
				// 查询如果第一个余额小于0则抛出异常回滚事务
				const aaaEndRes = await transaction.collection('price').doc('67c7e7e77c8de46bafa428a9')
					.get()
				if (aaaEndRes.data.price < 0) {
					// 回滚事务
					await transaction.rollback()
					return false
				}
				// 提交事务
				await transaction.commit()
				return true
			}
		} catch (err) {
			console.error('err', err);
			// 转账过程出现了问题回滚事务
			await transaction.rollback()
			return false
		}
	},
	// 查询数据
	async getPrice() {
		let res = await dbSQL.get()
		return res
	},
	async _after(error, result) {
		if (error) {
			console.log('error', error);
		}
		return result
	}
}

遇到的坑

动态参数的条件查询这样写

js
// account_number值是字符串,里面的参数要用""包裹才能查到
let res = await dbSQL.where(`account_number == "${username}"`).get()

// doc查询就不用了,根据id查询的,直接把id放进去就行了
let res = await dbSQL.doc(category_id).get()

URL化post请求参数的body拿不到

js
// 需要转化为对象才能取到,但是要注意,如果不传数据的话body是空字符串,这时候会报错,因此这里要判断如果body是空字符串就要提示它传递参数,如果body支持不传递参数,则应该给这个参数一个默认值
const { category_id } = JSON.parse(this.getHttpInfo().body);

// 通过下面方式判断需要的参数是否传递了
// 新建公共模块utils,放入下面代码
// 定义一个函数来检查必需的参数是否存在
function validateParams(requiredFields, that) {
	const body = that.getHttpInfo().body
	if (body == '') {
		return false
	}
	const obj = JSON.parse(body)
	for (const field of requiredFields) {
		if (!obj[field]) {
			return false; // 如果某个参数不存在,返回 false
		}
	}
	return true; // 所有参数都存在,返回 true
}

module.exports = {
	validateParams
}

// 在需要判断参数的云对象那里右键 - 管理公共模块 - 添加上刚刚选的utils模块,在代码里面如下使用
const {	validateParams } = require("utils")
// 具体对象里面逻辑为
if (!validateParams(['category_name', 'category_id'], this)) {
	return createResponse(-1, null, '参数不完整')
}
// 拿到了参数,可以解析了
const { category_name , category_id } = JSON.parse(this.getHttpInfo().body);

查询到的数据为空也能过判断

js
let res = await dbSQL.where(`account_number == 哈哈`).get()
// 这里注意不能直接写if(res.data) 虽然说是空数组,正常来说进不了判断,但是这里的数组是json字符串,是一直都有值的,因此这里数据要判断它的length
if (res.data.length == 0) {
	return createResponse(-1, null, '账号不存在')
}
// 账号存在,密码对比
if (password == res.data[0].password) {
	// 这里就没问题了
}