---
# 文档型数据库/数据操作/增删改查
> 当前文档链接: https://docs.cloudbase.net/database/doc-crud
云开发提供了多种 SDK 供开发者进行操作文档型数据库,包括小程序 SDK、JS SDK、Node SDK 等
| SDK 类型 | 适用平台 |
| ------------------------------------------------------------------------------------------------------------- | ------------ |
| [小程序 SDK](https://developers.weixin.qq.com/miniprogram/dev/wxcloudservice/wxcloud/guide/database/add.html) | 微信小程序 |
| [JS SDK](/api-reference/webv2/database/fetch) | Web 浏览器 |
| [Node SDK](/api-reference/server/node-sdk/database/fetch) | Node.js 环境 |
---
# 文档型数据库/数据操作/事务操作
> 当前文档链接: https://docs.cloudbase.net/database/transaction
事务操作确保多个数据库操作要么全部成功,要么全部失败,保证数据的一致性。云开发支持数据库事务,并保证事务的 ACID 特性。
:::tip 注意
目前数据库事务只支持在服务端运行,只有 `node-sdk` 支持事务。
:::
### 使用场景
在大多数场景中,单文档完全可以满足需求。但在一些场景中,使用数据库事务的优势更明显:
- **从传统关系型数据库迁移到云开发**:数据模型平滑迁移,业务代码改造成本低
- **涉及多个文档/多个集合的业务流程**:保证一系列读写操作完全成功或者完全失败,防止出现中间态
### 支持的方法
#### 事务流程方法
| API | 说明 |
|-----|------|
| **startTransaction** | 发起事务 |
| **commit** | 提交事务 |
| **rollback** | 回滚事务 |
| **runTransaction** | 自动提交事务 |
#### 事务中的数据操作
| API | 说明 |
|-----|------|
| **get** | 查询文档 |
| **add** | 插入文档 |
| **delete** | 删除文档 |
| **update** | 更新文档 |
| **set** | 更新文档,文档不存在时,会自动创建 |
### 基础事务
#### 参数说明
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| **updateFunction** | `function` | ✅ 是 | 事务更新函数,接收 transaction 参数 |
#### 代码示例
```javascript
const cloudbase = require('@cloudbase/node-sdk')
// 初始化数据库
const app = cloudbase.init({
env: 'your-env-id'
})
const db = app.database()
// 基础事务操作
const result = await db.runTransaction(async transaction => {
// 在事务中进行多个操作
const todoDoc = await transaction.collection('todos').doc('todo-id').get()
if (!todoDoc.data) {
throw new Error('待办事项不存在')
}
// 更新待办事项状态
await transaction.collection('todos').doc('todo-id').update({
data: {
completed: true,
completedAt: new Date()
}
})
// 更新用户统计
await transaction.collection('users').doc('user-id').update({
data: {
completedTodos: db.command.inc(1)
}
})
return { success: true }
})
console.log('事务执行结果:', result)
```
#### 返回结果
```javascript
{
result: { success: true }, // updateFunction 的返回值
errMsg: "runTransaction:ok"
}
```
### 复杂事务示例
#### 清空购物车场景
以"清空购物车"的需求为例,展示数据库事务在复杂业务场景中的使用:
```javascript
// Node.js 环境
const cloudbase = require('@cloudbase/node-sdk')
// 初始化数据库
const app = cloudbase.init({
env: 'your-env-id'
})
const db = app.database()
exports.main = async (event, context) => {
const userId = 'user1'
const transaction = await db.startTransaction()
try {
const usersCollection = transaction.collection('users')
const goodsCollection = transaction.collection('goods')
// 1. 获取用户信息
const user = await usersCollection.doc(userId).get()
const { cart, balance } = user.data
// 2. 获取购物车数据和对应的商品信息
const goods = []
for (const { id } of cart) {
const good = await goodsCollection.doc(id).get()
goods.push(good.data)
}
let totalPrice = 0
for (let i = 0; i < cart.length; ++i) {
// 3. 计算购物车中的商品总价
totalPrice += cart[i].num * goods[i].price
// 4. 更新商品库存
await goodsCollection.doc(goods[i]._id).set({
inventory: goods[i].inventory - cart[i].num
})
}
// 5. 更新用户账户余额和清空购物车
await usersCollection.doc(userId).set({
balance: balance - totalPrice,
cart: []
})
await transaction.commit()
return { success: true, totalPrice }
} catch (error) {
await transaction.rollback()
throw error
}
}
```
### 事务原理
#### 快照隔离
- 在调用 `db.startTransaction()` 开始事务后,"快照"是在第一次读之后才会生成
- 在没有调用 `transaction.commit()` 提交事务前,所有的读写操作都是在"快照"上进行
- 在成功提交事务后,"快照"上的数据才会落盘,相关文档数据完成更新
#### 锁与写冲突
- 当事务修改文档时,会锁定相关文档,使其不受其他更改的影响,直到事务结束
- 如果一个事务无法获取到试图修改的文档的锁,事务会终止,并出现写冲突
- 读取文档的操作不需要与文档修改相同的锁
### 错误处理
推荐在进行事务操作时,使用 `try-catch` 来捕获异常:
```javascript
try {
const transaction = await db.startTransaction()
// 涉及文档更改的事务操作
await transaction.collection('orders').add({
data: { /* 订单数据 */ }
})
// 如果发生错误,抛出异常触发回滚
if (someCondition) {
throw new Error('业务逻辑错误')
}
await transaction.collection('products').doc('product-id').update({
data: { stock: db.command.inc(-1) }
})
await transaction.commit()
} catch (error) {
console.error('事务失败,已自动回滚:', error)
// 发生写冲突时,进行异常处理
}
```
### 事务限制
#### 操作限制
| 限制项 | 说明 | 影响 |
|--------|------|------|
| **操作数量** | 单个事务最多 100 个操作 | 需要合理规划事务范围 |
| **执行时间** | 事务执行时间不能超过 30 秒 | 避免复杂计算和外部调用 |
| **文档操作** | 仅支持 `doc` 方法,不支持 `where` 方法 | 当前事务仅支持单文档操作 |
| **DDL 操作** | 避免在 DDL 操作期间进行事务 | 可能导致事务无法获得锁 |
#### 注意事项
```javascript
// ❌ 错误示例:在事务中进行外部调用
await db.runTransaction(async transaction => {
// 不要在事务中调用云函数或外部 API
const result = await wx.cloud.callFunction({
name: 'someFunction'
})
// 事务操作...
})
// ✅ 正确示例:先获取数据,再执行事务
const externalData = await wx.cloud.callFunction({
name: 'someFunction'
})
await db.runTransaction(async transaction => {
// 使用预先获取的数据
await transaction.collection('data').add({
data: externalData.result
})
})
```
---
# 文档型数据库/数据操作/聚合搜索
> 当前文档链接: https://docs.cloudbase.net/database/aggregate
聚合主要用来处理数据(统计平均值,求和等)并返回计算后的数据结果。
## 流水线/管道
聚合是一个流水线式的批处理作业,初始文档经过多个阶段的流水线处理后,得到转换后的聚合结果。
假设已有一个集合 **books**,其中包含以下格式记录:
```json
[
{
"_id": "xxx",
"category": "Novel",
"name": "The Catcher in the Rye",
"onSale": true,
"sales": 80
}
]
```
聚合示例如下:
```javascript
// 云函数端示例
const cloudbase = require("@cloudbase/node-sdk");
const app = cloudbase.init();
const db = app.database();
const $ = db.command.aggregate;
exports.main = async (event, context) => {
const res = await db
.collection("books")
.aggregate()
.match({
onSale: true, // 是否正在出售
})
.group({
// 按 category 字段分组
_id: "$category",
// 让输出的每组记录有一个 avgSales 字段,其值是组内所有记录的 sales 字段的平均值
avgSales: $.avg("$sales"),
})
.end();
return {
res,
};
};
```
第一阶段:**match** 阶段过滤出了集合中的文档数据(`onSale:true` 表示找出正在出售的书籍)并传给下一个阶段。
第二阶段:**group** 阶段基于 `category` 字段进行分组,并统计出每组中所有记录的 `sales` 字段平均值。
## API 及操作符
参考 Node SDK API 文档,一览所有的聚合阶段 [API](/api-reference/server/node-sdk/database/aggregate/) 及 [操作符](/api-reference/server/node-sdk/database/aggregate/aggregateCommand)。
## 优化执行
### 利用索引
**[match](/api-reference/server/node-sdk/database/aggregate/stages/match)** 和 **[sort](/api-reference/server/node-sdk/database/aggregate/stages/sort.md)** 如果是在流水线的开头的话是可以利用索引的。**[geoNear](/api-reference/server/node-sdk/database/aggregate/stages/geoNear.md)** 也可以利用地理位置索引,但要注意的是,**[geoNear](/api-reference/server/node-sdk/database/aggregate/stages/geoNear.md)** 必须是流水线的第一个阶段。
### 尽早缩小数据集
在不需要集合的全集情况下,应该尽早的通过 **[match](/api-reference/server/node-sdk/database/aggregate/stages/match)**、**[limit](/api-reference/server/node-sdk/database/aggregate/stages/limit.md)** 和 **[skip](/api-reference/server/node-sdk/database/aggregate/stages/skip.md)** 缩小要处理的记录数量。
## 注意事项
除了 **match** 阶段,在各个聚合阶段中传入的对象中,可使用的操作符都是聚合操作符,需要特别注意的是,**match** 进行的是查询匹配,因此语法同普通查询 **[where](/api-reference/server/node-sdk/database#where)** 的语法,用的是普通查询操作符。
## FAQ
### Sort exceeded memory limit of 104857600 bytes
```
{"Error":{"Code":"FailedOperation","Message":"(Location16820) Sort exceeded memory limit of 104857600 bytes, but did not opt in to external sorting. Aborting operation. Pass allowDiskUse:true to opt in."},"RequestId":"1728459320973_0.7685102267589137_33591173-19270342dd4_15"}
```
mongo sort 排序内存溢出问题。
#### 解决方案
1. 合理使用 project,使用更小的数据集进行排序。
2. 合理使用 sort,减少排序字段的数量。
3. 合理使用 match, 尽量在 match 后,或者最后进行 sort。
4. 合理使用索引,使用索引进行排序(如果可能的话)。
---
# 文档型数据库/权限管理/基础权限
> 当前文档链接: https://docs.cloudbase.net/database/data-permission
CloudBase 提供了多层次的数据权限管理机制,确保数据安全的同时满足不同业务场景的权限控制需求。
数据库进行读写时会以 `_openid` 字段作为数据归属判定依据
## 配置方式
在 [云开发平台/文档型数据库/集合管理](https://tcb.cloud.tencent.com/dev?#/db/doc/collection/) 页面,为每个集合设置对应的权限
基础权限控制提供四种预设权限类型,根据用户身份和数据特性选择:
| 权限类型 | 适用场景 | 使用建议 |
| ------------------------------ | ---------------------- | ---------------------------- |
| **读取全部数据,修改本人数据** | 公开内容,如文章、商品 | 适合内容展示类应用 |
| **读取和修改本人数据** | 私人数据,如用户资料 | 适合个人信息管理 |
| **读取全部数据,不可修改数据** | 配置数据,如系统设置 | 适合只读配置和参考数据 |
| **无权限** | 敏感数据,如财务信息 | 适合需要服务端处理的敏感数据 |

## 安全规则权限
安全规则权限是 CloudBase 数据库提供的**文档级权限控制**能力,相比基础权限控制具有更高的灵活性和精确度。
详情请参考 [数据库安全规则详解](/database/security-rules)
---
# 文档型数据库/权限管理/安全规则
> 当前文档链接: https://docs.cloudbase.net/database/security-rules
**安全规则** 是 CloudBase 数据库的 **文档级权限控制** 功能,通过自定义表达式精确控制每条数据的读写权限。
## 配置入口
在 [云开发平台/文档型数据库/集合管理](https://tcb.cloud.tencent.com/dev?#/db/doc/collection/) 选择「权限管理」,点击「切换到安全规则」。
## 基本语法
```json
{
"read": "表达式", // 读取权限控制表达式
"write": "表达式" // 写入权限控制表达式
}
```
> ⚠️ 注意:前端查询条件必须是安全规则的子集,否则访问被拒绝。当安全规则使用 `doc.字段名` 时,查询条件必须包含对应字段。
### 常用模板
## 索引使用注意事项
### 索引数量限制
:::warning 重要提示
建议单个集合的索引数量不超过 **20 个**。索引过多不仅不会提升性能,反而会带来严重的负面影响。
:::
**索引过多的负面影响:**
1. **写入性能显著下降** - 每次插入或更新数据时,需要同步维护所有索引,导致写入操作耗时增加
2. **存储空间和内存压力增大** - 每个索引都会占用额外的存储空间和内存资源
3. **查询性能反而降低** - 当工作集超过 RAM 后,会进行频繁的磁盘交换,查询性能大幅下降
**索引优化建议:**
- **定期审查索引使用情况** - 在 [云开发平台/文档型数据库](https://tcb.cloud.tencent.com/dev#/db/doc/model) 的索引管理页面查看各索引的命中频率
- **清理低频索引** - 对于很少使用或从未使用的索引,及时删除以释放资源
- **优先保留高频索引** - 保留那些能显著提升查询性能的核心索引
- **合理使用组合索引** - 一个精心设计的组合索引可以替代多个单字段索引
### 唯一性限制
创建索引时,索引属性选择**唯一**,即可添加唯一性限制。此限制会要求集合中**索引字段对应的值不能重复**。
例如,某个集合内建立了索引字段 `foo` ,且属性为"唯一",那么在这个集合内,要求不能存在 `foo` 字段相同的文档。
> ⚠️ 注意:假如记录中不存在某个字段,则对索引字段来说其值默认为 null。如果索引有唯一性限制,则不允许存在两个或以上的该字段为空/不存在该字段的记录。
### 大小限制
- 索引的字段大小限制不能超过 1024 字节
- 添加索引时,如果集合中已有文档索引字段超过 1024 字节,添加索引时将报错
- 已设置索引的字段,如果插入一个文档,文档中该字段超过 1024 字节将会报错
> 💡 注意:每个英文字母(不分大小写)占一字节的空间,每个中文汉字占两字节的空间。
### 正则表达式查询
> ⚠️ 注意:正则表达式查询无法使用索引提升性能,建议在必要时使用全文索引替代。
---
# 文档型数据库/高级功能/实时推送
> 当前文档链接: https://docs.cloudbase.net/database/realtime
云开发数据库支持监听集合中符合查询条件的数据的更新事件。
## 建立监听
使用 `watch()` 方法即可建立监听,并且返回 `watcher` 对象,用于关闭监听。
符合条件的文档有任何变化,都会触发 `onChange` 回调。
5. 配置外键参数
### 外键配置参数
| 参数名称 | 说明 | 是否必填 | 示例 |
| -------- | -------------------------------- | -------- | -------------------- |
| 外键名称 | 外键约束的名称,用于标识该外键 | 是 | `fk_user_department` |
| 关联库表 | 外键引用的父表名称 | 是 | `departments` |
| 关联字段 | 父表中被引用的字段名称 | 是 | `id` |
| 删除规则 | 当父表记录被删除时的处理方式 | 是 | `级联` |
| 更新规则 | 当父表关联字段被更新时的处理方式 | 是 | `无动作` |
## 删除规则详解
当父表中的记录被删除时,外键约束会根据删除规则对子表中的相关记录进行相应处理:
| 规则名称 | 英文标识 | 行为说明 | 使用场景 |
| ---------- | ------------- | ------------------------------------------ | -------------------------- |
| 无动作 | `NO ACTION` | 如果子表中存在引用记录,则拒绝删除父表记录 | 需要严格控制数据删除的场景 |
| 限制 | `RESTRICT` | 与 NO ACTION 相同,拒绝删除操作 | 保护重要关联数据不被误删 |
| 级联 | `CASCADE` | 自动删除子表中所有引用该记录的数据 | 主从关系明确,需要同步删除 |
| 设为NULL | `SET NULL` | 将子表中的外键字段设置为 NULL | 允许子表记录独立存在 |
| 设为默认值 | `SET DEFAULT` | 将子表中的外键字段设置为默认值 | 有合理默认值的业务场景 |
### 删除规则示例
假设有用户表(users)和订单表(orders),订单表的 `user_id` 字段引用用户表的 `id` 字段:
```sql
-- 用户表
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(50)
);
-- 订单表(带外键约束)
CREATE TABLE orders (
id INT PRIMARY KEY,
user_id INT,
amount DECIMAL(10,2),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
```
**不同删除规则的效果:**
- **CASCADE**:删除用户时,该用户的所有订单也会被删除
- **SET NULL**:删除用户时,相关订单的 `user_id` 设为 NULL
- **RESTRICT**:如果用户有订单,则无法删除该用户
- **NO ACTION**:与 RESTRICT 相同,拒绝删除操作
- **SET DEFAULT**:删除用户时,相关订单的 `user_id` 设为默认值
## 更新规则详解
当父表中被引用字段的值发生更新时,外键约束会根据更新规则处理子表中的相关记录:
| 规则名称 | 英文标识 | 行为说明 | 使用场景 |
| ---------- | ------------- | ------------------------------------------ | -------------------------- |
| 无动作 | `NO ACTION` | 如果子表中存在引用记录,则拒绝更新父表字段 | 需要严格控制主键更新的场景 |
| 限制 | `RESTRICT` | 与 NO ACTION 相同,拒绝更新操作 | 保护关联关系不被破坏 |
| 级联 | `CASCADE` | 自动更新子表中所有引用该值的字段 | 需要保持数据同步的场景 |
| 设为NULL | `SET NULL` | 将子表中的外键字段设置为 NULL | 允许临时断开关联关系 |
| 设为默认值 | `SET DEFAULT` | 将子表中的外键字段设置为默认值 | 有合理默认值的业务场景 |
### 更新规则示例
继续使用上面的用户表和订单表示例:
```sql
-- 订单表(带更新规则的外键约束)
CREATE TABLE orders (
id INT PRIMARY KEY,
user_id INT,
amount DECIMAL(10,2),
FOREIGN KEY (user_id) REFERENCES users(id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
```
**不同更新规则的效果:**
- **CASCADE**:更新用户ID时,相关订单的 `user_id` 也会同步更新
- **SET NULL**:更新用户ID时,相关订单的 `user_id` 设为 NULL
- **RESTRICT**:如果用户有订单,则无法更新该用户的ID
- **NO ACTION**:与 RESTRICT 相同,拒绝更新操作
- **SET DEFAULT**:更新用户ID时,相关订单的 `user_id` 设为默认值
## 实际应用示例
### 示例1:用户与订单关系
```sql
-- 创建用户表
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE
);
-- 创建订单表(带外键约束)
CREATE TABLE orders (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
order_date DATETIME DEFAULT CURRENT_TIMESTAMP,
total_amount DECIMAL(10,2),
-- 外键约束:级联删除,限制更新
CONSTRAINT fk_order_user
FOREIGN KEY (user_id) REFERENCES users(id)
ON DELETE CASCADE
ON UPDATE RESTRICT
);
```
### 示例2:分类与商品关系
```sql
-- 创建商品分类表
CREATE TABLE categories (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
description TEXT
);
-- 创建商品表(带外键约束)
CREATE TABLE products (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
category_id INT,
price DECIMAL(10,2),
-- 外键约束:删除时设为NULL,更新时级联
CONSTRAINT fk_product_category
FOREIGN KEY (category_id) REFERENCES categories(id)
ON DELETE SET NULL
ON UPDATE CASCADE
);
```
---
# MySQL数据库/高级功能/索引管理
> 当前文档链接: https://docs.cloudbase.net/database/configuration/db/tdsql/data-index
索引是提升数据库查询性能的关键技术。通过为常用查询字段建立索引,可以显著提升查询速度和用户体验。
## 如何创建索引
1. 访问 [云开发平台/MySQL数据库](https://tcb.cloud.tencent.com/dev?#/db/mysql/table/default/)
2. 进入索引管理:选择目标表,点击索引管理标签页
3. 管理索引:新建、删除或修改索引配置

---
# MySQL数据库/高级功能/DMC 数据库管理
> 当前文档链接: https://docs.cloudbase.net/database/configuration/db/tdsql/dmc-data-manage
用户可以通过 [DMC数据库管理平台](https://cloud.tencent.com/document/product/1222/98737) 进行管理MySQL数据库,包括创建、编辑、删除、导入、导出等操作。
## 进入DMC平台
1. 访问 [云开发平台/MySQL数据库/数据库设置](https://tcb.cloud.tencent.com/dev?#/db/mysql/setting)

2. 在「账号管理」模块进行创建账号密码,在弹出的对话框中输入账号名、主机、密码等信息
3. 创建账号后,点击「数据库管理」按钮,进入 DMC 工具页面,输入之前创建的账号密码登录
- 类型为 **云开发(微信云托管数据库)**
- 地域已默认选择环境所在地域

## SQL 执行
SQL语句执行是MySQL数据库提供的基本功能,方便开发者通过基础SQL语法执行处理数据库中存储的数据,相较于可视化表编辑方式更加灵活。
进入 DMC 工具后,选择 **SQL 窗口**,即可进入执行面板,进行 SQL 语句编辑

## 导入导出
MySQL 数据库提供导入导出能力,方便开发者进行数据迁移。
进入 DMC 工具后,点击「悬浮工具」,选择「导入导出」,

---
# MySQL数据库/高级功能/备份与回档
> 当前文档链接: https://docs.cloudbase.net/database/configuration/db/tdsql/backup
**MySQL数据库** 提供完善的数据备份和回档功能,帮助您保障数据安全,支持在数据丢失或误操作时快速恢复数据。本文档将详细介绍备份类型、操作流程以及回档方法。
## 数据备份
**MySQL数据库** 支持自动备份和手动备份两种方式:
- **自动备份**:平台每天自动执行一次备份,数据备份保留 7 天
- **手动备份**:用户可根据业务需要主动触发备份操作
:::info 重要说明
- 自动备份当前仅支持「快照备份」,用于原集群快速回档
- 自动备份与手动备份默认限制频率为 1 小时 1 次
:::
### 备份类型
**MySQL数据库** 提供两种备份类型,您可以根据业务场景选择合适的备份方式:
| 备份类型 | 技术原理 | 优势 | 劣势 | 适用场景 |
| ------------ | --------------------------------------- | ------------------------------------------------------------------------ | ----------------------------------- | -------------------------------- |
| **逻辑备份** | 以 SQL 语句方式保存数据库逻辑结构和内容 | • 支持精细的库表级备份
> ⚠️ **重要提醒**:
> - 请妥善保存导出的 SQL 文件,这是您的数据备份
> - 如果数据量较大,建议分批导出
> - 确保导出完成后再进行下一步操作
### 步骤 2:卸载数据库
备份完成后,需要卸载当前的 MySQL 数据库。
1. 进入 [云开发平台/MySQL数据库/数据库设置](https://tcb.cloud.tencent.com/dev?#/db/mysql/setting)
2. 点击右上角「销毁数据库」按钮
3. 确认销毁操作

> ⚠️ **警告**:销毁数据库后,原数据库中的所有数据将被清除,请确保已完成数据备份。
### 步骤 3:重新安装数据库
卸载完成后,重新安装 MySQL 数据库。此次安装将在您的账号下创建数据库实例。
1. 在 [云开发平台/MySQL数据库](https://tcb.cloud.tencent.com/dev?#/db/mysql/table/default/) 页面
2. 点击「开通 MySQL 数据库」按钮
3. 选择 MySQL 版本(推荐 8.0)
4. **关键步骤**:选择私有网络(VPC)和子网
- 如需云函数内网连接,选择云函数所在的 VPC
- 建议选择与其他云资源相同的 VPC,便于内网互联
5. 确认开通

> 💡 **提示**:
> - 开通完成后,数据库将部署在您的腾讯云账号下
> - 系统会自动生成默认表结构
> - 数据库的内网连接地址将在「数据库设置」页面显示
### 步骤 4:导入数据
数据库重新安装完成后,导入之前备份的数据。
#### 4.1 登录 DMC 平台
参考 [步骤 1.1](#11-登录-dmc-平台) 登录 DMC 平台。
#### 4.2 导入表结构和数据
1. 点击「悬浮工具」,选择「导入导出」
2. 点击「数据导入」
3. 执行数据库选择名为「当前环境 ID」的库
4. 选择文件类型为 **SQL**
5. 上传步骤 1 中导出的 `.sql` 文件
6. 点击「确定」开始导入
> 💡 **提示**:
> - 导入成功后即可完成数据迁移
> - 如有删除的数据模型,需手动重建
> - 建议导入后验证数据完整性
## 完整迁移示例
以下是完整的迁移流程总结,可作为操作检查清单:
```
✓ 步骤 1:备份数据
├─ 登录 DMC 平台
├─ 导出表结构和数据(SQL 格式)
└─ 验证 SQL 文件完整性
✓ 步骤 2:卸载数据库
├─ 进入数据库设置页面
└─ 销毁当前数据库
✓ 步骤 3:重新安装数据库
├─ 选择 MySQL 版本
├─ 配置 VPC 和子网
└─ 确认开通
✓ 步骤 4:导入数据
├─ 登录 DMC 平台
├─ 导入 SQL 文件
└─ 验证数据完整性
✓ 步骤 5:配置内网连接(可选)
├─ 获取内网连接地址
├─ 配置云函数 VPC
└─ 更新数据库连接代码
```
## 注意事项
### 数据安全
- 迁移前务必完整备份数据,建议保留多个备份副本
- 导出的 SQL 文件包含敏感数据,请妥善保管
- 迁移过程中数据库不可用,建议在业务低峰期进行
### 应用程序调整
- 迁移后需更新应用程序中的数据库连接地址
- 从公网地址更改为内网地址
- 建议使用环境变量管理数据库连接配置
### 性能优化
- 内网连接延迟通常在 1-5ms,远低于公网连接
- 建议使用连接池提升数据库操作性能
- 详细的性能优化方法请参考 [云函数/调用 MySQL 数据库/最佳实践](/cloud-function/resource-integration/mysql#最佳实践)
## 常见问题
### 云函数连接不上数据库?
请检查以下配置:
1. 云函数是否配置了正确的 VPC 和子网
2. VPC 是否与数据库所在 VPC 一致
3. 数据库连接地址是否使用内网地址
4. 云函数环境变量是否配置正确
## 相关文档
- [数据库管理](/database/configuration/db/tdsql/dmc-data-manage) - DMC 平台使用指南
- [云函数/调用 MySQL 数据库](/cloud-function/resource-integration/mysql) - 云函数内网连接数据库
- [云托管/调用 MySQL 数据库](/run/develop/databases/mysql) - 云托管内网连接数据库
- [云开发资源迁移指南](/quick-start/env-transfer) - 环境迁移完整指南
- [MySQL版本升级](/database/configuration/db/tdsql/up-version) - 数据库版本升级操作
---
# MySQL数据库/其他参考/MySQL版本升级
> 当前文档链接: https://docs.cloudbase.net/database/configuration/db/tdsql/up-version
云开发 MySQL数据库 版本包含 5.7、8.0,当前开通MySQL数据库时默认为8.0版本
如果您当前为5.7版本,则可以通过当前文档进行升级
## 升级流程
通过DMC平台导出导入表结构及数据
1. 导出表结构、数据
2. 升级数据库版本
3. 导入表结构、数据
## 操作步骤
### 1. 登录 DMC 平台
参考 [数据库管理](/database/configuration/db/tdsql/dmc-data-manage) 文档,登录到 DMC 平台

### 1.1 提交工单
若在DMC平台无法正常选择实例,请前往 [云开发平台/MySQL数据库/数据库设置](https://tcb.cloud.tencent.com/dev?#/db/mysql/setting),点击「直连服务」,若需要提交工单,待工单处理完成后,即可正常选择实例

### 2. 导出表结构、数据
点击「悬浮工具」,选择「导入导出」
1. 点击「数据导出」
2. 执行数据库选择名为「当前环境ID」的库
3. 选择文件类型为 **SQL**
4. 选择表导出,导出内容选择 **数据和结构**
5. 点击「确定」按钮,即可导出表结构、数据(为zip格式),解压后为 **.sql** 文件
### 3. 升级数据库版本
1. 进入 [云开发平台/MySQL数据库](https://tcb.cloud.tencent.com/dev?#/db/mysql/setting)
2. 点击右上角「销毁数据库」按钮

3. 重新开通 MySQL 8.0

4. 开通完成后,平台会重新生成系统默认表
### 导入表结构、数据
1. 参考 [1.登录 DMC 平台](#1-登录-dmc-平台) 登录 DMC 平台
2. 点击「悬浮工具」,选择「导入导出」
3. 点击「数据导入」
4. 执行数据库选择名为「当前环境ID」的库
5. 选择文件类型为 **SQL**
6. 上传 [2.导出表结构数据](2-导出表结构数据) 导出的 **.sql** 文件
7. 导入成功后即可完成整体数据迁移,若有删除的数据模型,则手动建立即可
---
# MySQL数据库/其他参考/错误码
> 当前文档链接: https://docs.cloudbase.net/database/configuration/db/tdsql/errorcode
本文档介绍云开发「MySQL 数据库」操作过程中可能遇到的错误码及其解决方法。
## 业务错误码
业务错误码用于标识云开发平台在处理 MySQL 数据库操作时产生的业务逻辑错误。
| 错误码 | 错误信息 | 说明 | 解决方法 |
| ------------------------------------------------ | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| AuthFailure.DataSourceOperationAuthFailure | Permission denied. Please check permissions in the permission module. | 权限拒绝,数据库操作权限不足 | 前往 [云开发平台/MySQL 数据库/权限管理](https://tcb.cloud.tencent.com) 检查并配置权限 |
| AuthFailure.SetOwnerNotAllowed | Setting user identifier field is not allowed. | 不允许设置用户标识字段 `_openid` | 移除对 `_openid` 字段的显式赋值操作,该字段由系统自动管理 |
| AuthFailure.PermissionNotConfigured | Permission not configured. Please configure proper permissions in the permission module. | 未配置数据访问权限 | 前往 [云开发平台/MySQL 数据库/权限管理](https://tcb.cloud.tencent.com) 配置相应权限规则 |
| AuthFailure.AuthError | Permission error. | 权限验证失败,通常是自定义表中缺少系统依赖的 `_openid` 字段 | 确认表结构中包含 `_openid` 字段(VARCHAR 类型),或关闭权限验证 |
| FailedOperation.EmptyDatabaseEndpoint | Database endpoint is empty. | 数据库连接信息为空 | 检查数据库实例是否正常启动,确认连接配置是否正确 |
| FailedOperation.DatabaseConnectError | Failed to establish database connection. | 数据库连接失败 | 检查网络连接、数据库实例状态、账号密码是否正确 |
| FailedOperation.DatabaseExecSqlError | Execute sql error. | SQL 执行失败,详细错误信息请参考「MySQL 原生错误码」章节 | 查看完整错误信息,根据 MySQL 错误码进行排查 |
| FailedOperation.DatabaseSchemaError | Database schema error. | 数据库 schema 元数据信息异常 | 检查表结构定义是否正确,必要时联系客服获取技术支持 |
| FailedOperation.DatabaseBuildSqlError | Build sql error. | SQL 构建失败,可能是请求参数错误或元数据信息缺失 | 检查请求参数格式是否正确,必要时联系客服获取技术支持 |
| FailedOperation.DatabaseDataProcessError | Process data error. | 数据处理失败 | 检查数据格式是否符合要求,必要时联系客服获取技术支持 |
| ResourceNotFound.InstanceNotFound | Database instance not found. | 数据库实例不存在 | 确认实例 ID 是否正确,或前往控制台创建新实例 |
| ResourceNotFound.SchemaNotFound | Database schema not found. | 数据库 schema 不存在 | 确认数据库名称是否正确,或前往控制台创建数据库 |
| ResourceNotFound.TableNotFound | Table not found. | 数据表不存在 | 确认表名是否正确,或前往控制台创建数据表 |
| UnsupportedOperation.TooManyTables | Number of tables exceeds the limit. | 数据表数量超过限制 | 删除不必要的表,或联系客服申请扩容 |
| InvalidParameter.ResponseUnacceptableSingleError | Response does not meet the requirement for a single record. | 响应数据不符合单条记录格式要求 | 检查请求头中的 `Accept` 参数设置,确保与查询结果匹配 |
## MySQL 原生错误码
当业务错误码为 `FailedOperation.DatabaseExecSqlError` 时,错误详情中会包含 MySQL 数据库返回的原生错误码。您可以根据错误码范围判断错误类型,并查阅 MySQL 官方文档获取详细说明。
### 错误码分类规则
MySQL 错误码按照数值范围分为以下几类:
#### Server Error(服务器错误)
**错误码范围**:1000-1999、3000-5000+
**错误特征**:数据库引擎、查询优化器或存储引擎层面的问题
**典型示例**:
```
Error 1054 (42S22): Unknown column 'username' in 'field list'
```
**解决思路**:
- 检查 SQL 语句语法是否正确
- 确认表结构、字段名、索引等是否存在
- 检查数据类型是否匹配
#### Client Error(客户端错误)
**错误码范围**:2000-2999
**错误特征**:客户端驱动程序、连接库或网络层面的问题
**典型示例**:
```
Error 2003 (HY000): Can't connect to MySQL server on '192.168.1.100'
```
**解决思路**:
- 检查网络连接是否正常
- 确认数据库服务是否启动
- 检查防火墙和安全组配置
#### Global Error(全局错误)
**错误码范围**:1-999
**错误特征**:系统级别的通用错误
**典型示例**:
```
Error 13 (HY000): Can't settle log file (OS errno 13 - Permission denied)
```
**解决思路**:
- 检查文件系统权限
- 确认磁盘空间是否充足
- 检查系统资源使用情况
#### CynosDB Error(云数据库产品错误)
**错误码范围**:9000-9999
**错误特征**:腾讯云 CynosDB 产品特定错误
**典型示例**:
```
Error 9449 (08S01): CynosDB serverless instance is resuming, please try connecting again
```
**错误码文档**
[CynosDB 错误码说明](https://cloud.tencent.com/document/product/1003/48104)
### 官方文档参考
根据错误码类型,可查阅以下 MySQL 官方文档:
**Server Error(服务器错误)**
| MySQL 版本 | 官方文档链接 |
| ---------- | ---------------------------------------------------------------------------- |
| MySQL 8.0 | [Server Error Reference](https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html) |
| MySQL 5.7 | [Server Error Reference](https://dev.mysql.com/doc/mysql-errors/5.7/en/server-error-reference.html) |
**Client Error(客户端错误)**
| MySQL 版本 | 官方文档链接 |
| ---------- | ---------------------------------------------------------------------------- |
| MySQL 8.0 | [Client Error Reference](https://dev.mysql.com/doc/mysql-errors/8.0/en/client-error-reference.html) |
| MySQL 5.7 | [Client Error Reference](https://dev.mysql.com/doc/mysql-errors/5.7/en/client-error-reference.html) |
**Global Error(全局错误)**
| MySQL 版本 | 官方文档链接 |
| ---------- | ---------------------------------------------------------------------------- |
| MySQL 8.0 | [Global Error Reference](https://dev.mysql.com/doc/mysql-errors/8.0/en/global-error-reference.html) |
| MySQL 5.7 | [Global Error Reference](https://dev.mysql.com/doc/mysql-errors/5.7/en/global-error-reference.html) |
:::tip 💡 提示
- 遇到错误时,建议先记录完整的错误信息(包括错误码、SQLSTATE 和错误描述)
- 根据错误码范围快速判断问题来源(服务器端 vs 客户端)
- 查阅对应版本的官方文档获取详细的错误说明和解决建议
- 如果问题无法自行解决,可以联系 [技术支持](https://cloud.tencent.com/online-service) 获取帮助
:::
---
# MySQL数据库/其他参考/常见问题
> 当前文档链接: https://docs.cloudbase.net/database/configuration/db/tdsql/faq
## 计量计费相关问题
### 微信云托管中的 MySQL 数据库与云开发中的 MySQL 数据库有什么差异?
**微信云托管环境中的 MySQL 数据库**:
- 采用按量计费方式
- 计量包括计算资源使用量及存储量
- 当天产生的用量会在次日凌晨进行结算及扣费
**云开发中的 MySQL 数据库**:
- 计量同样包括计算资源使用量及存储量
- 扣费方式按照优先级:套餐 > 资源包 > 超限按量
## 使用相关问题
### 访问 MySQL 数据模型响应慢或报错?
**问题原因**:
MySQL 数据库支持自动暂停功能,30 分钟未被访问会自动暂停以减少计算资源消耗。当重新访问时需要启动服务,可能导致首次请求耗时较长。
**解决方案**:
在控制台关闭自动暂停功能来避免此问题。
**操作步骤**:
1. 进入 [云开发平台/MySQL数据库/数据库设置](https://tcb.cloud.tencent.com/dev?#/db/mysql/setting)
2. 关闭「自动暂停」功能
### MySQL如何支持事务
云开发MySQL目前不支持事务操作。如需使用事务功能,建议通过云托管直接调用MySQL原生API来实现。
> 💡 注意:使用原生MySQL API时,需要自行管理数据库连接和事务状态。
#### 实现示例
```js
const mysql = require('mysql2/promise');
exports.main = async () => {
const conn = await mysql.createConnection({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME
});
try {
await conn.beginTransaction();
// 执行多个相关操作
await conn.query('UPDATE accounts SET balance = balance - 100 WHERE user_id = 1');
await conn.query('UPDATE accounts SET balance = balance + 100 WHERE user_id = 2');
await conn.commit();
return { success: true };
} catch (error) {
await conn.rollback();
return { success: false, error: error.message };
} finally {
conn.end();
}
};
```
### 查询条件正确但结果为空
在使用数据库查询时,如果返回空结果,通常有以下两种情况:
1. 没有符合查询条件的数据
2. 数据被权限控制过滤
#### 排查方法
1. **确认数据存在性**
- 在云开发控制台直接查看集合中是否存在目标数据
- 检查数据的创建时间和字段值是否符合预期
2. **检查权限配置**
- 查看集合的基础权限设置是否允许当前用户读取
- 数据库查询时会以 `_openid` 字段作为数据归属判定依据
- 如果使用安全规则,验证规则表达式是否正确
- 确认查询条件是否包含安全规则要求的必要字段
3. **验证查询条件**
- 简化查询条件,逐步排查哪个条件导致结果为空
- 检查字段名称、数据类型和查询语法是否正确
---
# model Documentation
> 数据模型是云开发提供的声明式数据管理解决方案,它构建在云开发数据库之上,为开发者提供了一种更高效、更安全的数据操作方式
# 数据模型/概述
> 当前文档链接: https://docs.cloudbase.net/model/introduce
## 什么是数据模型?
数据模型是云开发提供的**声明式数据管理解决方案**,它构建在云开发数据库之上,为开发者提供了一种更高效、更安全的数据操作方式。
简单来说,数据模型就像是为您的数据定制的"智能管家":
- **定义数据结构**:通过可视化界面定义数据的字段、类型和关系
- **自动数据校验**:确保数据的准确性和一致性
- **处理关联关系**:自动管理数据之间的复杂关系
- **包含多端 SDK**:一次定义,多端使用(小程序、Web、云函数)
- **内置管理界面**:提供开箱即用的数据管理后台
- **AI 智能分析**:利用人工智能挖掘数据价值
> **核心理念**:让开发者专注于业务逻辑,而不是底层数据操作的复杂性。
这是富文本内容
", // 文章正文内容 "author": "张三", // 作者姓名 "email": "zhangsan@example.com", // 作者邮箱 "published": true, // 是否已发布 "viewCount": 1024, // 浏览次数 "tags": ["技术", "前端", "Vue"], // 文章标签 "metadata": { // 文章元数据 "seo": { "keywords": "Vue, 前端开发", // SEO 关键词 "description": "Vue 前端开发教程" // SEO 描述 } }, "coverImage": "https://example.com/cover.jpg", // 封面图片 "publishedAt": "2024-01-15T10:30:00Z", // 发布时间 "status": "published", // 文章状态 "articleNo": "A000001", // 文章编号 "location": { // 发布地点 "type": "Point", "coordinates": [116.4074, 39.9042] // 经纬度坐标(北京) } } ``` ### 第一步:访问数据模型管理页面 1. **访问控制台**:进入 [云开发平台](https://tcb.cloud.tencent.com/dev) 2. **选择数据库**:切换到 [云开发平台/数据库/文档型](https://tcb.cloud.tencent.com/dev?#/db/doc/model/?sourceType=internal_flexdb) 3. **创建模型**:点击「新建模型」 ### 第二步:设计模型结构 以博客系统为例,我们需要创建两个相关的数据模型: #### 文章模型(article) | 字段名 | 字段类型 | 是否必填 | 描述 | |--------|----------|----------|------| | `title` | 文本 | 是 | 文章标题 | | `content` | 富文本 | 是 | 文章内容 | | `summary` | 文本 | 否 | 文章摘要 | | `author` | 文本 | 是 | 作者姓名 | | `email` | 邮箱 | 是 | 作者邮箱 | | `coverImage` | 图片 | 否 | 封面图片 | | `tags` | 数组 | 否 | 文章标签 | | `published` | 布尔值 | 是 | 是否发布 | | `viewCount` | 数字 | 否 | 浏览次数 | | `publishedAt` | 日期时间 | 否 | 发布时间 | | `status` | 枚举 | 是 | 文章状态(草稿/已发布/已下线) | #### 评论模型(comment) | 字段名 | 字段类型 | 是否必填 | 描述 | |--------|----------|----------|------| | `content` | 文本 | 是 | 评论内容 | | `author` | 文本 | 是 | 评论者姓名 | | `email` | 邮箱 | 是 | 评论者邮箱 | | `website` | 网址 | 否 | 评论者网站 | | `article` | 文本 | 是 | 关联的文章Id | | `approved` | 布尔值 | 是 | 是否审核通过 | ## 使用 SDK 操作数据 ### 初始化 SDKVue 3 是一个现代化的前端框架...
", summary: "本文介绍 Vue 3 的基本概念和使用方法", author: "张三", email: "zhangsan@example.com", tags: ["Vue", "前端", "教程"], published: true, status: "published", publishedAt: new Date().toISOString() } }); console.log("文章创建成功:", data.id); ``` #### 查询文章列表 ```javascript // 查询已发布的文章 const { data } = await models.article.list({ filter: { where: { published: { $eq: true }, status: { $eq: "published" } } }, sort: { publishedAt: 'desc' // 按发布时间倒序 }, pageSize: 10, pageNumber: 1 }); console.log("文章列表:", data.records); console.log("总数:", data.total); ``` #### 创建评论 ```javascript // 为文章创建评论 const { data } = await models.comment.create({ data: { content: "这篇文章写得很好!", author: "李四", email: "lisi@example.com", website: "https://lisi.blog", article: "文章ID", // 关联到具体文章 approved: false // 待审核 } }); console.log("评论创建成功:", data.id); ``` #### 关联查询 ```javascript // 查询文章及其评论 const { data } = await models.article.get({ filter: { where: { _id: { $eq: "文章ID" } } }, select: { $master: true, comments: true } }); console.log("文章详情:", data); console.log("评论列表:", data.comments); ``` ## 下一步 恭喜!您已经成功创建了第一个文档型数据模型。接下来可以: * [学习更多 CRUD 操作](/model/sdk-reference/model) * [掌握关联查询技巧](/model/config/relation) * [使用数据管理](/toolbox/manage-data) * [配置数据安全规则](/model/data-permission) 开始构建您的应用吧! --- # 数据模型/数据操作/字段类型 > 当前文档链接: https://docs.cloudbase.net/model/data-field 数据模型字段是数据源的表结构基础,可以理解为 Excel 表格的表头(列),而数据可以理解为 Excel 表格的行。CloudBase 数据模型支持丰富的字段类型,满足各种业务场景的数据存储需求。 ## 字段类型概览 CloudBase 数据模型提供了 **20+ 种字段类型**,涵盖基础数据、格式化验证、媒体文件、内容编辑、时间位置、特殊功能等各个方面: ## 字段类型完整列表 | 字段类型 | DB字段类型 | 使用说明 | 示例 | |---------|-----------|----------------------------------------------------------------|------| | **文本/单行** | `string` | 适合标题、姓名等短文本(最长 4000 字节) | `"张三"` | | **文本/多行** | `string` | 适合描述、备注等长文本(最长 4000 字节) | `"这是一段描述文字"` | | **布尔值** | `boolean` | true 或 false | `true` | | **数字** | `number` | 支持整数和浮点数 | `123456.78` | | **数组** | `array` | 根据数组元素类型进行校验 | `["技术", "前端", "Vue"]` | | **对象** | `object` | 嵌套的键值对结构 | `{"name": "张三", "age": 25}` | | **JSON** | `object` | 复杂的数据结构或动态属性 | `{"title": "博客", "tags": ["技术"]}` | | **邮箱** | `string` | 包含 xx\@yy.zz 格式验证 | `"email@qq.com"` | | **电话/固定号码** | `string` | 0开头的2-3位区号或7-8位号码 | `"027-1234567"` | | **电话/手机号码** | `string` | 符合手机号规范的11位字符串 | `"13812341234"` | | **网址** | `string` | 符合网址规范的字符串 | `"https://example.com"` | | **图片** | `string` | 默认从前端组件获得图片的 cloudId | `"cloud://xxx.xxx.xxx.png"` | | **多媒体/视频** | `string` | 支持 ogm、wmv、mpg、webm、ogv、mov、asx、mpeg、mp4、m4v、avi | `"cloud://xxx.xxx.xxx/video.mp4"` | | **多媒体/音频** | `string` | 支持 opus、flac、webm、weba、wav、ogg、m4a、oga、mid、mp3、aiff、wma、au | `"cloud://xxx.xxx.xxx/audio.wav"` | | **富文本** | `string` | 支持格式化、链接、图片(最长 262144 字节) | `"内容
"` | | **Markdown** | `string` | 支持 Markdown 编辑器和实时预览 | `"# 这是一个Markdown示例"` | | **日期时间** | `number` | 默认从前端组件获取的时间戳(ms),底层db文档型数据库、mysql数据库支持相应的日期时间属性,详见日期时间存储格式说明。 | `1645977600000` | | **枚举** | `string` | 所填值必须为设置的枚举值中的某一个 | `"牛奶"` | | **地理位置** | `object` | 固定格式的对象,包含地址和坐标 | `{"geopoint": {"type": "Point", "coordinates": [40.56, 5.89]}, "address": "深圳市南山区"}` | | **文件** | `string` | 默认从前端组件获得文件的 cloudId | `"cloud://xxx.xxx.xxx.pdf"` | | **自动编号** | `string` | 用户不填则后端自动补齐;用户传参则使用客户定义的值 | `"1001"` | | **地区** | `string` | 省级行政区划 | `"陕西省"` | | **关联关系** | `string` | 支持一对一、一对多、多对一 | 存储关联记录的 ID | ### 日期时间存储格式说明| DB类型 | 日期时间格式 | 日期时间存储类型 | 日期时间存储类型说明 | 日期时间DB存储示例 |
|---|---|---|---|---|
| 文档型数据库 | 日期时间 | datetime | 使用date类型存储 | Tue Sep 23 2025 00:00:00 GMT+0800 (中国标准时间) |
| number | 使用number类型存储 | 1758560400000 | ||
| 日期 | number | 只支持number格式 | 1758556800000 | |
| 时间 | number | 只支持number格式 | 3600000 | |
| Mysql数据库 | 日期时间 | datetime | 使用datetime类型存储 | 2025-09-23 00:00:00 |
| timestamp | 使用timestamp类型存储 | 2025-09-23 02:00:00 | ||
| number | 使用bigint类型存储 | 1758560400000 | ||
| 日期 | date | 使用date类型存储 | 2025-09-23 | |
| number | 使用number类型存储 | 1758556800000 | ||
| 时间 | time | 使用time类型存储 | 01:00:00 | |
| number | 使用number类型存储 | 3600000 |
## 关联字段限制说明
### 字段长度限制
> ⚠️ 注意:关联字段存储的是 `_id` 值,存在以下长度限制:
| 限制项 | 最大长度 | 说明 |
|--------|---------|------|
| 单个 `_id` | 256 字节 | 单个关联记录的 ID 长度限制 |
| 一对多/多对多数组 | 1024 字节 | 存储多个 `_id` 的数组总长度限制 |
| 图片 CloudID | 100-200+ 字节 | 云存储图片 ID 可能较长,需特别注意 |
### 多对多关联数量限制
对于多对多关系,由于总长度限制为 1024 字节,关联记录数量受到限制:
| 单个 `_id` 长度 | 最大关联数量 | 说明 |
|----------------|-------------|------|
| 20 字节 | 约 50 个 | MongoDB ObjectId 标准长度 |
| 36 字节 | 约 28 个 | UUID 格式 |
| 10 字节 | 约 100 个 | 自定义短 ID |
**建议**:
- 对于需要大量关联的场景(如用户收藏的商品数量可能超过 100 个),建议使用独立的关联表
- 使用自定义短 ID 可以增加关联数量
- 评估业务场景的实际需求,选择合适的方案
## 多对多中间表操作
对于 MySQL 数据模型的多对多关系,系统会自动创建中间表。虽然不支持直接 SQL JOIN,但可以通过以下方式操作中间表。
### 查找中间表名称
**示例**:学生(student)和课程(course)的多对多关系
- 中间表结构:
```
course_student
├── course_id (外键)
├── student_id (外键)
└── created_at (创建时间)
```
### 查询中间表数据
虽然不支持直接 JOIN,但可以通过数据模型的关联查询获取关联数据:
```javascript
// 查询学生及其选修的所有课程
const { data } = await models.student.get({
filter: {
where: {
_id: { $eq: "student_123" }
}
},
select: {
_id: true,
name: true,
courses: {
_id: true,
courseName: true,
credits: true
}
}
});
// 结果包含完整的关联数据
console.log(data.courses);
// [
// { _id: "course_1", courseName: "数学", credits: 4 },
// { _id: "course_2", courseName: "英语", credits: 3 }
// ]
```
### 反向查询
```javascript
// 查询课程及选修该课程的所有学生
const { data } = await models.course.get({
filter: {
where: {
_id: { $eq: "course_1" }
}
},
select: {
_id: true,
courseName: true,
students: {
_id: true,
name: true,
age: true
}
}
});
console.log(data.students);
// [
// { _id: "student_1", name: "张三", age: 20 },
// { _id: "student_2", name: "李四", age: 21 }
// ]
```
### 使用关联条件查询
```javascript
// 查询选修了"数学"课程的所有学生
const { data } = await models.student.list({
filter: {
relateWhere: {
courses: {
where: {
courseName: { $eq: "数学" }
}
}
}
},
select: {
_id: true,
name: true,
courses: {
courseName: true
}
}
});
```
## 故障排查
### 关联字段更新失败
#### 错误1:格式错误
**错误信息**:`Invalid relation field format` 或 `关联字段格式不正确`
**原因**:未使用 `{_id: "xxx"}` 格式
**错误示例**:
```javascript
// ❌ 错误:直接传字符串
await models.student.update({
filter: { where: { _id: { $eq: "student_123" } } },
data: { class: "class_456" } // 错误格式
});
// ❌ 错误:传入完整对象
await models.student.update({
filter: { where: { _id: { $eq: "student_123" } } },
data: {
class: {
_id: "class_456",
name: "一年级1班" // 不需要传入其他字段
}
}
});
```
**正确示例**:
```javascript
// ✅ 正确:使用 {_id: "xxx"} 格式
await models.student.update({
filter: { where: { _id: { $eq: "student_123" } } },
data: {
class: {
_id: "class_456"
}
}
});
```
#### 错误2:数组格式错误
**错误信息**:`Expected array of objects with _id field`
**原因**:一对多或多对多关系中,数组格式不正确
**错误示例**:
```javascript
// ❌ 错误:直接传字符串数组
await models.student.update({
filter: { where: { _id: { $eq: "student_123" } } },
data: {
courses: ["course_1", "course_2"] // 错误格式
}
});
```
**正确示例**:
```javascript
// ✅ 正确:数组中每项使用 {_id: "xxx"} 格式
await models.student.update({
filter: { where: { _id: { $eq: "student_123" } } },
data: {
courses: [
{ _id: "course_1" },
{ _id: "course_2" }
]
}
});
```
#### 错误3:关联数据不存在
**错误信息**:`Related record not found` 或 `关联记录不存在`
**原因**:关联的 `_id` 在目标模型中不存在
**排查步骤**:
1. 检查关联的 `_id` 是否正确
2. 在目标模型中查询该记录是否存在
3. 确认 `_id` 的拼写和格式是否正确
**解决方法**:
```javascript
// 先验证关联记录是否存在
const classExists = await models.class.get({
filter: { where: { _id: { $eq: "class_456" } } }
});
if (classExists.data) {
// 记录存在,可以安全关联
await models.student.update({
filter: { where: { _id: { $eq: "student_123" } } },
data: { class: { _id: "class_456" } }
});
} else {
console.error("关联的班级不存在");
}
```
#### 错误4:字段长度超出限制
**错误信息**:`Relation field length exceeds limit` 或 `字段长度超出限制`
**原因**:
- 单个 `_id` 超过 256 字节
- 一对多/多对多数组总长度超过 1024 字节
**解决方法**:参考上文「关联字段限制说明」部分的解决方案
### 关联查询结果为空
#### 原因1:权限配置问题
关联模型的权限配置独立于主模型,需要分别设置。
**排查步骤**:
1. 检查主模型的权限配置
2. 检查关联模型的权限配置
3. 确认当前用户是否有权限访问关联数据
**示例**:
```javascript
// 查询文章及评论,但评论权限不足时,评论数据为空
const { data } = await models.post.get({
filter: { where: { _id: { $eq: "post_123" } } },
select: {
title: true,
comments: { // 如果评论模型权限不足,此处返回空数组
content: true
}
}
});
```
**解决方法**:
- 在控制台检查关联模型的权限设置
- 确保当前用户有读取关联模型数据的权限
- 或在云函数中使用管理员权限进行查询
#### 原因2:关联数据确实不存在
**排查步骤**:
1. 在控制台直接查看主记录的关联字段值
2. 检查关联字段是否为空或包含无效的 `_id`
3. 在关联模型中查询对应的记录是否存在
**解决方法**:
```javascript
// 先查询主记录,检查关联字段
const post = await models.post.get({
filter: { where: { _id: { $eq: "post_123" } } },
select: { comments: true }
});
console.log('关联的评论ID:', post.data.comments);
// 检查是否为空数组或包含无效ID
// 验证关联记录是否存在
const commentIds = post.data.comments.map(c => c._id);
const comments = await models.comment.list({
filter: {
where: {
_id: { $in: commentIds }
}
}
});
console.log('实际存在的评论:', comments.data);
```
#### 原因3:select 字段配置错误
**错误示例**:
```javascript
// ❌ 错误:未在 select 中包含关联字段
const { data } = await models.post.get({
filter: { where: { _id: { $eq: "post_123" } } },
select: {
title: true,
content: true
// 缺少 comments 字段,不会返回关联数据
}
});
```
**正确示例**:
```javascript
// ✅ 正确:在 select 中明确指定关联字段
const { data } = await models.post.get({
filter: { where: { _id: { $eq: "post_123" } } },
select: {
title: true,
content: true,
comments: { // 必须明确指定关联字段
_id: true,
content: true
}
}
});
```
#### 原因4:多层关联查询不支持
关联查询目前只支持一层关联,多层嵌套关联不会返回数据。
**错误示例**:
```javascript
// ❌ 错误:多层关联查询不返回数据
const { data } = await models.post.get({
filter: { where: { _id: { $eq: "post_123" } } },
select: {
title: true,
author: { // 第一层关联 ✅
name: true,
profile: { // ❌ 第二层关联:不支持,不会返回数据
address: { // ❌ 第三层关联:不支持
city: true
}
}
}
}
});
// 查询结果:
// {
// title: "文章标题",
// author: {
// name: "张三"
// // profile 字段不会返回
// }
// }
```
**正确示例**:
```javascript
// ✅ 正确:只查询一层关联
const { data } = await models.post.get({
filter: { where: { _id: { $eq: "post_123" } } },
select: {
title: true,
author: { // 一层关联 ✅
_id: true,
name: true,
email: true,
bio: true // 普通字段可以正常查询
}
}
});
```
**解决方法**:参考上文「关联查询层级限制说明」中的分步查询或数据冗余方案
### 多对多关系操作异常
#### 问题:无法找到中间表
**原因**:对于 MySQL 数据模型,中间表由系统自动创建和管理
**解决方法**:
- 不需要手动创建中间表
- 使用数据模型提供的关联查询方法
- 不要尝试直接通过 SQL JOIN 查询中间表
#### 问题:中间表数据不一致
**原因**:直接修改了中间表数据,导致关联关系不一致
**解决方法**:
- **始终通过数据模型的更新方法修改关联关系**
- 不要直接操作中间表数据
- 如果数据不一致,通过数据模型的更新方法重新设置关联关系
```javascript
// ✅ 正确:通过数据模型更新关联关系
await models.student.update({
filter: { where: { _id: { $eq: "student_123" } } },
data: {
courses: [
{ _id: "course_1" },
{ _id: "course_2" }
]
}
});
// ❌ 错误:不要直接操作中间表
// await db.query("INSERT INTO course_student ..."); // 不推荐
```
---
# 数据模型/高级功能/数据权限管理
> 当前文档链接: https://docs.cloudbase.net/model/data-permission
CloudBase 提供了多层次的数据权限管理机制,确保数据安全的同时满足不同业务场景的权限控制需求。
数据模型进行读写时会以 `_openid` 字段作为数据归属判定依据
## 权限管理体系
CloudBase 数据权限管理包含两个层次:
| 权限类型 | 控制粒度 | 适用场景 | 配置复杂度 |
| ---------------- | -------- | -------------- | ---------- |
| **基础权限控制** | 模型级别 | 简单的权限需求 | 低 |
| **角色权限** | 用户级别 | 组织架构权限 | 中 |
> 💡 注意: 数据模型不支持安全规则权限配置
### 权限优先级
不同权限类型之间的关系:
- **角色权限** 和 **基础权限** 取 **并集** 为最终权限
- 建议根据业务复杂度选择合适的权限管理方式
## 基础权限控制
### 功能特点
基础权限控制是最简单的权限管理方式,适合大多数常见的业务场景:
- **模型级别控制**:针对整个数据模型设置统一权限
- **预设权限模板**:提供常用的权限配置模板
- **简单易用**:无需编写复杂的规则表达式
### 配置方式
在 [云开发平台](https://tcb.cloud.tencent.com/dev) 的数据模型页面,为每个模型设置对应的权限:

### 权限选项
从用户的身份出发,去选择对应的权限
- 所有用户包含匿名用户、外部用户、内部用户。
- 一个匿名用户的实际权限是所有用户和匿名用户的权限最大集;外部用户和内部用户也是同理。
- **最佳实践一**:只通过所有用户来管理权限;设置匿名用户、外部用户、内部用户的权限为无权限。
- **最佳实践二**:删除"所有用户"的规则,通过细分角色来管理权限。
| 权限类型 | 适用场景 |
| ------------------------------ | ---------------------- |
| **读取全部数据,修改本人数据** | 公开内容,如文章、商品 |
| **读取和修改本人数据** | 私人数据,如用户资料 |
| **读取全部数据,不可修改数据** | 配置数据,如系统设置 |
| **无权限** | 敏感数据,如财务信息 |
## 自定义角色权限控制
### 功能概述
角色权限是基于组织架构的权限管理方式,适合企业级应用中的层级权限控制。它与基础权限控制相互补充,最终权限为两者的**并集**。
**核心特点**:
- **组织架构支持**:基于部门、上下级关系的权限控制
- **角色继承**:支持权限的层级继承
- **灵活组合**:与基础权限取并集,提供更灵活的权限配置
### 配置步骤
#### 第一步:进入角色管理
访问 **[云开发平台/自定义角色页面](https://tcb.cloud.tencent.com/cloud-admin?#/settings/roleManage)**,管理组织架构和角色定义:

#### 第二步:配置行权限
选择目标角色,点击「**行权限设置**」进行详细配置:

### 权限级别说明
| 权限级别 | 数据范围 | 适用场景 | 示例 |
| ---------------------- | ------------------------ | ------------ | ------------------------------ |
| **查看本人** | 所有人字段值为自己的数据 | 个人数据管理 | 员工只能查看自己的考勤记录 |
| **查看本人及下属** | 自己和下属的数据 | 团队管理 | 主管可以查看团队成员的工作报告 |
| **查看本部门及子部门** | 本部门及子部门的数据 | 部门管理 | 部门经理查看部门内所有项目 |
| **查看全部** | 所有数据 | 系统管理 | 管理员查看全公司数据 |
### 权限组合规则
#### 读写权限关系
> ⚠️ 注意:行修改权限自动包含读权限,即有修改权限就有读权限。
#### 权限并集计算
**基础权限** + **角色权限** = **最终权限**
**示例场景**:
```
基础权限:仅创建者及管理员可读写
角色权限:查看全部 + 无修改权限
─────────────────────────────────
最终权限:可以查看所有数据,但只能修改自己创建的数据
```
### 实际应用案例
#### 案例 1:博客权限
**业务需求**:
- 可以查看所有人的博客
- 自己更新自己的博客
**基础权限配置**
所有用户/读取全部数据,修改本人数据
#### 案例 2:项目管理系统
**业务需求**:
- 项目成员只能查看参与的项目
- 项目经理可以管理负责的项目
- 部门经理可以查看部门所有项目
**基础权限配置**
所有用户/读取和修改本人数据
**角色权限配置**
| 角色 | 角色数据权限 | 行修改权限 | 最终效果 |
| ------------ | -------------- | ---------- | ------------------------ |
| **销售员** | 查看本人 | 本人 | 只能查看和修改自己的客户 |
| **销售主管** | 查看本人及下属 | 本人及下属 | 可以管理团队所有客户 |
| **销售总监** | 查看全部 | 查看全部 | 可以管理所有客户 |
### 权限设计最佳实践
#### 1. 权限设计原则
- **最小权限原则**:只授予完成工作所需的最小权限
- **职责分离**:不同角色承担不同的数据责任
- **权限继承**:合理利用组织架构的层级关系
#### 2. 常见配置模式
**只读扩展模式**:
```
基础权限:仅创建者可读写
角色权限:扩大查看范围,不给修改权限
效果:扩大数据可见性,保持修改控制
```
**管理员模式**:
```
基础权限:仅创建者可读写
角色权限:查看全部 + 修改全部
效果:管理员可以管理所有数据
```
**部门隔离模式**:
```
基础权限:仅创建者可读写
角色权限:查看本部门 + 修改本部门
效果:部门间数据隔离,部门内共享
```
#### 3. 注意事项
- **权限测试**:在生产环境前充分测试各种角色的权限
- **权限审计**:定期检查和调整权限配置
- **文档记录**:详细记录权限设计的业务逻辑
- **变更管理**:权限变更要有审批流程
---
# 数据模型/高级功能/索引管理
> 当前文档链接: https://docs.cloudbase.net/model/data-index
索引是提升数据库查询性能的关键技术。通过为常用查询字段建立索引,可以显著提升查询速度和用户体验。
## 管理入口
1. **访问控制台**:进入 [云开发平台](https://tcb.cloud.tencent.com/dev)
2. **选择数据库**:切换到 [云开发平台/数据库/文档型](https://tcb.cloud.tencent.com/dev?#/db/doc/model/?sourceType=internal_flexdb)
3. **进入索引管理**:选择目标集合,点击「索引管理」标签页
4. **管理索引**:新建、删除或修改索引配置

## 索引类型详解
### 单字段索引
**适用场景**:针对单个字段的查询和排序操作
**特点说明**:
* 支持嵌套字段索引,使用"点表示法"访问
* 可指定升序或降序排序
* 适合简单的单条件查询
**示例配置**:
```json
// 原始数据结构
{
"_id": "product_001",
"name": "iPhone 15",
"price": 5999,
"style": {
"color": "深空黑",
"storage": "128GB"
}
}
```
**索引配置示例**:
| 字段路径 | 索引类型 | 排序方式 | 适用查询 |
|---------|----------|----------|----------|
| `name` | 单字段 | 升序 | 按商品名称查询 |
| `price` | 单字段 | 降序 | 按价格排序 |
| `style.color` | 单字段 | 升序 | 按颜色筛选 |
> 💡 注意:嵌套字段使用点表示法,如 `style.color` 表示访问 style 对象中的 color 字段。
### 组合索引
**适用场景**:多字段联合查询和复杂排序操作
**核心概念**:一个索引包含多个字段,支持前缀匹配查询
**示例数据**:
```json
{
"_id": "student_001",
"name": "张三",
"age": 20,
"score": 85,
"class": "计算机科学"
}
```
**索引前缀规则**:
假设创建组合索引: `name + age + score`
| 索引前缀 | 能命中的查询组合 | 查询示例 |
|---------|-----------------|----------|
| `name` | 单独查询 name | `db.collection('students').where({name: '张三'})` |
| `name, age` | 查询 name + age | `db.collection('students').where({name: '张三', age: 20})` |
| `name, age, score` | 查询全部字段 | `db.collection('students').where({name: '张三', age: 20, score: 85})` |
:::tip 💡 前缀匹配原理
组合索引 `(name, age, score)` 的前缀包含:
* ✅ `name` - 可以命中索引
* ✅ `name, age` - 可以命中索引
* ✅ `name, age, score` - 可以命中索引
* ❌ `age` - 无法命中索引(不是前缀)
* ❌ `score` - 无法命中索引(不是前缀)
* ❌ `age, score` - 无法命中索引(不是前缀)
:::
**组合索引的重要特性**:
### 🔄 1. 字段顺序的重要性
索引字段的顺序直接影响查询性能:
| 索引定义 | 能命中的查询 | 无法命中的查询 |
|---------|-------------|---------------|
| `(name, age)` | ✅ `name` 查询
> 💡 **提示**:「中间模型标识」是系统自动生成的唯一标识符,用于在数据库中定位对应的中间表。
#### 步骤 2:在数据库中搜索中间表
通过「中间模型标识」在数据库表列表中搜索,即可找到对应的中间表:
> ⚠️ **环境区分**:
> - 表名**以 `-preview` 结尾**:体验环境的中间表
> - 表名**不以 `-preview` 结尾**:正式环境的中间表
### 中间表字段说明
中间表通常包含以下核心字段:
| 字段名 | 说明 | 示例 |
|--------|------|------|
| `leftRecordId` | 主表(左表)记录的主键 ID | 学生表的 `_id`,如 `STUDENT_001` |
| `rightRecordId` | 从表(右表)记录的主键 ID | 课程表的 `_id`,如 `COURSE_101` |
#### 字段说明图示
#### 实际示例
以「学生选课」场景为例,中间表的数据结构如下:
| leftRecordId | rightRecordId | 含义 |
|--------------|---------------|------|
| `STUDENT_001` | `COURSE_101` | 学生 001 选修了课程 101 |
| `STUDENT_001` | `COURSE_102` | 学生 001 选修了课程 102 |
| `STUDENT_002` | `COURSE_101` | 学生 002 选修了课程 101 |
这样的设计使得:
- 通过 `STUDENT_001` 可以查询到该学生选修的所有课程(`COURSE_101`、`COURSE_102`)
- 通过 `COURSE_101` 可以查询到选修该课程的所有学生(`STUDENT_001`、`STUDENT_002`)
---
# 数据模型/MySQL 数据模型/SQL 操作
> 当前文档链接: https://docs.cloudbase.net/model/db-raw-query
当 **MySQL数据模型** SDK 无法满足复杂查询需求时,可以直接使用 SQL 语句进行数据库操作。
:::tip 提示
**SQL 操作** 仅支持服务端调用
- [@cloudbase/node-sdk](/api-reference/server/node-sdk/initialization)
- [HTTP API](/http-api/model/mysql-command)
:::
**SQL 语法支持范围**
当前支持的 SQL 语句类型:
- `SELECT` - 数据查询
- `INSERT` - 数据插入
- `UPDATE` - 数据更新
- `DELETE` - 数据删除
> ⚠️ 注意:不支持事务(Transaction)、存储过程、触发器等高级数据库功能
以下介绍通过 **@cloudbase/node-sdk** 进行SQL操作:
## SDK 初始化
### 安装依赖
```bash
npm install @cloudbase/node-sdk --save
```
### 初始化代码
```js
import cloudbase from "@cloudbase/node-sdk"
// 云函数环境下使用 require 方式
// const cloudbase = require('@cloudbase/node-sdk')
// 初始化应用
const app = cloudbase.init({
env: "your-env-id" // 替换为您的环境 ID
})
// 获取数据模型实例
const models = app.models
```
> 💡 注意:详细的初始化配置请参考 [SDK 初始化](/api-reference/server/node-sdk/initialization) 文档
## 查询方法
MySQL 数据模型提供两种 SQL 查询方式:
| 方法 | 说明 | 安全性 | 推荐度 |
| ------------ | ---------------------- | ----------------- | ------ |
| `$runSQL` | 预编译模式,参数化查询 | 高(防 SQL 注入) | ⭐⭐⭐ |
| `$runSQLRaw` | 原始模式,直接执行 SQL | 低(需自行防护) | ⭐ |
> ⚠️ 注意:仅支持服务端调用(云函数、云托管等),建议优先使用 `$runSQL` 预编译模式
### 预编译模式 $runSQL
使用参数化查询,通过 Mustache 语法(`{{ }}`)绑定参数,有效防止 SQL 注入。
#### SELECT 查询示例
```js
// 条件查询
const result = await models.$runSQL(
"SELECT * FROM `table_name` WHERE title = {{title}} LIMIT 10",
{ title: "hello" }
);
// 数值比较
const result = await models.$runSQL(
"SELECT * FROM `table_name` WHERE read_num > {{num}}",
{ num: 1000 }
);
// 时间范围查询
const result = await models.$runSQL(
"SELECT * FROM `table_name` WHERE updatedAt > UNIX_TIMESTAMP({{timestamp}}) * 1000",
{ timestamp: "2024-06-01 00:00:00" }
);
// LIKE 模糊查询
const result = await models.$runSQL(
"SELECT * FROM `table_name` WHERE author_tel LIKE {{tel}}",
{ tel: "1858%" }
);
// 聚合统计
const result = await models.$runSQL(
"SELECT COUNT(*) as total FROM `table_name` WHERE is_published = {{isPublished}}",
{ isPublished: true }
);
// 指定字段查询
const result = await models.$runSQL(
"SELECT read_num, title FROM `table_name` ORDER BY read_num DESC"
);
```
#### INSERT/UPDATE/DELETE 示例
> ⚠️ 注意:INSERT操作时必须传入唯一标识`_id`字段,否则数据将没有唯一标识,可能导致后续查询和更新操作出现问题
```js
// 插入数据(必须包含_id唯一标识)
const insertResult = await models.$runSQL(
"INSERT INTO `table_name` (_id, title, content, author) VALUES ({{id}}, {{title}}, {{content}}, {{author}})",
{
id: "article_" + Date.now() + "_" + Math.random().toString(36).substr(2, 9), // 生成唯一ID
title: "新文章",
content: "文章内容",
author: "作者名"
}
);
// 批量插入数据
const batchInsertResult = await models.$runSQL(
"INSERT INTO `table_name` (_id, title, status) VALUES ({{id1}}, {{title1}}, {{status1}}), ({{id2}}, {{title2}}, {{status2}})",
{
id1: "article_" + Date.now() + "_001",
title1: "文章1",
status1: "published",
id2: "article_" + Date.now() + "_002",
title2: "文章2",
status2: "draft"
}
);
// 更新数据
const updateResult = await models.$runSQL(
"UPDATE `table_name` SET read_num = read_num + 1 WHERE _id = {{id}}",
{ id: "article_id_123" }
);
// 删除数据
const deleteResult = await models.$runSQL(
"DELETE FROM `table_name` WHERE status = {{status}} AND updatedAt < {{date}}",
{
status: "draft",
date: "2024-01-01 00:00:00"
}
);
```
> 💡 注意:`_id`字段建议使用以下方式生成唯一标识:
> - 时间戳 + 随机字符串:`"prefix_" + Date.now() + "_" + Math.random().toString(36).substr(2, 9)`
> - UUID库:使用`uuid`包生成标准UUID
> - 业务相关ID:结合业务逻辑生成有意义的唯一标识
#### 返回结果格式
```js
// SELECT 查询返回示例
{
"data": {
"total": 1,
"executeResultList": [{
"_id": "9JXU7BWFZJ",
"title": "hello",
"read_num": 997,
"createdAt": "2024-01-01T00:00:00.000Z",
"updatedAt": "2024-01-02T00:00:00.000Z"
}],
"backendExecute": "27"
},
"requestId": "16244844-19fe-4946-8924-d35408ced576"
}
// INSERT/UPDATE/DELETE 操作返回示例
{
"data": {
"total": 1, // 影响的行数
"executeResultList": [],
"backendExecute": "15"
},
"requestId": "16244844-19fe-4946-8924-d35408ced576"
}
```
### 原始模式 $runSQLRaw
用于动态表名等特殊场景,需要自行处理 SQL 注入防范。
#### 基础用法
```js
// 直接执行 SQL 语句
const result = await models.$runSQLRaw(
"SELECT * FROM `table_name` WHERE title = 'hello' LIMIT 10"
);
// 动态表名场景(需确保表名来源安全)
const tableName = getValidTableName(); // 必须验证表名安全性
const result = await models.$runSQLRaw(
`SELECT * FROM \`${tableName}\` WHERE status = 'active'`
);
// 复杂查询示例
const result = await models.$runSQLRaw(`
SELECT t1.title, t2.category_name, COUNT(t3.comment_id) as comment_count
FROM articles t1
LEFT JOIN categories t2 ON t1.category_id = t2._id
LEFT JOIN comments t3 ON t1._id = t3.article_id
WHERE t1.status = 'published'
GROUP BY t1._id, t2.category_name
ORDER BY comment_count DESC
LIMIT 20
`);
```
## 安全注意事项
### 预编译模式安全建议
- **优先选择**:除非必要,始终使用 `$runSQL` 预编译模式
- **参数绑定**:所有用户输入都通过参数绑定传递,避免直接拼接
- **类型验证**:确保参数类型与数据库字段类型匹配
### 原始模式安全要求
使用 `$runSQLRaw` 时必须严格遵守:
- **输入验证**:严格验证和过滤所有用户输入
- **白名单机制**:只允许预定义的安全值(如表名、字段名)
- **特殊字符转义**:正确处理单引号、反引号等 SQL 特殊字符
- **错误处理**:避免向客户端暴露详细的数据库错误信息
- **权限控制**:确保执行 SQL 的账户具有最小必要权限
```js
// ❌ 危险示例 - 直接拼接用户输入
const userInput = req.body.title;
const result = await models.$runSQLRaw(
`SELECT * FROM articles WHERE title = '${userInput}'`
);
// ✅ 安全示例 - 使用预编译模式
const result = await models.$runSQL(
"SELECT * FROM articles WHERE title = {{title}}",
{ title: req.body.title }
);
```
---
# 数据模型/MySQL 数据模型/SQL 模板
> 当前文档链接: https://docs.cloudbase.net/model/sql-template
**MySQL 数据模型** 提供配置 **SQL 模板** 功能,支持参数化查询和权限控制,可在小程序端、Web 端安全调用。
## 基础使用
### 创建模板
进入 [云开发平台/MySQL数据库/数据模型](https://tcb.cloud.tencent.com/dev?#/db/mysql/model/?sourceType=internal_mysql) ,选择对应数据模型,点击「SQL模版管理」标签页

点击「新建」按钮,填写模板名称和 SQL 语句保存即可
### 调用 SQL 模版
选择对应 SDK 进行调用:
| SDK 类型 | 适用平台 |
| ------------------------------------------------------------- | ------------ |
| [JS SDK](/api-reference/webv3/model/sql-template) | Web 浏览器 |
| [Node SDK](/api-reference/server/node-sdk/model/sql-template) | Node.js 环境 |
### 调用示例
以 Node SDK 为例,调用示例代码如下:
```js
import cloudbase from "@cloudbase/node-sdk";
// 初始化应用
const app = cloudbase.init({
env: "your-env-id", // 替换为您的环境 ID
});
// 调用 user 表的 SQL 模版
const res = await app.models.user.runSQLTemplate({
templateName: "template_name",
params: {
key: "value",
},
});
```
## SQL 语句规范
### 支持的命令
- `SELECT`
- `INSERT INTO`
- `REPLACE INTO`
- `UPDATE`
- `DELETE`
### 基本规则
1. 每个模板仅支持一条 SQL 命令
2. 主表必须是当前模型的表
3. 联表查询时子表可以是同库下任意表
4. `INSERT`/`REPLACE` 操作必须包含 `owner` 和 `_openid` 系统字段
### 参数语法
使用 `{{ param }}` 语法引入变量参数:
```sql
SELECT * FROM {{ model.tableName() }}
WHERE status = {{ status }}
```
## 内置函数
SQL 模板支持以下内置函数,可在模板中直接使用:
| 类别 | 函数 | 说明 |
| ---- | ---------------------------------- | ------------------ |
| 模型 | `model.tableName()` | 获取当前模型表名 |
| 模型 | `model.tableName('model_name')` | 获取指定模型表名 |
| 模型 | `model.dataId()` | 生成数据 ID |
| 用户 | `user.userId()` | 获取用户 ID |
| 用户 | `user.openId()` | 获取用户 openId |
| 权限 | `auth.rowPermission()` | 获取当前模型行权限 |
| 权限 | `auth.rowPermission('model_name')` | 获取指定模型行权限 |
| 系统 | `system.currentEpoch()` | 获取秒级时间戳 |
| 系统 | `system.currentEpochMillis()` | 获取毫秒级时间戳 |
## 使用示例
### 表名引用
```sql
-- 使用当前模型表
SELECT * FROM {{ model.tableName() }}
-- 联表查询
SELECT a.*, b.*
FROM {{ model.tableName() }} a
JOIN {{ model.tableName('other_model') }} b
ON a.id = b.ref_id
```
### 数据操作
```sql
-- 插入数据
INSERT INTO {{ model.tableName() }}
(_id, name, owner, _openid, createBy, updatedAt)
VALUES (
{{ model.dataId() }},
{{ name }},
{{ user.userId() }},
{{ user.openId() }},
{{ user.userId() }},
{{ system.currentEpochMillis() }}
)
-- 更新数据
UPDATE {{ model.tableName() }}
SET name = {{ name }},
updatedAt = {{ system.currentEpochMillis() }}
WHERE _id = {{ id }}
AND {{ auth.rowPermission() }}
-- 查询数据
SELECT * FROM {{ model.tableName() }}
WHERE status = {{ status }}
AND {{ auth.rowPermission() }}
```
### NULL 值处理
由于不支持 `IS NULL` 和 `IS NOT NULL`,使用以下替代语法:
```sql
-- 查询空值
WHERE column <=> NULL
-- 查询非空值
WHERE !(column <=> NULL)
```
---
# 数据模型/框架快速入门/未命名文档
> 当前文档链接: https://docs.cloudbase.net/database/framework-quickstarts/model/mp
---
sidebar_label: '微信小程序'
---
# 在微信小程序中访问云开发数据模型
学习如何创建云开发 MySQL 数据模型,添加示例数据,以及从微信小程序中查询数据。
## 1. 创建 MySQL 数据模型并添加数据
> 注意:必须使用你的微信小程序关联的腾讯云账号登录云开发平台
在云开发平台创建名为 `todos` 的数据模型,字段信息如下:

| 字段名称 | 字段标识 | 数据类型 | 是否必填 | 是否唯一 |
| -------- | ------------ | -------------- | -------- | -------- |
| 是否完成 | is_completed | 布尔值 | 否 | 否 |
| 描述 | description | 文本、多行文本 | 否 | 否 |
| 标题 | title | 文本、单行文本 | 否 | 否 |
创建完成后,在 `todos` 数据模型中录入示例数据。
## 2. 创建微信小程序

## 3. 安装云开发 SDK 依赖
1. 在小程序 `app.json` 所在的目录执行命令安装 npm 包。
```bash
npm install @cloudbase/wx-cloud-client-sdk
```
2. 点击微信开发者工具工具栏上的“工具” -> “构建 npm”。
## 4. 从小程序中查询数据
- 初始化 SDK
```javascript
// app.js
const { init } = require('@cloudbase/wx-cloud-client-sdk');
App({
onLaunch() {
let client = null;
this.globalData.getModels = async () => {
if (!client) {
wx.cloud.init({
env: '<云开发环境 ID>',
});
client = init(wx.cloud);
}
return client.models;
};
},
globalData: {},
});
```
- 在页面 `js` 文件中添加以下查询代码
```javascript
// pages/index/index.js
Page({
data: {
todos: [],
},
onLoad() {
this.fetchTodos();
},
async fetchTodos() {
try {
const models = await getApp().globalData.getModels();
const { data } = await models.todos.list({
filter: {
where: {},
},
pageSize: 10,
pageNumber: 1,
getCount: true,
// envType: pre 体验环境, prod 正式环境
envType: 'prod',
});
this.setData({
todos: data.records,
});
} catch (error) {
console.error('获取待办事项失败:', error);
}
},
});
```
- 在页面对应的 `wxml` 文件中展示数据
```xml
### 方式一:以 npx 直接运行 tcb 命令
> 此方式仅适用于本地未安装 `tcb` 的情况。如果本地已安装 `tcb`,请使用[【方式二】](#方式二安装-tcb-命令行工具)
进入项目目录中(目录位置参考注意事项),然后执行以下命令
```shell
npx --package=@cloudbase/cli tcb sync-model-dts --envId=
**常见自定义角色示例:**
| 角色标识 | 角色名称 | 适用场景 |
| ----------------- | -------- | -------------------------- |
| `content_editor` | 内容编辑 | 管理文章、商品等内容数据 |
| `data_analyst` | 数据分析 | 查看统计数据和分析报表 |
| `finance_admin` | 财务管理 | 管理订单、交易等财务数据 |
| `customer_support`| 客服人员 | 处理用户反馈和工单 |
#### 多角色权限合并规则
当用户拥有多个角色时,最终权限按以下规则合并:
- **允许权限取并集**:用户拥有所有角色的允许权限之和
- **拒绝权限优先**:任一角色的拒绝规则会覆盖其他角色的允许规则
**示例:**
```
用户同时拥有:内容编辑 + 数据分析
角色权限:
- 内容编辑:允许访问文章表(读、写、改)
- 数据分析:允许访问订单表(仅读)
最终权限:
- 可以读写文章表的所有数据
- 可以查看订单表的数据(仅读)
```
## 成员管理
将用户添加到角色,实现权限分配。
### 添加成员的两种方式
**方式一:在角色中添加成员**
1. 访问 [云开发平台/身份认证/权限控制](https://tcb.cloud.tencent.com/dev#/identity/auth-control) 页面
2. 找到目标角色,点击「配置成员」
3. 点击「添加成员」批量选择用户
4. 确认后立即生效
**方式二:在组织架构中管理**
1. 访问 [云开发平台/身份认证/组织架构](https://tcb.cloud.tencent.com/dev#/identity/user-organization) 页面
2. 创建部门和岗位结构
3. 将用户添加到对应的组织节点
4. 为用户批量关联角色
> 💡 **提示**:组织架构方式适合企业应用,可以基于部门和岗位批量管理用户权限。
### 修改用户权限
修改用户权限有两种途径:
**修改角色权限**
- 在角色的权限配置中调整资源和数据权限
- 该角色下所有用户自动继承新权限
- 适合批量调整多个用户的权限
**调整用户角色**
- 将用户从某个角色移除
- 或添加用户到新角色
- 适合单个用户的权限调整
> ⚠️ **注意**:权限变更可能需要一定时间生效,建议用户退出重新登录,立即应用新权限。
## 权限配置
为角色配置具体的访问权限,包括资源权限、数据权限两个维度。
### 资源权限
资源权限决定角色可以访问哪些 CloudBase 资源。
**配置步骤:**
1. 在角色卡片中点击「配置权限」
2. 选择「添加自定义策略」
3. 选择要授权的资源类型和具体资源
**支持的资源类型:**
| 资源类型 | 说明 | 典型应用场景 |
| -------------- | -------------------- | -------------------- |
| 文档型数据库 | NoSQL 数据库权限配置,[文档型数据库权限说明](/database/data-permission) | 业务数据储存 |
| MySQL 数据库 | 关系型数据库权限配置,[MySQL 数据库权限说明](/database/configuration/db/tdsql/data-permission) | 复杂业务数据 |
| 云函数 | 处理后端业务逻辑,[云函数权限说明](/cloud-function/security-rules) | API 调用、数据处理 |
| 云存储 | 文件存储,[云存储权限管理](/storage/data-permission) | 图片、视频、文档上传 |
| 微搭应用 / 数据模型 / APIs / 工作台 | 可在权限控制页面直接进行管理, 前往[云开发平台/身份认证/权限控制](https://tcb.cloud.tencent.com/dev#/identity/auth-control),进入权限策略配置窗口,如下图 | 后台应用、内容管理、工作台(即[云后台](https://tcb.cloud.tencent.com/dev?envId=#/cloud-admin))等 |
**权限策略配置窗口:**
### 数据权限
数据权限控制角色可以访问哪些数据,支持行级和列级两种维度。
#### 行级权限(数据行)
控制用户可以访问哪些数据行,实现数据隔离。
**常见配置方式:**
| 权限类型 | 说明 | 适用场景 |
| -------------- | -------------------------- | ---------------------- |
| 所有数据 | 访问全部数据行 | 管理员、数据分析人员 |
| 仅自己的数据 | 只能访问自己创建的数据 | 普通用户、个人数据管理 |
| 特定条件数据 | 满足特定条件的数据(如部门)| 部门经理、区域负责人 |
**配置示例:**
**实际应用:**
- 销售人员只能查看自己负责的客户数据
- 部门经理可以查看本部门的所有数据
- 区域经理可以查看所负责区域的数据
#### 列级权限(数据列)
控制用户可以访问哪些字段(列),隐藏敏感信息。
**配置选项:**
- **可读字段**:用户可以查看的字段列表
- **隐藏字段**:对用户不可见的敏感字段
**配置示例:**
**实际应用:**
- 隐藏销售人员的成本和利润字段
- 隐藏客服人员的用户手机号和身份证号
- 隐藏普通员工的薪资和绩效数据
> 💡 **提示**:行级权限和列级权限可以同时使用,实现更精细的数据访问控制。
**配置说明:**
不同资源类型支持的操作权限可能有所差异,具体配置方式请参考对应的资源文档:
## 典型应用场景
### 场景一:个人博客网站
#### 需求分析
个人博客需要支持游客浏览、用户评论和博主管理三种角色。
#### 角色规划
| 角色 | 权限范围 | 使用对象 |
| -------- | ---------------------------- | ---------- |
| 匿名用户 | 浏览文章、查看评论 | 所有访客 |
| 注册用户 | 浏览文章、发表评论、点赞 | 注册读者 |
| 管理员 | 发布文章、管理评论、网站设置 | 博客所有者 |
#### 数据权限配置
**文章表(`blog_articles`):**
| 角色 | 查看权限 | 创建权限 | 修改权限 | 删除权限 |
| -------- | ---------------- | -------- | ------------ | ------------ |
| 匿名用户 | 已发布文章 | ❌ | ❌ | ❌ |
| 注册用户 | 已发布文章+草稿 | ✅ | 仅自己的草稿 | 仅自己的草稿 |
| 管理员 | 所有文章 | ✅ | ✅ | ✅ |
**评论表(`comments`):**
| 角色 | 查看权限 | 创建权限 | 修改权限 | 删除权限 |
| -------- | -------- | -------- | -------------- | -------------- |
| 匿名用户 | 所有评论 | ❌ | ❌ | ❌ |
| 注册用户 | 所有评论 | ✅ | 仅自己的评论 | 仅自己的评论 |
| 管理员 | 所有评论 | ✅ | ✅ | ✅ |
#### 实施步骤
**1. 启用匿名登录**
在 [登录方式管理](https://tcb.cloud.tencent.com/dev#/identity/login-manage) 开启「允许匿名登入」。
**2. 配置数据库权限**
在数据库集合的权限设置中配置对应角色的访问权限,参考 [文档型数据库权限](/database/data-permission)。
**3. 前端调用匿名登录**
```javascript
import cloudbase from '@cloudbase/js-sdk';
const app = cloudbase.init({
env: 'your-env-id'
});
const auth = app.auth();
// 页面加载时执行匿名登录
auth.signInAnonymously().then(() => {
console.log('匿名登录成功');
});
```
### 场景二:企业内部管理系统
#### 需求分析
企业管理系统需要区分不同部门和岗位的访问权限,实现数据隔离和精细化管理。
#### 角色规划
**创建自定义角色:**
1. 访问 [权限控制](https://tcb.cloud.tencent.com/dev#/identity/auth-control) 页面
2. 点击「创建角色」创建以下角色:
| 角色标识 | 角色名称 | 权限范围 |
| ----------------- | -------- | -------------------------- |
| `content_editor` | 内容编辑 | 管理文章、产品等内容数据 |
| `data_analyst` | 数据分析 | 查看统计数据和分析报表 |
| `finance_admin` | 财务管理 | 管理订单、交易等财务数据 |
| `dept_manager` | 部门经理 | 管理本部门的数据和成员 |
#### 数据权限配置
**行级权限示例**
配置"用户只能访问自己部门的数据":
**列级权限示例**
隐藏敏感字段(如成本、利润):
#### 实施步骤
**1. 创建组织架构**
在 [组织架构](https://tcb.cloud.tencent.com/dev#/identity/user-organization) 中创建部门结构。
**2. 配置角色权限**
为每个角色配置资源权限和数据权限(行级+列级)。
**3. 分配用户角色**
将用户添加到对应部门,并关联相应角色。
### 场景三:协同编辑平台
#### 需求分析
多人协同文档编辑平台,允许用户编辑他人创建的文档,需要严格的权限控制。
#### 角色规划
| 角色 | 权限范围 | 使用对象 |
| -------- | -------------------------------------- | ------------ |
| 普通用户 | 查看所有文档、编辑自己的文档 | 注册用户 |
| 协作者 | 查看所有文档、编辑被授权的文档 | 被邀请的用户 |
| 管理员 | 所有文档的完整权限(查看、编辑、删除) | 团队管理者 |
#### 实现方案
> ⚠️ **安全警告**:允许修改他人数据是高风险操作,必须实施严格的权限验证。
**方案一:通过云函数修改数据(推荐)**
云函数在服务端执行,可以绕过客户端权限限制,但需要在函数内部实现严格的权限验证:
```javascript
// 云函数:editDocument
const cloudbase = require('@cloudbase/node-sdk');
const app = cloudbase.init();
const db = app.database();
exports.main = async (event, context) => {
const { documentId, newContent } = event;
const { userInfo } = context;
// 1. 验证用户登录状态
if (!userInfo || !userInfo.uid) {
return { code: 401, message: '未登录' };
}
// 2. 查询文档信息
const doc = await db.collection('documents')
.doc(documentId)
.get();
if (!doc.data || doc.data.length === 0) {
return { code: 404, message: '文档不存在' };
}
const document = doc.data[0];
// 3. 权限验证:检查是否是作者或协作者
const isOwner = document._openid === userInfo.openid;
const isCollaborator = document.collaborators &&
document.collaborators.includes(userInfo.uid);
if (!isOwner && !isCollaborator) {
return { code: 403, message: '无权编辑此文档' };
}
// 4. 执行更新
const result = await db.collection('documents')
.doc(documentId)
.update({
content: newContent,
lastEditBy: userInfo.uid,
lastEditAt: new Date()
});
return { code: 0, message: '更新成功', data: result };
};
```
**方案二:通过安全规则配置(精细控制)**
对于需要更复杂、更精细的权限控制场景,可以使用安全规则功能。
**CloudBase 支持安全规则的资源:**
| 资源类型 | 安全规则文档 | 适用场景 |
| ------------ | ------------------------------------------------ | ---------------------------- |
| 文档型数据库 | [数据库安全规则](/database/security-rules) | 文档级、字段级的权限控制 |
| 云存储 | [云存储安全规则](/storage/security-rules) | 文件上传、下载、删除权限控制 |
| 云函数 | [云函数安全规则](/cloud-function/security-rules) | 函数调用权限控制 |
**使用安全规则实现协同编辑:**
```json
{
// 读权限:所有登录用户都可以查看文档
"read": "auth != null",
// 写权限:作者、协作者或管理员可以编辑
"write": "doc._openid == auth.openid || doc.collaborators.includes(auth.uid) || auth.role == 'admin'"
}
```
> 💡 **提示**:安全规则在数据库层面生效,无需编写云函数即可实现复杂的权限控制逻辑。
## 安全最佳实践
### 管理员权限的谨慎使用
> ⚠️ **安全警告**:管理员角色拥有系统**最高权限**(删除数据、修改配置、管理成员等),错误操作可能导致严重后果。
**建议做法:**
1. **最小化管理员数量**:仅为必要人员授予管理员权限
2. **创建日常角色**:为日常操作创建权限受限的自定义角色
3. **分离权限**:根据职责划分不同权限级别的角色
4. **定期审查**:定期检查管理员账号列表,移除不再需要的权限
5. **启用双因素认证**:启用[多因素认证验证](https://tcb.cloud.tencent.com/dev?envId=#/identity/mfa-management),保障账号安全
### 权限最小化原则
遵循"最小权限原则",仅授予用户完成工作所需的最小权限。
**正确做法:**
```
✅ 销售人员只能查看和编辑自己负责的客户数据
✅ 客服人员只能查看工单信息,但无法查看用户的支付信息
✅ 数据分析人员只能查看数据(只读权限),无法修改或删除
```
**应避免的做法:**
```
❌ 销售人员可以查看所有客户数据
❌ 客服人员拥有用户信息的完整访问权限
❌ 给普通员工授予管理员权限以"方便操作"
```
### 敏感数据保护
对于敏感数据,建议采用多层防护:
1. **列级权限**:隐藏敏感字段(如身份证、银行卡号)
2. **数据脱敏**:在展示时对敏感信息进行脱敏处理
3. **审计日志**:记录所有对敏感数据的访问和修改操作
4. **定期审查**:定期审查敏感数据的访问权限配置
### 权限变更管理
**权限变更前:**
1. 评估影响范围,确认变更不会影响业务正常运行
2. 在测试环境验证权限配置的正确性
3. 通知相关用户即将进行的权限调整
**权限变更后:**
1. 验证权限配置是否生效
2. 建议用户退出重新登录以立即应用新权限
3. 监控是否有权限相关的错误或异常
## 常见问题
### Q1: 用户已添加权限,但仍然无法访问资源?
**排查步骤:**
1. **检查角色分配**
- 确认用户已被正确分配到目标角色
- 在 [用户管理](https://tcb.cloud.tencent.com/dev#/identity/user-management) 中查看用户的角色列表
2. **检查策略配置**
- 确认策略已保存并关联到正确的角色
- 在 [权限控制](https://tcb.cloud.tencent.com/dev#/identity/auth-control) 中查看角色的策略配置
3. **检查权限生效时间**
- 权限变更可能需要一定时间生效
- 建议用户退出重新登录以立即应用新权限
4. **检查网关策略**
- 可能被网关层的限制拦截
- 检查用户关联的网关权限策略
5. **查看具体错误信息**
- 查看客户端或服务端的错误日志
- 根据错误码针对性解决问题
### Q2: 多个角色的权限如何合并?
当用户拥有多个角色时,权限合并遵循以下规则:
**权限合并规则:**
- **允许权限取并集**:用户拥有所有角色的允许权限之和
- **拒绝权限优先**:任一角色的拒绝规则会覆盖其他角色的允许规则
**示例:**
```
用户同时拥有:内容编辑 + 数据分析
角色权限:
- 内容编辑:允许访问文章表(读、写、改)
- 数据分析:允许访问订单表(仅读)、拒绝删除文章表
最终权限:
- 可以读写文章表,但不能删除文章(拒绝规则优先)
- 可以查看订单表的数据(仅读)
```
### Q3: 如何实现"用户只能查看自己部门的数据"?
使用行级权限配置实现数据隔离:
**实施步骤:**
1. 在数据表中添加 `department` 字段,记录数据所属部门
2. 在用户信息中添加 `department` 字段,记录用户所属部门
3. 配置数据权限时,设置行级权限规则:`doc.department == user.department`
**参考配置:**
### Q4: 安全规则和角色权限有什么区别?
**角色权限(Role-based Access Control):**
- 基于用户角色的权限管理
- 适合企业级应用,支持组织架构
- 在控制台可视化配置
- 适合大部分常见场景
**安全规则(Security Rules):**
> 详细说明见:[数据库安全规则](/database/security-rules)
- 基于表达式的文档级权限控制
- 支持更复杂的权限逻辑(如基于数据内容的权限判断)
- 需要编写规则表达式
- 适合需要精细控制的场景
**推荐做法:**
- 优先使用角色权限满足基本需求
- 对于复杂场景,结合安全规则实现更精细的控制
- 角色权限和安全规则可以同时使用,最终权限为两者的交集
### Q5: 如何批量管理用户权限?
**方法一:通过组织架构批量管理**
1. 在 [组织架构](https://tcb.cloud.tencent.com/dev#/identity/user-organization) 中创建部门结构
2. 将用户添加到对应部门
3. 为部门统一配置角色,该部门的所有用户自动继承角色权限
**方法二:通过修改角色权限**
1. 找到需要调整的角色
2. 修改该角色的权限配置
3. 该角色下的所有用户自动应用新权限
**方法三:使用 HTTP API 批量操作**
对于大规模用户权限管理,可以使用 CloudBase HTTP API 进行批量操作,参考 [HTTP API 文档](/http-api/auth/登录认证接口)。
## 相关文档
**身份认证相关:**
- [身份认证概述](/authentication-v2/auth/introduce) - 了解 CloudBase 身份认证体系
- [管理用户](/authentication-v2/auth/manage-users) - 用户信息管理和账号操作
- [登录方式管理](/authentication-v2/auth/manage-login) - 配置和启用不同的登录方式
- [JS SDK (V2) 身份认证](/api-reference/webv2/authentication) - Web 端身份认证 API
- [HTTP API 登录认证接口](/http-api/auth/登录认证接口) - HTTP API 身份认证
**数据权限相关:**
- [文档型数据库权限](/database/data-permission) - NoSQL 数据库权限管理
- [数据库安全规则](/database/security-rules) - 文档级权限控制
- [MySQL 数据库权限](/database/configuration/db/tdsql/data-permission) - 关系型数据库权限管理
**资源权限相关:**
- [云函数权限控制](/cloud-function/security-rules) - 云函数调用权限管理
- [云存储权限管理](/storage/data-permission) - 文件存储权限控制
---
# 身份认证/用户/权限/网关权限控制
> 当前文档链接: https://docs.cloudbase.net/authentication-v2/auth/auth-gateway
「网关权限」是云开发资源访问控制的第一道防线。当携带 [云开发用户身份](/authentication-v2/auth/manage-users) 的请求通过 **[HTTP API 域名](/http-api/basic/overview)、[HTTP 访问服务域名](/service/introduce) 或 [云托管域名](/run/develop/access/client)** 访问云开发资源(云函数、云托管、云存储、AI 能力等)时,请求首先到达网关层,由网关根据配置的策略决定:
- **放行(allow)**:请求通过网关,进入资源层继续鉴权
- **拒绝(deny)**:请求被网关拦截,**不会**到达资源层
**鉴权顺序**
网关鉴权 **早于** 资源鉴权。[查看资源权限文档](https://docs.cloudbase.net/authentication-v2/auth/auth-control#%E8%B5%84%E6%BA%90%E6%9D%83%E9%99%90)
以云函数为例:
| 网关策略 | 执行情况 | 结果 |
| ---------- | -------------------------- | --------------------- |
| **未放行** | 网关直接拒绝(403/无权限) | 函数**不会被执行** |
| **已放行** | 请求到达云函数 | 继续资源层/业务层鉴权 |
:::info 提示
网关权限只控制"能否访问",不控制"能做什么"。细粒度的数据权限应在资源层(如数据库安全规则)或业务层实现。
:::
### 默认策略
平台默认为每个角色关联了一定的预设策略,使不同的角色通过不同的链路访问时默认拥有不同的访问权限。具体如下:
进入角色详情页 → 点击「添加自定义策略」
资源类型选择「网关」
## 策略配置
网关权限支持两种策略配置方式:**预设策略**和**自定义策略**。预设策略为一系列策略鉴权模板,可直接关联到用户所属角色,使角色具备相应的权限。如预设策略不满足需求,可根据语法编写自定义策略实现更灵活的权限控制。
### 预设策略
系统提供常用的预设策略,开箱即用,适合大多数场景。
**场景 1:开发测试阶段 - 管理员全权限**
- **适用场景**:个人开发、快速验证功能
- **推荐策略**:`AdministratorAccess`
- **优势**:无权限阻断,快速迭代
- **注意事项**:⚠️ 生产环境不要将此策略绑定到面向公网的应用身份
**场景 2:团队协作 - 按资源类型拆分**
- **适用场景**:多人协作开发,按资源域分工
- **推荐策略**:
- `StoragesAccess`:云存储访问权限
- `FunctionsAccess`:云函数访问权限
- `CloudrunAccess`:云托管访问权限
- **优势**:按资源隔离权限,降低误操作风险
**场景 3:对外服务 - HTTP 访问控制**
- **适用场景**:通过 HTTP API 对外提供服务
- **推荐策略**:
- `FunctionsHttpApiAllow` / `FunctionsHttpServiceAllow`
- `CloudrunHttpApiAllow`
- `StoragesHttpServiceAllow`
- **最佳实践**:⚡ 先开放最小权限集合,再根据需求逐步扩展
### 自定义策略
当预设策略无法满足需求时,可以自定义策略实现精细化权限控制。
**策略语法说明**
策略 `policy` 由版本 `version` 和语句 `statement` 构成。每条语句 `statement` 包含以下字段:
| 字段 | 是否必填 | 说明 |
| ---------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `effect` | 必填 | 策略效果:`allow`(允许)或 `deny`(拒绝) |
| `action` | 必填 | 操作类型采用**四段式**格式:`资源标识:域名:HTTP方法:请求路径`
配置后,用户收到的邮件效果:
**常见邮箱 SMTP 配置参考:**
| 邮箱 | SMTP 服务器主机 | SMTP 服务器端口 | SMTP 安全模式 |
| ------------ | ------------------ | --------------- | ---------------------- |
| qq 邮箱 | smtp.qq.com | 465/587 | SSL(465)/STARTTLS(587) |
| 腾讯企业邮箱 | smtp.exmail.qq.com | 465 | SSL |
| 163 邮箱 | smtp.163.com | 465 | SSL |
| gmail | smtp.gmail.com | 465/587 | SSL(465)/STARTTLS(587) |
详细的 SMTP 配置指南请参考 [自定义 SMTP 配置指南](#自定义-smtp-配置指南) 章节。
然后,在「账户」设置中,找到「开启服务」设置项,开启 IMAP/SMTP 服务:
开启成功后,请保存您的邮箱登录授权码:
:::tip 注意
您也可以开启 POP3/SMTP 服务,两种服务的授权码都可以作为第 3 步的 SMTP 账号密码。
:::
#### 第 3 步:配置 QQ 邮箱作为发件人
使用 QQ 邮箱作为发件人地址和 SMTP 账号用户名,使用第 2 步的授权码作为 SMTP 账号密码。
### Gmail 邮箱
#### 第 1 步:登录 Gmail 邮箱
Gmail 邮箱默认已开启 IMAP/SMTP 服务,此步骤验证邮箱是否可用。
#### 第 2 步:开启 Google 账号两步验证
访问 [Google 账号安全设置](https://myaccount.google.com/security?utm_source=OGB&utm_medium=app),点击「安全(Security)」→「两步验证(2-Step Verification)」进行开启。
#### 第 3 步:开启 Google 账号应用专用密码
访问 [应用专用密码设置](https://support.google.com/accounts/answer/185833?hl=zh-Hans),生成应用专用密码。
#### 第 4 步:配置云开发平台邮箱登录 SMTP
| 配置项 | 配置值 |
| --------------- | ------------------ |
| 发件人 | 你的 Gmail 邮箱 |
| SMTP 服务器主机 | smtp.gmail.com |
| 端口 | 465 |
| SMTP 账号用户名 | 你的 Gmail 邮箱 |
| SMTP 账号密码 | 第 3 步的应用专用密码 |
| SMTP 安全模式 | SSL |
---
# 身份认证/注册登录/微信授权登录
> 当前文档链接: https://docs.cloudbase.net/authentication-v2/method/wechat-login
经微信开放平台(普通网站应用及移动应用等)授权的应用可以直接使用微信登录 CloudBase。
## 前置条件
### 开启微信登录
1. 首先需要一个开放平台的注册账号,如果没有,请前往 [微信开放平台](https://open.weixin.qq.com/) 申请
2. 前往 [云开发平台/身份认证/登录方式](https://tcb.cloud.tencent.com/dev?#/identity/login-manage)
3. 在登录方式列表中,选择「微信开放平台登录」方式,点击「去设置」填入 `AppId` 和 `AppSecret`
## 登录流程
### 获取第三方平台授权页地址
填入 [`微信平台 ID`](/api-reference/webv2/authentication#系统内置三方列表)、`重定向 URI`、`自定义状态标识` 字段用于识别平台回调来源。
[`Auth.genProviderRedirectUri`](/api-reference/webv2/authentication#authgenproviderredirecturi) 用于生成第三方平台授权页 URL
```js
const { uri } = await auth.genProviderRedirectUri({
provider_id: "wx_open", // 微信开放平台
provider_redirect_uri: providerUri, // 重定向 URI
state: state, // 自定义状态标识
other_params: otherParams, // 其他参数
});
```
### 访问 URI 并进行授权后,回调至指定地址,获取第三方平台 Token
将用户重定向至 URI,例如 `location.href = uri`;待用户授权完毕后,回调至指定的 `provider_redirect_uri` 地址。
```js
// 此时 URL query 中携带授权 code,state 等参数
// 1. 检查 state 是否符合预期(如设置的 wx_open)
// 2. 获取第三方平台跳转回页面时,URL param 中携带的 code 参数
const provider_code = "your_provider_code";
// 3. 如符合预期,获取第三方平台 Token
const { provider_token } = await auth.grantProviderToken({
provider_id: "wx_open",
provider_redirect_uri: "curpage", // 指定三方平台跳回的 URL 地址
provider_code: provider_code, // 第三方平台跳转回页面时,URL param 中携带的 code 参数
});
```
### 通过第三方平台 Token 登录
[`Auth.signInWithProvider`](/api-reference/webv2/authentication#authsigninwithprovider) 方法用于 **第三方平台登录**
```js
await auth.signInWithProvider({
provider_token: provider_token,
});
```
## 注册流程
使用微信授权登录时,用户在第一次使用微信登录时开发者需要使用[Auth.bindWithProvider](/api-reference/webv2/authentication#authbindwithprovider)进行用户信息创建。
具体流程如下:
1. 用户第一次使用微信登录时,调用[signInWithProvider](/authentication-v2/method/wechat-login#%E9%80%9A%E8%BF%87%E7%AC%AC%E4%B8%89%E6%96%B9%E5%B9%B3%E5%8F%B0-token-%E7%99%BB%E5%BD%95)会返回`not_found`错误码
2. 使用其他方式注册用户(例如手机验证码、用户名密码、邮箱验证码等),然后调用[Auth.bindWithProvider](/api-reference/webv2/authentication#authbindwithprovider)将用户的微信信息(如昵称、头像等)同步到 CloudBase 用户资料中,后续使用相同微信账号登录时,会直接登录到对应的 CloudBase 用户
3. 让用户再次触发[登录流程](/authentication-v2/method/wechat-login#%E7%99%BB%E5%BD%95%E6%B5%81%E7%A8%8B)
```js
try {
await auth.signInWithProvider({
provider_token: provider_token,
});
} catch (e) {
// 未关联,去绑定用户
if (e.error === "not_found") {
// 使用其他方式注册用户
// 绑定用户
await auth.bindWithProvider({
provider_token: provider_token,
});
// 绑定成功,重新让用户进行登录
} else {
throw e;
}
}
```
---
# 身份认证/注册登录/Google 登录
> 当前文档链接: https://docs.cloudbase.net/authentication-v2/method/google-login
:::tip 平台支持
Google 社交登录目前仅支持 Web 端(PC/H5),暂不支持小程序端。
:::
使用 Google 账号快速登录您的应用或网站,无需单独注册账号
## 效果预览
**登录页展示**

**登录授权页**
用户点击 Google 登录后,将跳转至 Google 授权页面:

## 配置指南
Google 登录配置需要在「云开发控制台」和「Google 开发者控制台」之间来回操作,建议按照以下步骤完成配置:
### 配置流程概览
```
云开发控制台(获取回调地址)→ Google 控制台(创建应用并配置回调地址)→ 云开发控制台(完成配置)
```
### 步骤 1:在云开发控制台获取回调地址
#### 1.1 进入身份认证配置页面
1. 登录 [云开发控制台/身份认证/登录方式](https://tcb.cloud.tencent.com/dev?envId=#/identity/login-manage)
2. 找到「Google Web 端」卡片,点击「配置」按钮

#### 1.2 自定义登录页展示
配置身份源的显示信息,这将影响用户在登录页看到的内容:
- **身份源名称**:建议填写「Google 登录」或「使用 Google 账号登录」
- **Logo 图标**:可以上传自定义图标或使用默认 Google 图标
#### 1.3 复制回调地址
进入配置页面后,在「身份源基础配置」区域可以看到回调地址(格式为:`https://xxx.tcloudbaseauth.com/xxx`):
**复制这个回调地址**,稍后需要在 Google 控制台中使用。
> ⚠️ **注意**:暂时不要关闭云开发控制台页面,稍后还需要回到这里完成配置。
### 步骤 2:在 Google 开发者控制台创建 OAuth 应用
#### 2.1 进入 Google API Console
前往 [Google API Console Credentials](https://console.cloud.google.com/apis/credentials) 控制台。
> 💡 **提示**:如果您还没有 Google Cloud 项目,需要先创建一个项目。
#### 2.2 创建 OAuth 客户端 ID
点击「创建凭据」按钮,选择「OAuth 客户端 ID」:

#### 2.3 配置 OAuth 客户端
填写以下配置信息:
1. **应用类型**:选择「Web 应用」(Web Application)
2. **名称**:为您的应用命名(如:我的云开发应用)
3. **已获授权的重定向 URI**:粘贴在步骤 1.2 中复制的回调地址
> ⚠️ **重要**:重定向 URI 必须与云开发控制台提供的回调地址完全一致,否则会导致登录失败。
#### 2.4 获取凭据信息
点击「创建」按钮后,Google 会生成 OAuth 客户端的凭据信息:
- **Client ID**(客户端 ID)
- **Client Secret**(客户端密钥)
**复制并保存这两个值**,稍后需要填入云开发控制台。
### 步骤 3:回到云开发控制台完成配置
#### 3.1 填写身份源基础配置
回到步骤 1 中打开的云开发控制台配置页面,在「身份源基础配置」中填入以下信息:
- **Client ID**:填入步骤 2.4 中保存的客户端 ID
- **Client Secret**:填入步骤 2.4 中保存的客户端密钥
- **授权 URL**:使用默认值 `https://accounts.google.com/o/oauth2/v2/auth`
- **Scope 授权范围**:使用默认值或根据需求调整(如 `openid profile email`)
> 💡 **提示**:关于 Scope 授权范围的详细说明,请参考 [Google OAuth 2.0 文档](https://developers.google.com/identity/protocols/oauth2/scopes)。
#### 3.2 保存配置
填写完成后,点击「保存」按钮完成配置。
## 接入指南
### 方式一:使用托管登录页(推荐)
「云开发 CloudBase」提供了开箱即用的托管登录页,无需编写登录界面代码,即可快速集成 Google 登录。
1. 进入 [云开发控制台/身份认证/登录页](https://tcb.cloud.tencent.com/dev?#/identity/land-page)
2. 在「登录方式配置」中勾选「Google 登录」
3. 点击「保存」

### 方式二:使用 SDK 自定义登录
如果您需要自定义开发登录界面和流程,可以使用「云开发 Web SDK」提供的 API 进行登录
#### 获取第三方平台授权页地址
[`Auth.genProviderRedirectUri`](/api-reference/webv2/authentication#authgenproviderredirecturi) 用于生成第三方平台授权页 URL
```javascript
import cloudbase from "@cloudbase/cloudbase";
const app = cloudbase.init({
env: "your-env-id",
region: "ap-shanghai", // 不传默认为上海地域
});
const auth = app.auth();
const { uri } = await auth.genProviderRedirectUri({
provider_id: "google", // google平台
provider_redirect_uri: providerUri, // 重定向 URI
state: state, // 自定义状态标识
other_params: otherParams, // 其他参数
});
```
### 访问 URI 并进行授权后,回调至指定地址,获取第三方平台 Token
将用户重定向至 URI,例如 `location.href = uri`;待用户授权完毕后,回调至指定的 `provider_redirect_uri` 地址。
```js
// 此时 URL query 中携带授权 code,state 等参数
// 1. 检查 state 是否符合预期(如设置的参数)
// 2. 获取第三方平台跳转回页面时,URL param 中携带的 code 参数
const provider_code = "your_provider_code";
// 3. 如符合预期,获取第三方平台 Token
const { provider_token } = await auth.grantProviderToken({
provider_id: "google",
provider_redirect_uri: "curpage", // 指定三方平台跳回的 URL 地址
provider_code: provider_code, // 第三方平台跳转回页面时,URL param 中携带的 code 参数
});
```
### 通过第三方平台 Token 登录
[`Auth.signInWithProvider`](/api-reference/webv2/authentication#authsigninwithprovider) 方法用于 **第三方平台登录**
```js
await auth.signInWithProvider({
provider_token: provider_token,
});
```
---
# 身份认证/注册登录/自定义登录
> 当前文档链接: https://docs.cloudbase.net/authentication-v2/method/custom-login
开发者可以使用**自定义登录**,在自己的服务器或者云函数内,为用户签发带有**自定义身份 ID** 的「自定义登录凭证 Ticket」,随后用户端 SDK 便可以使用 「Ticket」 登录
## 适用场景
自定义登录适用于以下场景:
- 开发者希望将自有的账号体系与云开发 CloudBase 账号进行一对一关联
- 开发者希望自行接管鉴权流程
## 步骤概览
自定义登录需要以下几个步骤:
1. 获取 CloudBase 自定义登录私钥
2. 使用 CloudBase 服务端 SDK,通过私钥签发出 Ticket,并返回至用户端
3. 用户端 SDK 使用 Ticket 登录 CloudBase
## 前置动作
### 获取自定义登录私钥
1. 前往 [云开发平台/身份认证/登录方式](https://tcb.cloud.tencent.com/dev?#/identity/login-manage)
2. 在登录方式列表中,选择「自定义登录」方式,点击「启用」,再点击私钥下载
私钥是一份携带有 JSON 数据的文件,请将下载或复制的私钥文件保存到您的服务器或者云函数中,假设路径为 `/path/to/your/tcb_custom_login.json`。
:::tip 注意
1. 私钥文件是证明管理员身份的重要凭证,请务必妥善保存,避免泄漏
2. 每次生成私钥文件都会**使之前生成的私钥文件在 2 小时后失效**
:::
## 签发 Ticket
调用 CloudBase 服务端 SDK,在初始化时传入自定义登录私钥,随后便可以签发出 Ticket,并返回至用户端。
```js
const cloudbase = require("@cloudbase/node-sdk");
// 1. 初始化 SDK
const app = cloudbase.init({
env: "your-env-id",
region: "ap-shanghai", // 不传默认为上海地域
// 传入自定义登录私钥
credentials: require("/path/to/your/tcb_custom_login.json"),
});
// 2. 开发者自定义的用户唯一身份标识
const customUserId = "your-customUserId";
// 3. 创建ticket
const ticket = app.auth().createTicket(customUserId);
// 4. 将ticket返回至客户端
return ticket;
```
:::tip 注意
`customUserId` 必须满足以下需求:
- 4-32 位字符
- 字符只能是大小写英文字母、数字、以及 `_-#@(){}[]:.,<>+#~` 中的字符
:::
:::tip 提示
开发者也可以编写一个**云函数**用于生成 Ticket,并为其设置 HTTP 访问服务,随后用户端便可以通过 HTTP 请求的形式获取 Ticket,详细的方案请参阅 [使用 HTTP 访问云函数](/service/access-cloud-function) 。
:::
## 注册流程
使用自定义登录时,用户管理完全由开发者自行处理。在 CloudBase 中,自定义登录不需要单独的注册流程,用户在第一次使用自定义登录时会自动创建对应的 CloudBase 用户。
注册流程主要包括:
1. 在您的用户系统中创建用户账号
2. 使用 CloudBase 服务端 SDK 为该用户签发 Ticket
3. 客户端使用 Ticket 完成首次登录(此时 CloudBase 会自动创建对应用户)
## 登录流程
用户端应用获取到 Ticket 之后,便可以进行登录
[`Auth.setCustomSignFunc`](/api-reference/webv2/authentication#authsetcustomsignfunc) 用于设置获取自定义登录的 ticket 函数
[`Auth.signInWithCustomTicket`](/api-reference/webv2/authentication#authsigninwithcustomticket) 用于自定义登录
```js
import cloudbase from "@cloudbase/js-sdk";
const app = cloudbase.init({
env: "your-env-id",
});
const auth = app.auth();
async function login() {
const loginState = auth.hasLoginState();
// 1. 建议登录前检查当前是否已经登录
if (!loginState) {
// 2. 请求开发者自有服务接口获取ticket
await auth.setCustomSignFunc(() => {
// 调用开发者自有服务接口获取ticket
const ticket = "xxx";
// 获取 ticket 并返回 Promise
return Promise.resolve(ticket);
});
// 3. 登录 CloudBase
await auth.signInWithCustomTicket();
}
}
login();
```
整体流程示意如下:

---
# 身份认证/其他参考/账户关联登录
> 当前文档链接: https://docs.cloudbase.net/authentication-v2/auth/account-linking
每个云开发用户账号,除了最初注册时使用的登录方式外,还可以关联其他登录方式。关联后,无论用户使用哪种登录方式,均可以登录到同一个云开发账户。
## 关联手机号密码登录
:::tip 提示
仅支持 `上海` 地域
:::
当前用户支持密码登录时,可以为用户绑定手机号,绑定后用户可以使用「手机号+密码」完成登录:
1. 用户以任意一种登录方式登录
2. 获取 `sudo_token`,这里以密码方式获取 `sudo_token`,还可以使用邮箱验证码、手机号验证码等方式,具体请参考 [`Auth.sudo`](/api-reference/webv2/authentication#authsudo) 接口
```javascript
import cloudbase from "@cloudbase/js-sdk";
const app = cloudbase.init({
env: "your-env-id",
});
// 获取 auth 实例
const auth = app.auth();
// 假设用户输入的密码为 passwd
const password = "passwd";
// 获取 sudo_token,sudo_token 的过期时间默认为 10 分钟
const sudo_token = await auth.sudo({
password: password,
});
```
3. 向用户手机发送验证短信
```javascript
// 假设用户手机号为 13800000000
const phoneNumber = "+86 13800000000";
// 发送验证码
const verification = await auth.getVerification({
phone_number: phoneNumber,
});
```
4. 校验用户输入的验证码
```javascript
// 假设用户输入的验证码为 000000
const verificationCode = "000000";
// 校验验证码
const verificationTokenRes = await auth.verify({
verification_id: verification.verification_id,
verification_code: verificationCode,
});
const verification_token = verificationTokenRes.verification_token;
```
5. 使用 `verification_token` 和 `sudo_token` 绑定手机号
```javascript
await auth.bindPhoneNumber({
sudo_token: sudo_token,
phone_number: phoneNumber,
verification_token: verification_token,
});
```
## 关联邮箱密码登录
当前用户支持密码登录时,可以为用户绑定邮箱,绑定后用户可以使用「邮箱+密码」完成登录:
1. 用户以任意一种登录方式登录云开发
2. 获取 `sudo_token`,这里以密码方式获取 `sudo_token`,还可以使用邮箱验证码、手机号验证码等方式,具体请参考 [`Auth.sudo`](/api-reference/webv2/authentication#authsudo) 接口
```javascript
// 假设用户输入的密码为 passwd
const password = "passwd";
// 获取 sudo_token,sudo_token 的过期时间默认为 10 分钟
const sudo_token = await auth.sudo({
password: password,
});
```
3. 给邮箱发送验证码
```javascript
// 假设用户邮箱为 "test@example.com"
const email = "test@example.com";
// 获取邮箱验证码
const verification = await auth.getVerification({
email: email,
});
```
4. 校验用户输入的验证码
```javascript
// 假设用户输入的验证码为 000000
const verificationCode = "000000";
// 校验验证码
const verificationTokenRes = await auth.verify({
verification_id: verification.verification_id,
verification_code: verificationCode,
});
const verification_token = verificationTokenRes.verification_token;
```
5. 使用 `verification_token` 和 `sudo_token` 绑定邮箱
```javascript
await auth.bindEmail({
sudo_token: sudo_token,
email: email,
verification_token: verification_token,
});
```
## 关联微信登录
关联微信登录的步骤如下:
1. 用户以任意一种登录方式(除微信登录)登录云开发
2. 参考 [微信授权登录](/authentication-v2/method/wechat-login#第-2-步使用-sdk-处理登录流程) 获取微信授权 `provider_token`
3. 使用授权 `token` 关联微信登录
```javascript
const app = cloudbase.init({
env: "xxxx-yyy",
});
const provider_token = "test_provider_token"; // 上一步取得的授权 token
const auth = app.auth();
await auth.bindWithProvider({
provider_token,
});
```
---
# 身份认证/其他参考/验证码处理指南
> 当前文档链接: https://docs.cloudbase.net/authentication-v2/method/captcha
本文档介绍如何在腾讯云开发身份验证中处理验证码相关逻辑,包括触发条件、错误处理和完整的实现流程。
## 触发条件
验证码会在以下情况下被要求输入:
- 用户名密码登录失败 5 次后
- 发送手机号或邮箱验证码时达到频率限制
## 错误信息
当需要验证码时,接口会返回相应的错误信息:
- `error == captcha_required`:表示请求触发了验证码相关逻辑,需要进行机器验证
- `error == captcha_invalid`:表示验证码无效,需要重新获取验证码
:::tip 注意
验证码流程完成后,若业务接口返回 `error` 等于 `captcha_required`,表示请求需要 `captcha_token` 参数,应尽可能使用本地未过期的验证码。当 `error` 等于 `captcha_invalid` 时,表示验证码无效,需要重新获取验证码。在同一个验证流程内,`captcha_invalid` 最多尝试一次即可。
:::
```typescript
// 错误信息示例:
{
data: {
error: "captcha_required",
error_code: 4001,
error_description: "captcha_token required"
}
}
```
## 处理流程
### 完整流程
验证码处理的完整流程如下:
1. 用户尝试登录或发送验证码
2. 若触发验证码要求,SDK 抛出 `captcha_required` 错误
3. 编写 SDK 适配器,捕获错误并触发 `openURIWithCallback`
4. `openURIWithCallback` 方法中获取验证码参数,并通过 `EVENT_BUS` 发送给前端展示
5. 前端展示验证码图片并等待用户输入
6. 用户输入验证码并提交
7. 系统验证并返回结果
8. 根据验证结果决定是否重试原操作
关于第三步的 SDK 适配器以及 `openURIWithCallback` 方法请参考 [适配器指引](/api-reference/webv2/adapter/)
### 适配器实现
```typescript
function genAdapter(options) {
const adapter: SDKAdapterInterface = {
captchaOptions: {
openURIWithCallback: async (url: string) => {
// 解析 URL 中的验证码参数
const { captchaData, state, token } = cloudbase.parseCaptcha(url);
// 通过事件总线发送验证码数据,进行前端缓存及展示
options.EVENT_BUS.emit("CAPTCHA_DATA_CHANGE", {
captchaData, // Base64 编码的验证码图片
state, // 验证码状态标识
token, // 验证码 token
});
// 监听验证码校验结果
return new Promise((resolve) => {
console.log("等待验证码校验结果...");
options.EVENT_BUS.once("RESOLVE_CAPTCHA_DATA", (res) => {
// auth.verifyCaptchaData 的校验结果
resolve(res);
});
});
},
},
};
return adapter;
}
```
### 初始化 SDK
```typescript
import cloudbase from "@cloudbase/js-sdk";
// 创建事件总线实例,具体EventBus实现可参考网上示例
const EVENT_BUS = new EventBus();
// 配置并使用适配器,将 EVENT_BUS 注入给 genAdapter
cloudbase.useAdapters(adapter, { EVENT_BUS });
const app = cloudbase.init({
env: "环境ID",
appSign: "应用标识",
appSecret: {
appAccessKeyId: "应用凭证版本号",
appAccessKey: "应用凭证",
},
});
const auth = app.auth();
```
### 验证码界面实现
```typescript
// 存储当前验证码状态
let captchaState = {
captchaData: "", // Base64 编码的验证码图片
state: "", // 验证码状态标识
token: "", // 验证码 token
};
// 监听验证码数据变化
EVENT_BUS.on("CAPTCHA_DATA_CHANGE", ({ captchaData, state, token }) => {
console.log("收到验证码数据", { captchaData, state, token });
// 更新本地验证码状态
captchaState = { captchaData, state, token };
// 在页面中显示验证码图片,例如在 Web 中使用 img 标签展示
const captchaImage = document.getElementById("captcha-image");
if (captchaImage) {
captchaImage.src = captchaData;
}
});
// 用户点击刷新验证码时调用
const refreshCaptcha = async () => {
try {
// 获取最新验证码信息
const result = await auth.createCaptchaData({
state: captchaState.state,
});
// 更新本地验证码状态
captchaState = {
...captchaState,
captchaData: result.data,
token: result.token,
};
// 更新显示的验证码图片
const captchaImage = document.getElementById("captcha-image");
if (captchaImage) {
captchaImage.src = result.data;
}
} catch (error) {
console.error("刷新验证码失败", error);
}
};
// 用户提交验证码时调用
const verifyCaptchaData = async (userCaptcha) => {
try {
// 校验验证码
const verifyResult = await auth.verifyCaptchaData({
token: captchaState.token,
key: userCaptcha,
});
// 将校验结果通知适配器
EVENT_BUS.emit("RESOLVE_CAPTCHA_DATA", verifyResult);
console.log("验证码校验成功");
} catch (error) {
console.error("验证码校验失败", error);
// 校验失败时可以选择刷新验证码
await refreshCaptcha();
}
};
```
## 验证码展示效果
示例代码地址: [示例代码](https://github.com/TencentCloudBase/awesome-cloudbase-examples/blob/f38fe782c6c099612eb591866e3c35039e816bb6/universal/soul-chat/src/components/show-captcha.vue)
## 相关 API
- `auth.createCaptchaData(options)` - 创建验证码数据
- `auth.verifyCaptchaData(options)` - 验证验证码
- `cloudbase.parseCaptcha(url)` - 解析验证码 URL 参数
## 注意事项
1. 验证码具有时效性,建议在获取后及时使用
2. 同一验证流程中,验证码校验失败最多重试一次
3. 建议在验证码校验失败后提供刷新验证码的功能
4. 确保事件总线的正确初始化和事件监听
---
# 身份认证/其他参考/为注册用户赋予管理员权限
> 当前文档链接: https://docs.cloudbase.net/authentication-v2/auth/practice/grant-admin-permission
## 用户类型与角色说明
**重要概念澄清**:
- **用户类型**(组织成员、注册用户): 决定用户在组织架构中的归属
- **用户角色**(管理员、自定义角色): 决定用户拥有的权限
只有**组织成员**可以被赋予管理员权限或自定义角色。如果需要为**注册用户**赋予管理员权限,需要先将其转为组织成员。
## 方式一: 在组织架构中直接为组织成员分配管理员角色
如果用户已经是组织成员,可以直接为其分配管理员角色:
1. 访问 [云开发平台/身份认证/组织架构](https://tcb.cloud.tencent.com/dev#/identity/user-organization)
2. 在组织架构树中找到目标用户
3. 点击用户右侧的「编辑」按钮
4. 在「角色」选项中选择「管理员」角色
5. 点击「确定」保存配置
## 方式二: 将注册用户导入组织架构(转为组织成员)
如果需要将注册用户纳入企业组织架构管理并赋予管理员权限,可以将其转为组织成员:
1. 访问 [云开发平台/身份认证/组织架构](https://tcb.cloud.tencent.com/dev#/identity/user-organization)
2. 点击「导入用户」按钮
3. 选择「导入注册用户」
4. 在列表中选择需要导入的注册用户
5. 确认后,该用户将转为组织成员并加入组织架构
6. 为该用户分配管理员角色或其他自定义角色
> 💡 **提示**: 组织成员受套餐限制,转换前请确认当前套餐的用户数配额。参考 [计费文档](https://cloud.tencent.com/document/product/876/75213)

## 验证管理员权限
用户被赋予管理员权限后,可以:
- 访问云开发控制台的所有功能
- 管理环境配置、资源和成员
- 执行所有数据库、云函数、云存储操作
- 查看和管理其他用户
建议用户退出重新登录以立即应用新权限。
## 注意事项
- **用户类型转换**: 注册用户转为组织成员后,将受到套餐用户数限制
- **权限生效时间**: 角色分配后建议用户重新登录以立即生效
- **套餐配额**: 转换前请确认当前套餐的组织成员数量配额是否充足
- **权限范围**: 管理员角色拥有环境的完整管理权限,请谨慎分配
## 相关文档
- [用户管理](/authentication-v2/auth/manage-users) - 用户类型说明和基本管理
- [权限控制](/authentication-v2/auth/auth-control) - 角色体系和权限配置详解
- [最佳实践](/authentication-v2/auth/best-practice) - 身份认证和权限管理最佳实践
---
# 身份认证/其他参考/存储额外的用户信息
> 当前文档链接: https://docs.cloudbase.net/authentication-v2/auth/practice/store-extra-user-info
## 系统用户表 vs 自定义用户表
使用云开发登录认证后,用户的基础信息(如 `uid`、登录方式、基础资料等)会自动存储在系统维护的用户表中。如果需要存储业务相关的额外用户信息(如地址、邮箱、会员等级等),**推荐创建自定义用户表**。
**系统用户表**:
- 由云开发自动维护
- 存储用户身份认证相关信息(`uid`、`name`、`gender`、登录方式等)
- 通过 `auth.currentUser` 获取
- 不建议直接在此表添加业务字段
**自定义用户表**(推荐):
- 开发者自行创建和维护(如 `users` 集合)
- 存储业务相关的扩展信息(地址、邮箱、积分、会员等级等)
- 通过 `uid` 与系统用户表关联
- 灵活扩展字段,满足业务需求
## 实现方式
### 1. 创建自定义用户表
在 [云开发平台/文档型数据库](https://tcb.cloud.tencent.com/dev#/db/doc) 创建 `users` 集合,设计以下结构:
```javascript
{
_id: "用户记录ID",
uid: "云开发用户ID(与系统用户表关联)",
address: "用户地址",
email: "邮箱",
phone: "手机号",
vip_level: "会员等级",
createTime: "创建时间"
}
```
### 2. 用户注册时创建扩展信息
```javascript
import cloudbase from '@cloudbase/js-sdk';
const app = cloudbase.init({
env: 'your-env-id',
});
const auth = app.auth();
const db = app.database();
// 用户注册成功后
auth.signUp({
phone_number: '+86 13800000000',
verification_code: '123456',
verification_token: 'token',
name: '张三',
}).then(async () => {
// 获取当前用户 uid
const user = auth.currentUser;
// 在自定义用户表中创建扩展信息
await db.collection('users').add({
uid: user.uid, // 关联系统用户表
address: '北京市朝阳区',
email: 'zhangsan@example.com',
phone: '+86 13800000000',
vip_level: 1,
createTime: new Date(),
});
console.log('用户注册并创建扩展信息成功');
});
```
### 3. 查询用户完整信息
```javascript
// 获取系统用户信息
const user = auth.currentUser;
console.log('系统用户信息:', user.uid, user.name);
// 获取自定义扩展信息
const { data } = await db.collection('users').where({ uid: user.uid }).get();
console.log('用户扩展信息:', data[0].address, data[0].email);
```
### 4. 更新用户扩展信息
```javascript
// 更新自定义用户表中的信息
await db.collection('users').where({ uid: auth.currentUser.uid }).update({
address: '上海市浦东新区',
vip_level: 2,
});
```
## 最佳实践
- **分离关注点**: 系统用户表专注于身份认证,自定义用户表专注于业务逻辑
- **使用 uid 关联**: 通过 `uid` 字段在两个表之间建立关联关系
- **合理设计字段**: 根据业务需求设计自定义用户表的字段结构
- **注意数据同步**: 当用户信息更新时,注意同步更新相关的数据表
## 相关文档
- [用户管理](/authentication-v2/auth/manage-users) - 用户信息管理和更新
- [数据库操作](/database/introduction) - 云开发数据库操作指南
- [最佳实践](/authentication-v2/auth/best-practice) - 身份认证和权限管理最佳实践
---
# 身份认证/其他参考/TCB 登录态长期保持方案
> 当前文档链接: https://docs.cloudbase.net/faq/knowledge/tcb-login-state-refresh-token
在使用腾讯云开发(TCB)进行用户认证时,Token 有效期较短,用户长时间不使用应用后需要重新登录。本文介绍如何使用 refresh token 机制实现登录态的长期保持。
## 问题现象
使用云开发 HTTP API 进行用户认证时遇到以下问题:
- Token 有效期较短,需要频繁刷新
- 用户长时间(几天或几周)不使用应用后,Token 过期需要重新登录
- 应用在后台无法触发 Token 刷新,导致用户体验不佳
## 解决方案
云开发提供了 **refresh token 机制**,可以在主 Token 过期后通过 refresh token 自动获取新的访问 Token。
### Refresh Token 机制说明
| Token 类型 | 有效期 | 用途 |
|-----------|-------|------|
| Access Token | 2 小时(7200 秒) | 访问云资源的凭证 |
| Refresh Token | 默认 31 天(可配置) | 用于刷新 Access Token |
### 使用方法
#### 1. 获取 Token(包含 refresh_token)
首次登录时,调用 `/auth/v1/token` 接口获取 Token(以密码模式为例):
```javascript
const envId = 'your-env-id'
const response = await fetch(`https://${envId}.ap-shanghai.tcb-api.tencentcloudapi.com/auth/v1/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
grant_type: 'password',
username: 'your-username',
password: 'your-password',
client_id: envId
})
})
const data = await response.json()
// 保存 access_token 和 refresh_token
const { access_token, refresh_token, expires_in } = data
```
#### 2. 使用 Refresh Token 刷新登录态
当 Access Token 过期时,使用 Refresh Token 获取新的 Token:
```javascript
const envId = 'your-env-id'
const response = await fetch(`https://${envId}.ap-shanghai.tcb-api.tencentcloudapi.com/auth/v1/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
grant_type: 'refresh_token',
refresh_token: savedRefreshToken,
client_id: envId
})
})
const data = await response.json()
// 更新保存的 token(注意:原 refresh_token 使用后立即失效)
const { access_token, refresh_token, expires_in } = data
```
### 最佳实践
1. **安全存储 Token**:将 refresh_token 存储在安全的位置(如 Keychain、EncryptedSharedPreferences)
2. **自动刷新机制**:在应用启动或从后台恢复时检查 Token 状态
```javascript
async function ensureValidToken() {
const tokenExpiry = getTokenExpiry()
if (Date.now() >= tokenExpiry - 60000) { // 提前 1 分钟刷新
await refreshAccessToken()
}
}
```
3. **错误处理**:当 refresh_token 也过期时,引导用户重新登录
```javascript
async function refreshAccessToken() {
try {
const response = await refreshToken()
if (response.error) {
// 错误格式: { error: 'invalid_grant', error_code: 4001, error_description: '...' }
if (response.error === 'invalid_grant') {
// refresh_token 无效或已过期,引导用户重新登录
navigateToLogin()
return
}
}
saveTokens(response)
} catch (error) {
console.error('Token 刷新失败', error)
}
}
```
4. **应用恢复时刷新**:在应用从后台恢复时检查并刷新 Token
```javascript
// React Native 示例
import { AppState } from 'react-native'
AppState.addEventListener('change', (nextAppState) => {
if (nextAppState === 'active') {
ensureValidToken()
}
})
```
## 注意事项
1. Refresh Token 也有有效期(默认 31 天),过期后需要用户重新登录
2. **重要**:刷新 Token 时原 refresh_token 立即失效,必须保存并使用返回的新 refresh_token
3. 不要在不安全的位置(如 localStorage)存储 refresh_token
4. 建议在 Token 即将过期前主动刷新,而不是等到过期后再刷新
5. 可在[云开发平台/身份认证/Token](https://tcb.cloud.tencent.com/dev?#/identity/token-management)修改 Refresh Token 有效期
## 相关文档
- [HTTP API 认证文档](https://docs.cloudbase.net/http-api/auth/auth-grant-token#1-refresh-token-%E5%88%B7%E6%96%B0%E6%A8%A1%E5%BC%8F)
- [身份认证概述](/authentication/introduce)
---
# 身份认证/其他参考/最佳实践
> 当前文档链接: https://docs.cloudbase.net/authentication-v2/auth/best-practice
本文档提供云开发身份认证与权限控制的最佳实践指南,帮助开发者快速理解和接入身份认证模块,构建安全可靠的应用系统。
## 功能介绍
### 核心概念
云开发身份认证体系包含两个核心部分:
- **身份认证**:解决"用户是谁"的问题,支持手机号、邮箱、微信、匿名等多种登录方式
- **权限控制**:解决"用户能调用什么资源"的问题,通过角色和策略精细化管理资源访问权限
完整的身份认证接入需要理解:登录方式 → 角色体系 → 成员管理 → 权限策略 → 安全配置。
### 登录方式
云开发支持多种登录方式,需要先在 [云开发平台/身份认证/登录方式](https://tcb.cloud.tencent.com/dev?#/identity/login-manage) 中开启,可根据应用场景灵活选择:
| 登录方式 | 适用场景 | 配置入口 |
| ---------- | ---------------------- | ------------------------------------------------------------------------ |
| 手机号登录 | 需要实名认证的应用 | [登录方式管理](https://tcb.cloud.tencent.com/dev#/identity/login-manage) |
| 邮箱登录 | 企业应用、国际化应用 | [登录方式管理](https://tcb.cloud.tencent.com/dev#/identity/login-manage) |
| 微信登录 | 微信生态应用 | [登录方式管理](https://tcb.cloud.tencent.com/dev#/identity/login-manage) |
| 匿名登录 | 无需注册即可使用的功能 | [注册配置](https://tcb.cloud.tencent.com/dev#/identity/login-manage) |
| 用户名密码 | 传统 Web 应用 | [登录方式管理](https://tcb.cloud.tencent.com/dev#/identity/login-manage) |
> 💡 **建议**:应用可以同时开启多种登录方式,让用户自主选择。
### 角色体系
#### 角色定义
角色是权限的载体,通过将用户分配到不同角色,实现精细化的权限管理。系统提供以下预设角色:
| 角色类型 | 说明 | 典型使用场景 |
| ------------ | ------------------------------------ | ------------------------------------------- |
| **管理员** | 拥有所有权限,可管理环境、成员、资源 | 项目负责人、技术主管 |
| **所有用户** | 包含所有已登录和未登录用户 | 公开内容访问控制 |
| **组织成员** | 添加到组织架构的用户 | 企业内部应用访问 |
| **外部用户** | 除组织成员外,非匿名登录的用户 | C端应用用户,需要登录进行访问,如电商应用等 |
| **匿名用户** | 未登录的访客 | 公开页面、营销落地页 |
> ⚠️ **重要提醒**:管理员角色拥有系统的**最高权限**,包括删除数据、修改配置、管理成员等敏感操作,请谨慎使用。
#### 自定义角色
当预设角色无法满足业务需求时,可以创建自定义角色:
1. 访问 [云开发平台/身份认证/权限控制](https://tcb.cloud.tencent.com/dev#/identity/auth-control) 页面
2. 点击「创建角色」
3. 设置角色标识和描述,例如:
- `content_editor`:内容编辑
- `data_analyst`:数据分析
- `finance_admin`:财务管理
4. 为角色配置访问权限
#### 角色权限合并规则
当用户拥有多个角色时,权限合并遵循以下规则:
- **允许权限取并集**:用户拥有所有角色的允许权限之和
- **拒绝权限优先**:任一角色的拒绝规则会覆盖其他角色的允许规则
**示例:**
```
用户角色:内容编辑 + 数据分析
- 内容编辑:允许访问文章表(读、写、改)
- 数据分析:允许访问订单表(仅读)
- 结果:用户可以读写文章,可以查看订单
```
### 成员管理
#### 成员添加
将用户添加到角色,有以下两种方式:
**方式一:在角色中添加成员**
1. 访问 [云开发平台/身份认证/权限控制](https://tcb.cloud.tencent.com/dev#/identity/auth-control) 页面找到目标角色
2. 点击角色的「配置成员」
3. 点击「添加成员」批量选择用户
4. 确认后立即生效
**方式二:在组织架构中管理**
1. 访问 [云开发平台/身份认证/组织架构](https://tcb.cloud.tencent.com/dev#/identity/user-organization) 页面
2. 创建部门和岗位结构
3. 将用户添加到对应的组织节点
4. 批量为用户关联角色
#### 成员权限变更
修改用户权限有两种途径:
- **修改角色权限**:该角色下所有用户自动继承新权限
- **调整用户角色**:将用户从某个角色移除或添加到新角色
> 💡 **提示**:权限变更可能需要一定时间生效,建议用户退出重新登录。
### 权限控制
#### 权限配置模型
云开发的权限控制基于以下模型:
```
权限配置 = 资源范围 + 数据范围 + 操作类型
```
- **资源范围**:该角色可以访问哪些资源(数据表、云函数、云存储等)
- **数据范围**:可以访问哪些数据(全部数据、仅自己创建的数据、特定条件数据)
- **操作类型**:可以执行什么操作(查看、创建、修改、删除)
#### 数据行权限
通过行级权限控制用户可以访问哪些数据行(记录)。常见的配置方式:
- **所有数据**:用户可以访问全部数据
- **仅自己的数据**:用户只能访问自己创建的数据
- **特定条件数据**:用户只能访问满足特定条件的数据(如所属部门、特定状态等)
**示例:**销售人员只能查看自己负责的客户数据
#### 数据列权限
通过字段级权限控制用户可以访问哪些字段(列)。可以配置:
- **可读字段**:用户可以查看的字段列表
- **隐藏字段**:对用户不可见的敏感字段
**示例:**隐藏销售人员的成本和利润字段
> 💡 **提示**:行级权限和字段级权限可以同时使用,实现更精细的数据访问控制。
#### 操作权限
为角色配置对资源的操作权限:
- **查看(Read)**:读取数据
- **创建(Create)**:新增数据
- **修改(Update)**:编辑数据
- **删除(Delete)**:删除数据
不同资源类型支持的操作权限可能有所差异,具体请参考相应的资源文档:
- [文档型数据库权限](/database/data-permission)
- [MySQL数据库权限](/database/configuration/db/tdsql/data-permission)
- [云函数权限控制](/cloud-function/security-rules)
## 典型场景实践示例
### 场景一:个人博客网站
#### 需求描述
个人博客需要支持游客浏览、用户评论和博主管理三种场景。
#### 角色规划
| 角色 | 权限范围 | 使用对象 |
| -------- | ---------------------------- | ---------- |
| 匿名用户 | 浏览文章、查看评论 | 所有访客 |
| 注册用户 | 浏览文章、发表评论、点赞 | 注册读者 |
| 管理员 | 发布文章、管理评论、网站设置 | 博客所有者 |
#### 数据权限配置
**文章表(`blog_articles`)权限设置:**
- 匿名用户:仅可读取已发布文章
- 注册用户:可读取已发布文章和自己写的草稿
- 管理员:拥有所有权限
**评论表(`comments`)权限设置:**
- 匿名用户:仅可查看评论
- 注册用户:可查看所有评论,可修改/删除自己的评论
- 管理员:拥有所有权限
#### 实施步骤
1. **启用匿名登录**:在 [登录方式管理](https://tcb.cloud.tencent.com/dev#/identity/login-manage) 开启「允许匿名登入」
2. **配置数据库权限**:在数据库集合的权限设置中配置对应角色的访问权限,具体请参考:[文档型数据库权限](/database/data-permission)
3. **前端默认调用匿名登录**:
```javascript
import cloudbase from '@cloudbase/js-sdk';
const app = cloudbase.init({
env: 'your-env-id'
});
const auth = app.auth();
// 默认执行匿名登录操作
auth.signInAnonymously();
```
### 场景二:企业内部管理系统
#### 需求描述
企业管理系统需要区分不同部门和岗位的访问权限,实现数据隔离。
#### 角色规划
**自定义角色创建:**
1. 在 [权限控制](https://tcb.cloud.tencent.com/dev#/identity/auth-control) 页面创建自定义角色
2. 根据岗位职责设置角色标识,例如:
- `content_editor`:内容编辑
- `data_analyst`:数据分析
- `finance_admin`:财务管理
#### 数据权限配置
**行级权限配置:**
实现"用户只能访问自己部门的数据":
**字段级权限配置:**
隐藏敏感字段(如成本、利润):
### 场景三:协同编辑平台
#### 需求描述
多人协同文档编辑平台,允许用户编辑他人创建的文档,但需要严格的权限控制。
#### 角色规划
| 角色 | 权限范围 | 使用对象 |
| -------- | -------------------------------------- | ------------ |
| 普通用户 | 查看所有文档、编辑自己的文档 | 注册用户 |
| 协作者 | 查看所有文档、编辑被授权的文档 | 被邀请的用户 |
| 管理员 | 所有文档的完整权限(查看、编辑、删除) | 团队管理者 |
#### 实现方案
> ⚠️ **安全警告**:允许修改他人数据是高风险操作,必须谨慎实施权限控制。
**方案一:通过云函数修改数据(推荐)**
云函数在服务端执行,可以绕过客户端权限限制,但需要在函数内部实现严格的权限验证:
```javascript
// 云函数:editDocument
const cloudbase = require('@cloudbase/node-sdk');
const app = cloudbase.init();
const db = app.database();
exports.main = async (event, context) => {
const { documentId, newContent } = event;
const { userInfo } = context;
// 1. 验证用户登录状态
if (!userInfo || !userInfo.uid) {
return { code: 401, message: '未登录' };
}
// 2. 查询文档信息
const doc = await db.collection('documents')
.doc(documentId)
.get();
if (!doc.data || doc.data.length === 0) {
return { code: 404, message: '文档不存在' };
}
const document = doc.data[0];
// 3. 权限验证:检查是否是作者或协作者
const isOwner = document._openid === userInfo.openid;
const isCollaborator = document.collaborators &&
document.collaborators.includes(userInfo.uid);
if (!isOwner && !isCollaborator) {
return { code: 403, message: '无权编辑此文档' };
}
// 4. 执行更新
const result = await db.collection('documents')
.doc(documentId)
.update({
content: newContent,
lastEditBy: userInfo.uid,
lastEditAt: new Date()
});
return { code: 0, message: '更新成功', data: result };
};
```
**方案二:通过安全规则配置(精细控制)**
对于需要更复杂、更精细的权限控制场景,可以使用 CloudBase 的**安全规则**功能。
**CloudBase 支持安全规则的资源:**
| 资源类型 | 安全规则文档 | 适用场景 |
| ------------ | ------------------------------------------------ | -------------------------------- |
| 文档型数据库 | [数据库安全规则](/database/security-rules) | 文档级、字段级的精细权限控制 |
| 云存储 | [云存储安全规则](/storage/security-rules) | 文件上传、下载、删除权限控制 |
| 云函数 | [云函数安全规则](/cloud-function/security-rules) | 函数调用权限控制(限客户端 SDK) |
**使用安全规则实现协同编辑:**
```javascript
// 文档型数据库安全规则示例
{
// 读权限:所有登录用户都可以查看文档
"read": "auth != null",
// 写权限:作者、协作者或管理员可以编辑
"write": "doc._openid == auth.openid || doc.collaborators.includes(auth.uid) || auth.role == 'admin'"
}
```
## 安全配置建议
### 管理员角色的谨慎使用
> ⚠️ **重要提醒**:管理员角色拥有系统的**最高权限**,包括删除数据、修改配置、管理成员等敏感操作。
**建议做法:**
- 为日常操作创建权限受限的自定义角色
- 仅在必要时使用管理员账号
- 定期审查管理员账号列表
- 为管理员账号启用双因素认证
### 权限最小化原则
遵循"最小权限原则",仅授予用户完成工作所需的最小权限:
```
✓ 推荐:销售人员只能查看自己负责的客户数据
✗ 避免:销售人员可以查看所有客户数据
```
## 常见问题与解决方案
### Q1: 为什么用户已经添加了权限,但仍然无法访问?
**排查步骤:**
1. **检查角色分配**:确认用户已被分配到正确的角色
2. **检查策略状态**:确认策略已保存并关联到正确的角色上
3. **检查生效时间**:权限变更可能需要一定时间生效,建议用户退出重新登录
4. **检查网关策略**:可能被网关层的限制拦截,检查用户关联的网关权限策略
5. **查看报错信息**:查看具体拒绝原因,针对性解决
### Q2: 多个角色的权限如何合并?
当用户拥有多个角色时,权限合并遵循以下规则:
- **允许权限取并集**:用户拥有所有角色的允许权限之和
- **拒绝权限优先**:任一角色的拒绝规则会覆盖其他角色的允许规则
**示例:**
```
用户角色:内容编辑 + 数据分析
- 内容编辑:允许访问文章表(读、写、改)
- 数据分析:允许访问订单表(仅读)
- 结果:用户可以读写文章,可以查看订单
```
## 相关资源
- [JS SDK (V2)/身份认证](/api-reference/webv2/authentication)
- [HTTP API/登录认证接口](/http-api/auth/登录认证接口)
- [文档型数据库/权限管理](/database/data-permission)
- [MySQL数据库/权限管理](/database/configuration/db/tdsql/data-permission)
- [云函数/权限管理](/cloud-function/security-rules)
- [云存储/权限管理](/storage/data-permission)
---
# 身份认证/其他参考/常见问题
> 当前文档链接: https://docs.cloudbase.net/authentication-v2/auth/faq
## 匿名登录与未登录有什么区别?
从 C 端用户的角度来讲:
- 匿名登录和未登录在上手使用上没有任何区别,都无需注册
- 匿名登录用户有独立的用户标识,在同设备有效期内,用户可以产生独立的私有数据
- 与未登录相比,匿名登录可以转为正式用户,匿名登录期间的私有数据会自动继承到正式用户名下
从应用开发者的角度来讲:
- CloudBase 匿名登录产生的匿名用户本质上是一个有效用户,拥有唯一的用户 ID。从而可以为其创建私有的 [数据库](/database/introduce) 和 [云存储](/storage/introduce.md) 数据,以及配合 [安全规则](/rule/introduce.md) 制定个性化的访问策略
- 未登录模式是纯粹的无登录态访问,该模式下的访问都不会进入用户的追踪统计
- 未登录的用户默认权限下无法使用任何 CloudBase 的服务和资源,而匿名登录在基础权限下也可以进行对应的资源读写,也可以结合安全规则实现更细粒度的管控
**匿名用户是否会过期?**
CloudBase 对匿名用户的有效期限策略是:每个设备同时只存在一个匿名用户,并且此用户永不过期。当然,如果用户手动清除了设备或浏览器的本地数据,那么匿名用户的数据便会被同步清除,再次调用 CloudBase 匿名登录 API 会产生一个新的匿名用户。
---
# storage Documentation
> 云开发存储服务,提供文件上传下载、CDN加速、安全规则、权限管理等云端存储解决方案
# 云存储/概述
> 当前文档链接: https://docs.cloudbase.net/storage/introduce
CloudBase 云存储提供稳定、安全、低成本、简单易用的云端存储服务,支持任意数量和形式的非结构化数据存储,如图片、文档、音频、视频、文件等。
您可以通过控制台、CloudBase CLI、HTTP API、SDK 等方式将文件上传到云端存储空间内,并对云端文件进行**文件管理**、**权限管理**和**缓存设置**等。
## 管理方式
CloudBase 云存储提供多种管理方式,您可以根据自己的需求选择最适合的方式:
| 管理方式 | 特点 | 适用场景 | 优势 |
| --- | --- | --- | --- |
| [**控制台管理**](/storage/manage) | 图形化界面,可配置权限、缓存设置 | 日常管理 | 上手快速,可视化操作 |
| [**SDK 管理**](/storage/sdk) | 可集成到应用中 | 应用程序集成 | 灵活性高,可定制化程度高,适合集成到应用中 |
| [**CLI 工具管理**](/cli-v1/storage) | 命令行操作,支持批量处理 | 自动化脚本、批量处理 | 高效批量操作,适合自动化部署 |
| [**cosBrowser**](/storage/cosbrowser) | 图形化界面,支持批量处理 | 频繁文件管理 | 结合了图形界面的便捷性和批量操作的高效性 |
根据您的具体需求和使用场景,选择合适的管理方式可以显著提高工作效率。例如,开发阶段可以使用 SDK,运维阶段可以使用 CLI 工具,而日常管理则可以使用控制台或 cosBrowser。
## 主要特性
### 默认支持 CDN 加速
云存储无需进行繁杂的配置,默认支持 CDN 加速,并提供免费的 CDN 域名。CDN 会将云存储的内容分发至最接近用户的节点,直接由服务节点快速响应,可以有效降低用户访问延迟。
### 身份验证集成
CloudBase 云存储与 CloudBase 用户身份验证无缝集成,您可以结合用户身份认证和安全规则对云存储里的文件设置访问权限,例如云存储的文件只对已登录用户公开或仅限使用微信登录的用户访问等。
### 高可扩展性
CloudBase 云存储可以与 CloudBase 其它多种服务配合使用,例如:
* 将文件的 fileID 存储到数据库;
* 配合 CloudBase 扩展能力使用,如图像安全审核、图像处理、图像标签、图像盲水印等。
---
# 云存储/文件管理/控制台管理文件
> 当前文档链接: https://docs.cloudbase.net/storage/manage
本文档将指导您如何通过云开发控制台对云存储进行全面管理,包括文件管理、权限设置和缓存配置等核心功能。
## 文件管理
进入 [云开发平台/云存储](https://tcb.cloud.tencent.com/dev?#/storage) 文件管理功能,您可以方便地查看和管理云存储中的所有文件:
1. 进入**文件管理**页面,您可以查看云存储空间中的所有文件列表
2. 单击文件名或「详情」按钮,即可查看文件的详细信息

## 权限设置
云存储权限设置允许您控制谁可以访问您的文件:
1. 访问 [云开发平台/云存储/权限设置](https://tcb.cloud.tencent.com/dev#/storage?tab=auth) 页面
2. 根据您的业务需求,选择合适的存储权限并保存
3. 您还可以通过 [自定义安全规则](/storage/security-rules) 实现更精细的权限控制
- 当设置「所有用户可读」时,**临时访问链接**则可永久访问
- 当设置非「所有用户可读」时,**访问链接** 可配置有效时间

## 缓存配置
### 缓存配置概述
云存储的缓存配置功能可以帮助您优化文件访问性能和节省带宽成本:
- 云存储内的文件默认启用 CDN 加速,您可以通过缓存配置控制 CDN 的过期规则
- 合理配置缓存时间可以有效提升命中率,降低回源率,节省带宽成本
- CDN 缓存资源的工作原理:
- **未过期资源**:用户请求直接从 CDN 节点返回,提高访问速度
- **已过期资源**:CDN 节点需要向源站重新获取内容,然后缓存并返回给用户
### 配置缓存规则
按照以下步骤配置云存储的缓存规则:
1. 访问 [缓存配置](https://tcb.cloud.tencent.com/dev?#/storage?tab=cache) 页面
2. 单击「编辑缓存配置」按钮,在默认配置基础上添加自定义缓存规则
> 默认配置为:所有文件缓存 2 分钟
3. 选择以下三种配置方式之一:
| 配置方式 | 说明 | 示例 |
| -------------- | -------------------------------------- | ---------------------- |
| **按文件类型** | 针对特定后缀名的文件设置缓存时间 | `.png`、`.jpg`、`.php` |
| **按文件夹** | 针对特定文件夹下的所有文件设置缓存时间 | `/images/`、`/static/` |
| **按文件** | 针对特定文件或文件模式设置缓存时间 | `/test/abc/*.jpg` |
4. 配置规则说明:
- 可填入多项配置,每项用分号(`;`)隔开
- 内容区分大小写
- 文件类型必须以`.`开头,文件夹必须以`/`开头
- 缓存时间设置为 0 时表示不缓存,所有请求将转发至源站
- 缓存时间最大值不能超过 365 天
5. 单击「保存」按钮完成配置,部署过程需等待约 5 分钟
:::tip 重要提示
配置部署过程中,若再次编辑缓存配置,将覆盖之前的配置。系统以最后一次部署结果为准。
:::
### 缓存策略优先级
在配置多条缓存策略时,需要注意以下优先级规则:
- 配置列表中**底部**的策略优先级高于**顶部**的策略
- 您可以通过拖动列表前的移动图标调整各策略的优先级
- 当多条策略同时匹配一个资源时,将以最后一次匹配的策略为准
#### 优先级示例
假设某域名配置了以下缓存规则:
| 优先级 | 目标 | 缓存时长 |
| ------- | --------------- | -------- |
| 5(最低) | 所有文件 | 2 分钟 |
| 4 | .php .jsp .aspx | 0 秒 |
| 3 | .jpg .png .gif | 300 秒 |
| 2 | /test/*.jpg | 400 秒 |
| 1(最高) | /test/abc.jpg | 200 秒 |
当访问资源 `www.test.com/test/abc.jpg` 时,匹配过程如下:
1. 匹配"所有文件"规则 → 命中,缓存时间为 2 分钟
2. 匹配".php .jsp .aspx"规则 → 未命中
3. 匹配".jpg .png .gif"规则 → 命中,缓存时间更新为 300 秒
4. 匹配"/test/*.jpg"规则 → 命中,缓存时间更新为 400 秒
5. 匹配"/test/abc.jpg"规则 → 命中,缓存时间最终确定为 200 秒
**最终结果**:该资源的缓存时间为 200 秒,因为最后一次匹配的规则优先级最高。
---
# 云存储/文件管理/SDK 管理文件
> 当前文档链接: https://docs.cloudbase.net/storage/sdk
CloudBase 云存储提供了完整的文件管理功能,包括上传、下载、删除、复制文件以及获取临时链接等操作。本文将详细介绍如何使用这些功能
| SDK 类型 | 适用平台 |
| ------------------------------------------------------------------------------------------------------------ | ------------ |
| [小程序 SDK](https://developers.weixin.qq.com/miniprogram/dev/wxcloudservice/wxcloud/guide/storage/api.html) | 小程序 |
| [JS SDK](/api-reference/webv2/storage) | Web 浏览器 |
| [Node SDK](/api-reference/server/node-sdk/storage) | Node.js 环境 |
| [HTTP API](http-api/storage/云存储) | 通用 |
## 上传文件
您可以上传任意数量、格式的文件至 CloudBase 云存储,也可以自定义文件、目录的路径和名字。
默认情况下,只有通过了 [CloudBase 身份验证](/authentication-v2/auth/introduce) 的用户才可以向云存储空间上传/删除文件,因此在用户端上传文件时需先进行登录认证。
:::tip 提示
您也可以使用 [自定义安全规则](/storage/security-rules),为云存储设置更宽松或更严格的读写权限。
:::
使用 SDK 可以向云存储空间上传文件,并返回该文件全局唯一标识 **fileID**。
### 2. 添加自定义 CDN
点击「添加自定义 CDN」,填入加速域名(您的自定义域名),选择 SSL 证书。如无证书可前往 [SSL 证书控制台](https://console.cloud.tencent.com/ssl) 申请。
### 3. 完成授权配置
点击「添加」完成授权配置。
### 4. 启用 CDN 配置
启用 CDN 配置。
### 5. 管理 CDN 域名
可在「自定义 CDN」中查看已授权的 CDN 域名,并可对其进行管理。
### 6. 访问云存储文件
通过自定义 CDN 域名访问云存储中的文件。
## 重要配置说明
:::tip 注意事项
### 鉴权配置
开启自定义 CDN 后,文件访问需经过云存储鉴权和 CDN 鉴权。若云存储权限为公有读,需前往 [CDN 控制台 - 域名管理 - 访问控制](https://console.cloud.tencent.com/cdn/domains),关闭 **防盗链** 和 **鉴权配置**。
### CNAME 解析
在 CDN 控制台完成自定义域名 CNAME 解析及等待部署完成。
:::
### 公有读场景配置示例
**场景说明:**

**关闭 CDN 防盗链和鉴权:**
## 常见问题
### CNAME 配置失败
CNAME 校验未通过时无法开启自定义 CDN,请参考 [配置 CNAME](https://cloud.tencent.com/document/product/228/3121) 完成域名解析。
### 计费说明
配置自定义 CDN 后,相关费用由腾讯云 CDN 收取,详见 [计费概述](https://cloud.tencent.com/document/product/228/2949)。
### 小程序域名配置
小程序使用自定义 CDN 需配置 request 合法域名:
1. 登录微信公众平台,进入小程序管理后台
2. 在「开发管理」中找到「服务器域名」
3. 在「request合法域名」中添加自定义CDN域名
4. 保存设置

---
# 云存储/高级功能/云调用扩展
> 当前文档链接: https://docs.cloudbase.net/storage/extension
云存储可以与云开发提供的云调用扩展服务联动,提供「存储+处理」一体化解决方案。您可以根据实际需求安装相应的扩展能力,实现图像处理、图像标签识别、图像盲水印、图像安全审核等功能。
## 处理方式
云开发提供两种图片处理方式:
- **URL 拼接参数**:适用于单次处理,直接在图片 URL 后拼接处理参数
- **持久化处理**:适用于批量处理或需要保存处理结果的场景,使用 SDK 进行处理
## 方式一:URL 拼接参数
此方式适用于单次图片处理,无需安装 SDK,直接在图片 URL 后拼接处理参数即可。处理后的链接可在小程序、Web 网页中使用,也可用于图床。
### 使用方法
在云存储图片的下载地址或临时链接后,使用 `&` 符号拼接处理参数。
**示例**:将图片等比例缩小到原来的 20%
```javascript
// 原始图片地址
https://786c-xly-xrlur-1300446086.tcb.qcloud.la/hehe.jpg?sign=b8ac757538940ead8eed4786449b4cd7&t=1591752049
// 拼接缩放参数后
https://786c-xly-xrlur-1300446086.tcb.qcloud.la/hehe.jpg?sign=b8ac757538940ead8eed4786449b4cd7&t=1591752049&imageMogr2/thumbnail/!20p
```
### 常用处理参数
```js
const downloadUrl = "https://786c-xly-xrlur-1300446086.tcb.qcloud.la/hehe.jpg?sign=xxx&t=xxx";
// 等比缩放
downloadUrl + "&imageMogr2/thumbnail/!20p" // 缩放到原图的 20%
downloadUrl + "&imageMogr2/thumbnail/!50px" // 宽度缩放到 50%,高度不变
downloadUrl + "&imageMogr2/thumbnail/!x50p" // 高度缩放到 50%,宽度不变
// 指定尺寸
downloadUrl + "&imageMogr2/thumbnail/640x" // 宽度 640px,高度等比缩放
downloadUrl + "&imageMogr2/thumbnail/x355" // 高度 355px,宽度等比缩放
downloadUrl + "&imageMogr2/thumbnail/640x355" // 限定最大宽高,等比缩放
downloadUrl + "&imageMogr2/thumbnail/640x355r" // 限定最小宽高,等比缩放
downloadUrl + "&imageMogr2/thumbnail/640x355!" // 强制缩放到指定尺寸(可能变形)
downloadUrl + "&imageMogr2/thumbnail/150000@" // 限制总像素数不超过 150000
// 裁剪效果
downloadUrl + "&imageMogr2/iradius/300" // 内切圆裁剪,半径 300px
downloadUrl + "&imageMogr2/rradius/100" // 圆角裁剪,半径 100px
// 其他效果
downloadUrl + "&imageMogr2/rotate/90" // 顺时针旋转 90 度
downloadUrl + "&imageMogr2/format/png" // 转换为 PNG 格式
downloadUrl + "&imageMogr2/blur/8x5" // 高斯模糊(半径 8,sigma 5)
// 获取图片信息
downloadUrl + "&imageInfo" // 返回图片基础信息(JSON 格式)
```
> 💡 **提示**:
> - 参数中 `p` 表示百分比,`x` 表示像素值
> - 多个处理参数可以叠加使用,用 `|` 符号分隔
> - 更多参数说明请参考 [图像处理文档](https://cloud.tencent.com/document/product/460/6924)
## 方式二:持久化图像处理
此方式适用于需要批量处理或保存处理结果的场景,通过 SDK 调用云调用扩展能力。
### 安装扩展 SDK
2. 编写 `index.js` 文件内容如下:
```js
exports.main = async function () {
return 'Hello World!';
};
```
3. 选择云函数目录,右键点击,选择「**创建并部署:云端安装依赖**」,即可完成云函数的**部署**