You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
canteen/uniCloud-aliyun/cloudfunctions/uni-sms-co/index.obj.js

375 lines
10 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// 云对象教程: https://uniapp.dcloud.net.cn/uniCloud/cloud-obj
// jsdoc语法提示教程https://ask.dcloud.net.cn/docs/#//ask.dcloud.net.cn/article/129
const createConfig = require('uni-config-center')
const buildTemplateData = require('./build-template-data')
const { parserDynamicField, checkIsStaticTemplate } = require('./utils')
const schemaNameAdapter = require('./schema-name-adapter')
const uniSmsCo = uniCloud.importObject('uni-sms-co')
const db = uniCloud.database()
const smsConfig = createConfig({
pluginId: 'uni-sms-co'
}).config()
function errCode(code) {
return 'uni-sms-co-' + code
}
const tableNames = {
template: 'opendb-sms-template',
task: 'opendb-sms-task',
log: 'opendb-sms-log'
}
module.exports = {
_before: async function () { // 通用预处理器
if (!smsConfig.smsKey || smsConfig.smsKey.length <= 20 || !smsConfig.smsSecret || smsConfig.smsSecret.length <= 20) {
throw new Error('请先配置smsKey和smsSecret')
}
this.tableNames = tableNames
/**
* 优化 schema 的命名规范,需要兼容 uni-admin@2.1.6 以下版本
* 如果是在uni-admin@2.1.6版本以上创建的项目可以将其注释
* */
await schemaNameAdapter.call(this)
},
_after: function (error, result) {
if (error) {
if (error instanceof Error) {
return {
errCode: 'error',
errMsg: error.message
}
}
if (error.errCode) {
return error
}
throw error
}
return result
},
/**
* 创建短信任务
* @param {Object} to
* @param {Boolean} to.all=false 全部用户发送
* @param {String} to.type=user to.all=true时用来区分发送类型
* @param {Array} to.receiver 用户ID's / 用户标签ID's
* @param {String} templateId 短信模板ID
* @param {Array} templateData 短信模板数据
* @param {String} options.taskName 任务名称
*/
async createSmsTask(to, templateId, templateData, options = {}) {
if (!templateId) {
return {
errCode: errCode('template-id-required'),
errMsg: '缺少templateId'
}
}
if (!to.all && (!to.receiver || to.receiver.length <= 0)) {
return {
errCode: errCode('send-users-is-null'),
errMsg: '请选择要发送的用户'
}
}
const clientInfo = this.getClientInfo()
const {data: templates} = await db.collection(this.tableNames.template).where({_id: templateId}).get()
if (templates.length <= 0) {
return {
errCode: errCode('template-not-found'),
errMsg: '短信模板不存在'
}
}
const [template] = templates
// 创建短信任务
const task = await db.collection(this.tableNames.task).add({
app_id: clientInfo.appId,
name: options.taskName,
template_id: templateId,
template_contnet: template.content,
vars: templateData,
to,
send_qty: 0,
success_qty: 0,
fail_qty: 0,
create_date: Date.now()
})
uniSmsCo.createUserSmsMessage(task.id)
return new Promise(resolve => setTimeout(() => resolve({
errCode: 0,
errMsg: '任务创建成功',
taskId: task.id
}), 300))
},
async createUserSmsMessage(taskId, execData = {}) {
const parallel = 100
let beforeId
const { data: tasks } = await db.collection(this.tableNames.task).where({ _id: taskId }).get()
if (tasks.length <= 0) {
return {
errCode: errCode('task-id-not-found'),
errMsg: '任务ID不存在'
}
}
const [task] = tasks
const query = {
mobile: db.command.exists(true)
}
// 指定用户发送
if (!task.to.all && task.to.type === 'user') {
let index = 0
if (execData.beforeId) {
const i = task.to.receiver.findIndex(id => id === execData.beforeId)
index = i !== -1 ? i + 1 : 0
}
const receiver = task.to.receiver.slice(index, index + parallel)
query._id = db.command.in(receiver)
beforeId = receiver[receiver.length - 1]
}
// 指定用户标签
if (task.to.type === 'userTags') {
query.tags = db.command.in(task.to.receiver)
}
// 全部用户
if (task.to.all && execData.beforeId) {
query._id = db.command.gt(execData.beforeId)
}
// 动态数据仅支持uni-id-users表字段
const dynamicField = parserDynamicField(task.vars)
const userFields = dynamicField['uni-id-users'] ? dynamicField['uni-id-users'].reduce((res, field) => {
res[field] = true
return res
}, {}): {}
const { data: users } = await db.collection('uni-id-users')
.where(query)
.field({
mobile: true,
...userFields
})
.limit(parallel)
.orderBy('_id', 'asc')
.get()
if (users.length <= 0) {
// 更新要发送的短信数量
const count = await db.collection(this.tableNames.log).where({ task_id: taskId }).count()
await db.collection(this.tableNames.task).where({ _id: taskId }).update({
send_qty: count.total
})
// 开始发送
uniSmsCo.sendSms(taskId)
return new Promise(resolve => setTimeout(() => resolve({
errCode: 0,
errMsg: '创建完成'
}), 500))
}
if (!beforeId) {
beforeId = users[users.length - 1]._id
}
let docs = []
for (const user of users) {
const varData = await buildTemplateData(task.vars, user)
docs.push({
uid: user._id,
task_id: taskId,
mobile: user.mobile,
var_data: varData,
status: 0,
create_date: Date.now()
})
}
await db.collection(this.tableNames.log).add(docs)
uniSmsCo.createUserSmsMessage(taskId, { beforeId })
return new Promise(resolve => setTimeout(() => resolve(), 500))
},
async sendSms(taskId) {
const { data: tasks } = await db.collection(this.tableNames.task).where({ _id: taskId }).get()
if (tasks.length <= 0) {
console.warn(`task [${taskId}] not found`)
return
}
const [task] = tasks
const isStaticTemplate = !task.vars.length
let sendData = {
appId: task.app_id,
smsKey: smsConfig.smsKey,
smsSecret: smsConfig.smsSecret,
templateId: task.template_id,
data: {}
}
const { data: records } = await db.collection(this.tableNames.log)
.where({ task_id: taskId, status: 0 })
.limit(isStaticTemplate ? 50 : 1)
.field({ mobile: true, var_data: true })
.get()
if (records.length <= 0) {
return {
errCode: 0,
errMsg: '发送完成'
}
}
if (isStaticTemplate) {
sendData.phoneList = records.reduce((res, user) => {
res.push(user.mobile)
return res
}, [])
} else {
const [record] = records
sendData.phone = record.mobile
sendData.data = record.var_data
}
try {
// await sendSms(sendData)
await uniCloud.sendSms(sendData)
// 修改发送状态为已发送
await db.collection(this.tableNames.log).where({
_id: db.command.in(records.map(record => record._id))
}).update({
status: 1,
send_date: Date.now()
})
// 更新任务的短信成功数
await db.collection(this.tableNames.task).where({ _id: taskId })
.update({
success_qty: db.command.inc(records.length)
})
} catch (e) {
console.error('[sendSms Fail]', e)
// 修改发送状态为发送失败
await db.collection(this.tableNames.log).where({
_id: db.command.in(records.map(record => record._id))
}).update({
status: 2,
reason: e.errMsg || '未知原因',
send_date: Date.now()
})
// 更新任务的短信失败数
await db.collection(this.tableNames.task).where({ _id: taskId })
.update({
fail_qty: db.command.inc(records.length)
})
}
uniSmsCo.sendSms(taskId)
return new Promise(resolve => setTimeout(() => resolve(), 500))
},
async template() {
const {data: templates = []} = await db.collection(this.tableNames.template).get()
return templates
},
async task (id) {
const {data: tasks} = await db.collection(this.tableNames.task).where({_id: id}).get()
if (tasks.length <= 0) {
return null
}
return tasks[0]
},
async updateTemplates (templates) {
if (templates.length <= 0) {
return {
errCode: errCode('template-is-null'),
errMsg: '缺少模板信息'
}
}
let group = []
for (const template of templates) {
group.push(
db.collection(this.tableNames.template).doc(String(template.templateId)).set({
name: template.templateName,
content: template.templateContent,
type: template.templateType,
sign: template.templateSign
})
)
}
await Promise.all(group)
return {
errCode: 0,
errMsg: '更新成功'
}
},
async preview (to, templateId, templateData) {
const count = 1
let query = {
mobile: db.command.exists(true)
}
// 指定用户发送
if (!to.all && to.type === 'user') {
const receiver = to.receiver.slice(0, 10)
query._id = db.command.in(receiver)
}
// 指定用户标签
if (to.type === 'userTags') {
query.tags = db.command.in(to.receiver)
}
const {data: users} = await db.collection('uni-id-users').where(query).limit(count).get()
console.log({users, query})
if (users.length <= 0) {
return {
errCode: errCode('users-is-null'),
errMsg: '请添加要发送的用户'
}
}
const {data: templates} = await db.collection(this.tableNames.template).where({_id: templateId}).get()
if (templates.length <= 0) {
return {
errCode: errCode('template-not-found'),
errMsg: '模板不存在'
}
}
const [template] = templates
let docs = []
for (const user of users) {
const varData = buildTemplateData(templateData, user)
const content = template.content.replace(/\$\{(.*?)\}/g, ($1, param) => varData[param] || $1)
docs.push(`${template.sign}${content}`)
}
return {
errCode: 0,
errMsg: '',
list: docs
}
}
}