| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375 |
- // 云对象教程: 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
- }
- }
- }
|