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"
}
}
}
右键上传表
增删改查
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) {
// 这里就没问题了
}