第一次接触Uni-Push 2.0时,我被它的全平台推送能力惊艳到了。相比传统推送方案需要针对Android、iOS分别对接厂商通道的繁琐,Uni-Push 2.0只需要一套代码就能搞定所有平台。但真正让我兴奋的是发现它能与uniCloud云函数深度结合,这意味着我们可以把推送能力封装成API,让企业自有系统也能调用。
开通服务其实很简单,在DCloud开发者中心找到uniPush 2.0页面,按指引完成应用注册就行。不过有个细节要注意:如果你需要应用未启动时也能接收推送(即离线推送),就得配置各手机厂商的推送通道。我遇到过不少开发者在这里踩坑——厂商通道要求应用必须上架对应应用商店才能开通。所以如果你的App还处于开发测试阶段,建议先只用在线推送功能。
在manifest.json中配置时,新手常会忽略权限声明。除了勾选uniPush 2.0模块外,记得在"App权限配置"中添加以下权限:
json复制{
"android": {
"permissions": [
"<uses-permission android:name=\"android.permission.INTERNET\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>"
]
}
}
在App.vue中初始化推送服务时,我建议采用更健壮的写法。很多教程只展示基础用法,实际项目中我们需要考虑各种异常情况:
javascript复制onLaunch() {
this.initPushService()
},
methods: {
async initPushService() {
try {
const { cid } = await uni.getPushClientId()
console.log('客户端推送标识:', cid)
// 将cid发送到服务器保存
await this.saveClientIdToServer(cid)
uni.onPushMessage(res => {
console.log("收到推送消息:", res)
// 处理点击通知栏跳转逻辑
if (res.data.payload && res.data.payload.page) {
uni.navigateTo({ url: res.data.payload.page })
}
})
} catch (err) {
console.error('推送初始化失败:', err)
// 重试逻辑
setTimeout(() => this.initPushService(), 5000)
}
}
}
这里有几个实战经验值得分享:
调试阶段必须使用自定义基座,这是很多新手容易忽略的点。我建议在HBuilderX中直接使用"运行->制作自定义调试基座",选择开发证书即可。有个小技巧:可以在基座打包时勾选"调试模式",这样控制台会输出更详细的推送日志。
原始云函数示例太过简单,实际项目中我们需要考虑更多边界情况。下面是我优化后的云函数代码:
javascript复制'use strict';
const uniPush = uniCloud.getPushManager({ appId: "__UNI_XXXXXX" }) // 替换为你的应用ID
exports.main = async (event) => {
// 参数校验
if (!event.body) {
return { code: 400, message: '请求体不能为空' }
}
let params;
try {
params = JSON.parse(event.body)
} catch (e) {
return { code: 400, message: 'JSON解析失败' }
}
// 必填字段检查
const { cids, title, content, request_id } = params
if (!title || !content) {
return { code: 400, message: '标题和内容不能为空' }
}
// 防止全推内容重复
if (!cids || cids.length === 0) {
const cacheKey = `push:${content}`
const cache = await uniCloud.redis().get(cacheKey)
if (cache) {
return { code: 400, message: '全推内容10分钟内不能重复' }
}
await uniCloud.redis().setex(cacheKey, 600, '1')
}
try {
const res = await uniPush.sendMessage({
push_clientid: cids || [], // 支持单设备或多设备
title,
content,
payload: params.payload || {},
request_id: request_id || Date.now().toString() // 默认使用时间戳
})
return { code: 0, data: res }
} catch (err) {
console.error('推送失败:', err)
return { code: 500, message: err.message || '推送服务异常' }
}
}
这个版本增加了以下关键改进:
云函数URL化是个非常实用的功能,它允许我们将云函数暴露为HTTP接口。在uniCloud控制台找到你的云函数,进入"URL化"标签页,建议设置:
配置完成后你会得到一个类似这样的URL:
code复制https://fc-mp-xxxxxx-xxxxxx.next.bspapp.com/push
在企业后台系统调用时,我推荐使用axios这样的HTTP库。下面是一个Node.js调用示例:
javascript复制const axios = require('axios')
async function sendPushNotification(devices, title, content, extraData) {
const payload = {
cids: Array.isArray(devices) ? devices : [devices],
title,
content,
payload: extraData,
request_id: `${Date.now()}_${Math.random().toString(36).substr(2, 8)}`
}
try {
const response = await axios.post('你的URL化地址', payload, {
headers: { 'Content-Type': 'application/json' }
})
return response.data
} catch (error) {
console.error('推送调用失败:', error.response?.data || error.message)
throw error
}
}
在实际项目中,我们还需要考虑:
在大量设备推送场景下,直接调用API可能会遇到性能瓶颈。我设计了一个分片推送的方案:
javascript复制async function batchPush(deviceList, title, content) {
const batchSize = 400 // 留有余量
const chunks = []
for (let i = 0; i < deviceList.length; i += batchSize) {
chunks.push(deviceList.slice(i, i + batchSize))
}
const results = []
for (const chunk of chunks) {
try {
const res = await sendPushNotification(chunk, title, content)
results.push({ success: true, data: res })
} catch (err) {
results.push({ success: false, error: err.message })
}
// 适当间隔,避免触发限流
await new Promise(resolve => setTimeout(resolve, 200))
}
return results
}
另一个常见问题是request_id重复导致消息丢失。我建议采用以下生成策略:
code复制时间戳(13位) + 随机字符串(8位) + 业务前缀(可选)
例如:1689324567890_abc123de_orderNotify
对于全推限制,除了内容不能重复外,还要注意:
成熟的推送系统需要完善的监控机制。我们可以通过以下方式增强:
下面是一个增强版的推送云函数示例:
javascript复制exports.main = async (event) => {
// ...原有参数校验逻辑...
const startTime = Date.now()
let logId = null
try {
// 记录推送日志
const logRes = await uniCloud.database().collection('push_logs').add({
type: cids ? 'targeted' : 'broadcast',
device_count: cids ? cids.length : 0,
title,
content,
status: 'pending',
created_at: new Date()
})
logId = logRes.id
const pushRes = await uniPush.sendMessage({
push_clientid: cids || [],
title,
content,
payload: { ...params.payload, log_id: logId },
request_id: params.request_id || `${logId}_${startTime}`
})
// 更新日志状态
await uniCloud.database().collection('push_logs').doc(logId).update({
status: 'success',
task_id: pushRes.taskId,
duration: Date.now() - startTime
})
return {
code: 0,
data: {
...pushRes,
log_id: logId
}
}
} catch (err) {
if (logId) {
await uniCloud.database().collection('push_logs').doc(logId).update({
status: 'failed',
error: err.message,
duration: Date.now() - startTime
})
}
throw err
}
}
这个版本新增了:
企业级推送系统必须考虑安全性。我总结了几个关键防护措施:
javascript复制// 在云函数开头添加
const AUTH_TOKEN = 'your_secret_token'
if (event.headers['x-auth-token'] !== AUTH_TOKEN) {
return { code: 403, message: '未授权访问' }
}
javascript复制function sanitizeInput(content) {
const MAX_LENGTH = 100
return content
.replace(/<[^>]*>?/gm, '') // 去除HTML标签
.substring(0, MAX_LENGTH) // 限制长度
}
javascript复制const rateLimit = require('uni-cloud-rate-limit')
exports.main = rateLimit({
maxRequests: 100, // 每分钟最大请求数
handler: async (event) => {
// 原有业务逻辑
}
})
javascript复制const blockedWords = ['敏感词1', '敏感词2']
function checkContent(content) {
return blockedWords.some(word => content.includes(word))
}
在实际项目中,我建议采用分层架构:
遇到推送问题时,可以按照以下步骤排查:
我常用的调试命令(针对云函数):
bash复制# 查看最近错误日志
uni-cloud logs --tail --function yourFunctionName --level error
# 本地调试云函数
uni-cloud invoke local yourFunctionName --data '{"body":"{\\"title\\":\\"test\\",\\"content\\":\\"test\\"}"}'
对于复杂的推送场景,建议在测试环境先验证以下case:
基础推送功能实现后,可以考虑以下扩展:
javascript复制// 在云函数中使用setTimeout或对接uniCloud定时任务
exports.main = async (event) => {
if (event.triggerType === 'timer') {
// 执行定时推送逻辑
}
}
javascript复制// 先查询符合条件设备
const targetDevices = await uniCloud.database()
.collection('user_devices')
.where('tags.includes("vip")')
.field(['cid'])
.get()
// 然后执行推送
javascript复制// 基于push_logs集合聚合分析
const analysis = await uniCloud.database()
.collection('push_logs')
.aggregate()
.group({
_id: '$status',
count: { $sum: 1 }
})
.end()
javascript复制// 随机选择部分用户推送不同内容
function getRandomSample(devices, percent) {
return devices.filter(() => Math.random() < percent/100)
}
这些扩展功能可以根据实际业务需求灵活组合。比如我们之前做促销活动时,就实现了先筛选出最近7天活跃用户,然后随机分成两组推送不同优惠内容的方案,最后通过统计点击率来评估哪个版本更有效。