feat: 提供notification发送和发送失败处理的注入点
This commit is contained in:
parent
5ff8f1ed53
commit
78eb039a04
|
|
@ -1,11 +1,10 @@
|
|||
import { String } from 'oak-domain/lib/types/DataType';
|
||||
import { EntityShape } from 'oak-domain/lib/types/Entity';
|
||||
import { Channel } from '../types/Message';
|
||||
import { Schema as Application } from './Application';
|
||||
import { Schema as MessageSystem } from './MessageSystem';
|
||||
import { EntityDesc } from 'oak-domain/lib/types/EntityDesc';
|
||||
export interface Schema extends EntityShape {
|
||||
channel: Channel;
|
||||
channel: String<32>;
|
||||
application?: Application;
|
||||
data?: Object;
|
||||
messageSystem: MessageSystem;
|
||||
|
|
@ -18,6 +17,5 @@ export type IState = 'sending' | 'success' | 'failure';
|
|||
type Action = IAction;
|
||||
export declare const entityDesc: EntityDesc<Schema, Action, '', {
|
||||
iState: IState;
|
||||
channel: Schema['channel'];
|
||||
}>;
|
||||
export {};
|
||||
|
|
|
|||
|
|
@ -30,14 +30,14 @@ export const entityDesc = {
|
|||
success: '发送成功',
|
||||
failure: '发送失败',
|
||||
},
|
||||
channel: {
|
||||
wechatPublic: '公众号',
|
||||
jPush: '极光推送',
|
||||
jim: '极光消息',
|
||||
wechatMp: '小程序',
|
||||
sms: '短信',
|
||||
email: '邮箱',
|
||||
}
|
||||
// channel: {
|
||||
// wechatPublic: '公众号',
|
||||
// jPush: '极光推送',
|
||||
// jim: '极光消息',
|
||||
// wechatMp: '小程序',
|
||||
// sms: '短信',
|
||||
// email: '邮箱',
|
||||
// }
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -52,14 +52,14 @@ export const entityDesc = {
|
|||
success: '#008000',
|
||||
failure: '#9A9A9A',
|
||||
},
|
||||
channel: {
|
||||
wechatMp: '#008000',
|
||||
jPush: '#0000FF',
|
||||
jim: '#0000FF',
|
||||
wechatPublic: '#008000',
|
||||
sms: '#000000',
|
||||
email: '#000000',
|
||||
},
|
||||
// channel: {
|
||||
// wechatMp: '#008000',
|
||||
// jPush: '#0000FF',
|
||||
// jim: '#0000FF',
|
||||
// wechatPublic: '#008000',
|
||||
// sms: '#000000',
|
||||
// email: '#000000',
|
||||
// },
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@ export const desc = {
|
|||
attributes: {
|
||||
channel: {
|
||||
notNull: true,
|
||||
type: "enum",
|
||||
enumeration: ["wechatPublic", "jPush", "jim", "wechatMp", "sms", "email"]
|
||||
type: "varchar",
|
||||
params: {
|
||||
length: 32
|
||||
}
|
||||
},
|
||||
applicationId: {
|
||||
type: "ref",
|
||||
|
|
|
|||
|
|
@ -9,13 +9,13 @@ export const style = {
|
|||
success: '#008000',
|
||||
failure: '#9A9A9A',
|
||||
},
|
||||
channel: {
|
||||
wechatMp: '#008000',
|
||||
jPush: '#0000FF',
|
||||
jim: '#0000FF',
|
||||
wechatPublic: '#008000',
|
||||
sms: '#000000',
|
||||
email: '#000000',
|
||||
},
|
||||
// channel: {
|
||||
// wechatMp: '#008000',
|
||||
// jPush: '#0000FF',
|
||||
// jim: '#0000FF',
|
||||
// wechatPublic: '#008000',
|
||||
// sms: '#000000',
|
||||
// email: '#000000',
|
||||
// },
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,10 +2,9 @@ import { ForeignKey } from "oak-domain/lib/types/DataType";
|
|||
import { Q_DateValue, Q_NumberValue, Q_StringValue, Q_EnumValue, NodeId, ExprOp, ExpressionKey } from "oak-domain/lib/types/Demand";
|
||||
import { MakeAction as OakMakeAction, EntityShape } from "oak-domain/lib/types/Entity";
|
||||
import { Action, ParticularAction, IState } from "./Action";
|
||||
import { Channel } from "../../types/Message";
|
||||
import { String } from "oak-domain/lib/types/DataType";
|
||||
export type OpSchema = EntityShape & {
|
||||
channel: Channel;
|
||||
channel: String<32>;
|
||||
applicationId?: ForeignKey<"application"> | null;
|
||||
data?: Object | null;
|
||||
messageSystemId: ForeignKey<"messageSystem">;
|
||||
|
|
@ -22,7 +21,7 @@ export type OpFilter = {
|
|||
$$createAt$$: Q_DateValue;
|
||||
$$seq$$: Q_NumberValue;
|
||||
$$updateAt$$: Q_DateValue;
|
||||
channel: Q_EnumValue<Channel>;
|
||||
channel: Q_StringValue;
|
||||
applicationId: Q_StringValue;
|
||||
data: Object;
|
||||
messageSystemId: Q_StringValue;
|
||||
|
|
|
|||
|
|
@ -19,14 +19,6 @@
|
|||
"sending": "发送中",
|
||||
"success": "发送成功",
|
||||
"failure": "发送失败"
|
||||
},
|
||||
"channel": {
|
||||
"wechatPublic": "公众号",
|
||||
"jPush": "极光推送",
|
||||
"jim": "极光消息",
|
||||
"wechatMp": "小程序",
|
||||
"sms": "短信",
|
||||
"email": "邮箱"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ export const desc = {
|
|||
qrCodeType: {
|
||||
notNull: true,
|
||||
type: "enum",
|
||||
enumeration: ["wechatPublic", "wechatMpDomainUrl", "wechatMpWxaCode", "wechatPublicForMp", "webForWechatPublic"]
|
||||
enumeration: ["wechatMpDomainUrl", "wechatMpWxaCode", "wechatPublic", "wechatPublicForMp", "webForWechatPublic"]
|
||||
},
|
||||
expiresAt: {
|
||||
type: "datetime"
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ export const desc = {
|
|||
qrCodeType: {
|
||||
notNull: true,
|
||||
type: "enum",
|
||||
enumeration: ["wechatPublic", "wechatMpDomainUrl", "wechatMpWxaCode", "wechatPublicForMp", "webForWechatPublic"]
|
||||
enumeration: ["wechatMpDomainUrl", "wechatMpWxaCode", "wechatPublic", "wechatPublicForMp", "webForWechatPublic"]
|
||||
},
|
||||
expiresAt: {
|
||||
type: "datetime"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export const desc = {
|
|||
type: {
|
||||
notNull: true,
|
||||
type: "enum",
|
||||
enumeration: ["wechatPublic", "wechatMpDomainUrl", "wechatMpWxaCode", "wechatPublicForMp", "webForWechatPublic"]
|
||||
enumeration: ["wechatMpDomainUrl", "wechatMpWxaCode", "wechatPublic", "wechatPublicForMp", "webForWechatPublic"]
|
||||
},
|
||||
allowShare: {
|
||||
notNull: true,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,32 @@ export {
|
|||
*/
|
||||
registerMessageType, } from './aspects/template';
|
||||
export { registerWeChatPublicEventCallback, } from './endpoints/wechat';
|
||||
export { registerMessageNotificationConverters, registerMessageHandler, } from './utils/message';
|
||||
export {
|
||||
/**
|
||||
* 注册消息通知转换器
|
||||
* 用于将消息数据转换为特定渠道所需的格式
|
||||
* 例如: 将message转换为微信小程序、公众号、短信、邮件所需的数据格式
|
||||
*/
|
||||
registerMessageNotificationConverters,
|
||||
/**
|
||||
* 注册消息渠道处理器
|
||||
* 用于处理特定渠道的消息创建逻辑
|
||||
* 例如: 处理微信小程序、公众号、短信、邮件等渠道的消息生成
|
||||
*/
|
||||
registerMessageHandler, } from './utils/message';
|
||||
export {
|
||||
/**
|
||||
* 注册通知渠道处理器
|
||||
* 用于处理特定渠道的通知发送逻辑
|
||||
* 例如: 实际发送微信小程序、公众号、短信、邮件等渠道的通知
|
||||
*/
|
||||
registerNotificationHandler,
|
||||
/**
|
||||
* 注册通知失败处理器
|
||||
* 用于在所有通知渠道都失败后执行自定义的补救逻辑
|
||||
* 可以注册多个处理器,它们会依次执行
|
||||
* 例如: 在其他渠道失败后自动发送短信通知
|
||||
*/
|
||||
registerNotificationFailureHandler, } from './utils/notification';
|
||||
export { registSms, } from './utils/sms';
|
||||
export { registerCosBackend, } from './utils/cos/index.backend';
|
||||
|
|
|
|||
|
|
@ -11,8 +11,32 @@ export {
|
|||
// 注册微信事件回调处理器endpoint
|
||||
registerWeChatPublicEventCallback, } from './endpoints/wechat';
|
||||
export {
|
||||
// 注册消息通知转换器trigger
|
||||
registerMessageNotificationConverters, registerMessageHandler, } from './utils/message';
|
||||
/**
|
||||
* 注册消息通知转换器
|
||||
* 用于将消息数据转换为特定渠道所需的格式
|
||||
* 例如: 将message转换为微信小程序、公众号、短信、邮件所需的数据格式
|
||||
*/
|
||||
registerMessageNotificationConverters,
|
||||
/**
|
||||
* 注册消息渠道处理器
|
||||
* 用于处理特定渠道的消息创建逻辑
|
||||
* 例如: 处理微信小程序、公众号、短信、邮件等渠道的消息生成
|
||||
*/
|
||||
registerMessageHandler, } from './utils/message';
|
||||
export {
|
||||
/**
|
||||
* 注册通知渠道处理器
|
||||
* 用于处理特定渠道的通知发送逻辑
|
||||
* 例如: 实际发送微信小程序、公众号、短信、邮件等渠道的通知
|
||||
*/
|
||||
registerNotificationHandler,
|
||||
/**
|
||||
* 注册通知失败处理器
|
||||
* 用于在所有通知渠道都失败后执行自定义的补救逻辑
|
||||
* 可以注册多个处理器,它们会依次执行
|
||||
* 例如: 在其他渠道失败后自动发送短信通知
|
||||
*/
|
||||
registerNotificationFailureHandler, } from './utils/notification';
|
||||
export {
|
||||
// 注册短信服务商实现
|
||||
registSms, } from './utils/sms';
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
declare const _default: (import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "message", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "application", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "address", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "user", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "userEntityGrant", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatQrCode", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "notification", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatLogin", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "articleMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "article", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "parasite", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "extraFile", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "sessionMessage", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatPublicTag", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMpJump", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "system", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "passport", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthApplication", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthProvider", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthUser", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthUserAuthorization", import("../context/BackendRuntimeContext").BackendRuntimeContext<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "mobile", import("..").BRC<import("../oak-app-domain").EntityDict>>)[];
|
||||
declare const _default: (import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "address", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "application", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "article", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "articleMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "extraFile", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "user", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "userEntityGrant", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatQrCode", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "message", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "notification", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatLogin", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "parasite", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "sessionMessage", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatPublicTag", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMpJump", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "system", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "passport", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthApplication", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthProvider", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthUser", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthUserAuthorization", import("../context/BackendRuntimeContext").BackendRuntimeContext<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "mobile", import("..").BRC<import("../oak-app-domain").EntityDict>>)[];
|
||||
export default _default;
|
||||
|
|
|
|||
|
|
@ -1,25 +1,5 @@
|
|||
import { Trigger } from 'oak-domain/lib/types/Trigger';
|
||||
import { EntityDict } from '../oak-app-domain/EntityDict';
|
||||
import { BRC } from '../types/RuntimeCxt';
|
||||
import { BackendRuntimeContext } from '../context/BackendRuntimeContext';
|
||||
import { Router } from '../entities/Message';
|
||||
export declare function tryMakeSmsNotification(message: {
|
||||
userId?: string;
|
||||
type?: string;
|
||||
entity?: string;
|
||||
router?: Router | null;
|
||||
entityId?: string;
|
||||
}, context: BackendRuntimeContext<EntityDict>): Promise<any>;
|
||||
export declare function tryMakeEmailNotification(message: {
|
||||
userId?: string;
|
||||
type?: string;
|
||||
entity?: string;
|
||||
router?: Router | null;
|
||||
entityId?: string;
|
||||
}, context: BackendRuntimeContext<EntityDict>): Promise<{
|
||||
id: string;
|
||||
data: import("../types/Email").EmailOptions;
|
||||
channel: string;
|
||||
} | undefined>;
|
||||
declare const triggers: Trigger<EntityDict, 'message', BRC<EntityDict>>[];
|
||||
export default triggers;
|
||||
|
|
|
|||
|
|
@ -1,55 +1,12 @@
|
|||
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
||||
import { assert } from 'oak-domain/lib/utils/assert';
|
||||
import { uniqBy } from 'oak-domain/lib/utils/lodash';
|
||||
import { ConverterDict, getMessageHandler } from '../utils/message';
|
||||
import { getMessageHandler } from '../utils/message';
|
||||
const InitialChannelByWeightMatrix = {
|
||||
high: ['wechatMp', 'wechatPublic', 'sms', 'email'],
|
||||
medium: ['wechatMp', 'wechatPublic', 'email'],
|
||||
low: ['wechatMp', 'wechatPublic', 'email'],
|
||||
};
|
||||
export async function tryMakeSmsNotification(message, context) {
|
||||
const { userId, type, entity, entityId, router } = message;
|
||||
assert(userId);
|
||||
const [mobile] = await context.select('mobile', {
|
||||
data: {
|
||||
id: 1,
|
||||
mobile: 1,
|
||||
},
|
||||
filter: {
|
||||
userId,
|
||||
},
|
||||
indexFrom: 0,
|
||||
count: 1,
|
||||
}, { dontCollect: true });
|
||||
if (mobile) {
|
||||
const converter = ConverterDict[type] && ConverterDict[type].toSms;
|
||||
if (converter) {
|
||||
const dispersedData = await converter(message, context);
|
||||
if (dispersedData) {
|
||||
return {
|
||||
id: await generateNewIdAsync(),
|
||||
data: dispersedData,
|
||||
channel: 'sms',
|
||||
data1: mobile,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
export async function tryMakeEmailNotification(message, context) {
|
||||
const { userId, type, entity, entityId, router } = message;
|
||||
const converter = ConverterDict[type] && ConverterDict[type].toEmail;
|
||||
if (converter) {
|
||||
const dispersedData = await converter(message, context);
|
||||
if (dispersedData) {
|
||||
return {
|
||||
id: await generateNewIdAsync(),
|
||||
data: dispersedData,
|
||||
channel: 'email',
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
async function createNotification(message, context) {
|
||||
const { restriction, userId, weight, type, entity, entityId, platformId, channels } = message;
|
||||
assert(userId);
|
||||
|
|
|
|||
|
|
@ -1,264 +1,12 @@
|
|||
import { assert } from 'oak-domain/lib/utils/assert';
|
||||
import WechatSDK from 'oak-external-sdk/lib/WechatSDK';
|
||||
import { composeUrl } from 'oak-domain/lib/utils/domain';
|
||||
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
||||
import { sendSms } from '../utils/sms';
|
||||
import { sendEmail } from '../utils/email';
|
||||
import { tryMakeSmsNotification } from './message';
|
||||
import { composeDomainUrl } from '../utils/domain';
|
||||
import { tryMakeSmsNotification } from '../utils/message/sms';
|
||||
import { getNotificationHandler, getNotificationFailureHandlers } from '../utils/notification';
|
||||
async function sendNotification(notification, context) {
|
||||
const { data, templateId, channel, messageSystemId, data1, id } = notification;
|
||||
const [messageSystem] = await context.select('messageSystem', {
|
||||
data: {
|
||||
id: 1,
|
||||
messageId: 1,
|
||||
message: {
|
||||
id: 1,
|
||||
userId: 1,
|
||||
router: 1,
|
||||
type: 1,
|
||||
},
|
||||
system: {
|
||||
id: 1,
|
||||
application$system: {
|
||||
$entity: 'application',
|
||||
data: {
|
||||
id: 1,
|
||||
type: 1,
|
||||
config: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
id: messageSystemId,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
const { system, message } = messageSystem;
|
||||
const { router, userId, type } = message;
|
||||
const { application$system: applications, config } = system;
|
||||
switch (channel) {
|
||||
case 'wechatMp': {
|
||||
const app = applications.find(ele => ele.type === 'wechatMp');
|
||||
const { config } = app;
|
||||
const { appId, appSecret } = config;
|
||||
const instance = WechatSDK.getInstance(appId, 'wechatMp', appSecret);
|
||||
let page;
|
||||
if (router) {
|
||||
const pathname = router.pathname;
|
||||
const url = pathname.startsWith('/')
|
||||
? `pages${pathname}/index`
|
||||
: `pages/${pathname}/index`;
|
||||
page = composeUrl(url, Object.assign({}, router.props, router.state));
|
||||
}
|
||||
// 根据当前环境决定消息推哪个版本
|
||||
const StateDict = {
|
||||
'development': 'developer',
|
||||
'staging': 'trial',
|
||||
'production': 'former',
|
||||
};
|
||||
try {
|
||||
await instance.sendSubscribedMessage({
|
||||
templateId: templateId,
|
||||
data: data,
|
||||
openId: data1.openId, // 在notification创建时就赋值了
|
||||
page,
|
||||
state: StateDict[process.env.NODE_ENV],
|
||||
});
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'succeed',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
return 1;
|
||||
}
|
||||
catch (err) {
|
||||
console.warn('发微信小程序消息失败', err);
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'fail',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
case 'wechatPublic': {
|
||||
const app = applications.find(ele => ele.type === 'wechatPublic');
|
||||
const { config, id: applicationId } = app;
|
||||
const { appId, appSecret } = config;
|
||||
const [domain] = await context.select('domain', {
|
||||
data: {
|
||||
id: 1,
|
||||
url: 1,
|
||||
apiPath: 1,
|
||||
protocol: 1,
|
||||
port: 1,
|
||||
},
|
||||
filter: {
|
||||
system: {
|
||||
application$system: {
|
||||
id: applicationId,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, { dontCollect: true });
|
||||
const instance = WechatSDK.getInstance(appId, 'wechatPublic', appSecret);
|
||||
const { openId, wechatMpAppId } = data1;
|
||||
let page;
|
||||
// message 用户不需要跳转页面
|
||||
if (router) {
|
||||
const pathname = router.pathname;
|
||||
if (wechatMpAppId) {
|
||||
const url = pathname.startsWith('/')
|
||||
? `pages${pathname}/index`
|
||||
: `pages/${pathname}/index`;
|
||||
page = composeUrl(url, Object.assign({}, router.props, router.state));
|
||||
}
|
||||
else {
|
||||
const url = composeDomainUrl(domain, pathname);
|
||||
page = composeUrl(url, Object.assign({}, router.props, router.state));
|
||||
}
|
||||
}
|
||||
try {
|
||||
await instance.sendTemplateMessage({
|
||||
openId,
|
||||
templateId: templateId,
|
||||
url: !wechatMpAppId ? page : undefined,
|
||||
data: data,
|
||||
miniProgram: wechatMpAppId
|
||||
? {
|
||||
appid: wechatMpAppId,
|
||||
pagepath: page,
|
||||
}
|
||||
: undefined,
|
||||
clientMsgId: id,
|
||||
});
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'succeed',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
return 1;
|
||||
}
|
||||
catch (err) {
|
||||
console.warn('发微信公众号消息失败', err);
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'fail',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
case 'email': {
|
||||
try {
|
||||
const result = await sendEmail(data, context);
|
||||
if (result?.success) {
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'succeed',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
},
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
else {
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'fail',
|
||||
data: {
|
||||
data2: {
|
||||
res: result?.error,
|
||||
},
|
||||
},
|
||||
filter: {
|
||||
id,
|
||||
},
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
catch (err) {
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'fail',
|
||||
data: {
|
||||
data2: {
|
||||
res: err?.message,
|
||||
},
|
||||
},
|
||||
filter: {
|
||||
id,
|
||||
},
|
||||
}, { dontCollect: true });
|
||||
console.warn('发邮件消息失败', err);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
default: {
|
||||
assert(channel === 'sms');
|
||||
try {
|
||||
const result = await sendSms({
|
||||
messageType: type,
|
||||
templateParam: data.params,
|
||||
mobile: data1.mobile,
|
||||
}, context);
|
||||
if (result?.success === true) {
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'succeed',
|
||||
data: {
|
||||
data2: {
|
||||
res: result?.res || {}
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
else {
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'fail',
|
||||
data: {
|
||||
data2: {
|
||||
res: result?.res || {}
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'fail',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
console.warn('发短信消息失败', err);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
const { channel } = notification;
|
||||
const handler = getNotificationHandler(channel);
|
||||
await handler(notification, context);
|
||||
return 1;
|
||||
}
|
||||
async function tryCreateSmsNotification(message, context) {
|
||||
const smsNotification = await tryMakeSmsNotification(message, context);
|
||||
|
|
@ -415,8 +163,27 @@ const triggers = [
|
|||
closeRootMode();
|
||||
return 1;
|
||||
}
|
||||
if (message.weight === 'medium' && !smsTried && allFailed) {
|
||||
// 中级的消息,在其它途径都失败的情况下再发短信
|
||||
// 获取所有注册的失败处理器
|
||||
const failureHandlers = getNotificationFailureHandlers();
|
||||
if (failureHandlers.length > 0) {
|
||||
// 如果有注册的失败处理器,执行所有处理器
|
||||
let totalResult = 0;
|
||||
for (const handler of failureHandlers) {
|
||||
try {
|
||||
const result = await handler(message, context);
|
||||
totalResult += result;
|
||||
}
|
||||
catch (err) {
|
||||
console.error('执行notification失败处理器时出错:', err);
|
||||
}
|
||||
}
|
||||
if (totalResult > 0) {
|
||||
closeRootMode();
|
||||
return totalResult;
|
||||
}
|
||||
}
|
||||
else if (message.weight === 'medium' && !smsTried && allFailed) {
|
||||
// 如果没有注册处理器,走原有逻辑:中级的消息,在其它途径都失败的情况下再发短信
|
||||
const result = await tryCreateSmsNotification(message, context);
|
||||
closeRootMode();
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -1,2 +1,16 @@
|
|||
import BackendRuntimeContext from '../../context/BackendRuntimeContext';
|
||||
import { Router } from '../../entities/Message';
|
||||
import { EntityDict } from '../../oak-app-domain';
|
||||
import { MessageHandler } from './index';
|
||||
export declare const emailHandler: MessageHandler;
|
||||
export declare function tryMakeEmailNotification(message: {
|
||||
userId?: string;
|
||||
type?: string;
|
||||
entity?: string;
|
||||
router?: Router | null;
|
||||
entityId?: string;
|
||||
}, context: BackendRuntimeContext<EntityDict>): Promise<{
|
||||
id: string;
|
||||
data: import("../../types/Email").EmailOptions;
|
||||
channel: string;
|
||||
} | undefined>;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { tryMakeEmailNotification } from '../../triggers/message';
|
||||
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
||||
import { ConverterDict } from './index';
|
||||
export const emailHandler = async ({ message, applications, system, messageTypeTemplates, context }) => {
|
||||
const notificationDatas = [];
|
||||
const emailNotification = await tryMakeEmailNotification(message, context);
|
||||
|
|
@ -7,3 +8,17 @@ export const emailHandler = async ({ message, applications, system, messageTypeT
|
|||
}
|
||||
return notificationDatas;
|
||||
};
|
||||
export async function tryMakeEmailNotification(message, context) {
|
||||
const { userId, type, entity, entityId, router } = message;
|
||||
const converter = ConverterDict[type] && ConverterDict[type].toEmail;
|
||||
if (converter) {
|
||||
const dispersedData = await converter(message, context);
|
||||
if (dispersedData) {
|
||||
return {
|
||||
id: await generateNewIdAsync(),
|
||||
data: dispersedData,
|
||||
channel: 'email',
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
import { assert } from "oak-domain/lib/utils/assert";
|
||||
import { wechatMpHandler } from './wechatMp';
|
||||
import { wechatPublicHandler } from './wechatPublic';
|
||||
import { smsHandler } from './sms';
|
||||
import { emailHandler } from './email';
|
||||
export const ConverterDict = {};
|
||||
export function registerMessageNotificationConverters(converter) {
|
||||
Object.keys(converter).forEach(key => {
|
||||
|
|
@ -14,3 +18,8 @@ export function getMessageHandler(channel) {
|
|||
assert(handler, `消息渠道 ${channel} 的处理器未注册`);
|
||||
return handler;
|
||||
}
|
||||
// 默认注册所有处理器
|
||||
registerMessageHandler('wechatMp', wechatMpHandler);
|
||||
registerMessageHandler('wechatPublic', wechatPublicHandler);
|
||||
registerMessageHandler('sms', smsHandler);
|
||||
registerMessageHandler('email', emailHandler);
|
||||
|
|
|
|||
|
|
@ -1,2 +1,12 @@
|
|||
import BackendRuntimeContext from '../../context/BackendRuntimeContext';
|
||||
import { Router } from '../../entities/Message';
|
||||
import { EntityDict } from '../../oak-app-domain';
|
||||
import { MessageHandler } from './index';
|
||||
export declare const smsHandler: MessageHandler;
|
||||
export declare function tryMakeSmsNotification(message: {
|
||||
userId?: string;
|
||||
type?: string;
|
||||
entity?: string;
|
||||
router?: Router | null;
|
||||
entityId?: string;
|
||||
}, context: BackendRuntimeContext<EntityDict>): Promise<any>;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import { tryMakeSmsNotification } from '../../triggers/message';
|
||||
import assert from 'assert';
|
||||
import { ConverterDict } from './index';
|
||||
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
||||
export const smsHandler = async ({ message, applications, system, messageTypeTemplates, context }) => {
|
||||
const notificationDatas = [];
|
||||
const smsNotification = await tryMakeSmsNotification(message, context);
|
||||
|
|
@ -7,3 +9,32 @@ export const smsHandler = async ({ message, applications, system, messageTypeTem
|
|||
}
|
||||
return notificationDatas;
|
||||
};
|
||||
export async function tryMakeSmsNotification(message, context) {
|
||||
const { userId, type, entity, entityId, router } = message;
|
||||
assert(userId);
|
||||
const [mobile] = await context.select('mobile', {
|
||||
data: {
|
||||
id: 1,
|
||||
mobile: 1,
|
||||
},
|
||||
filter: {
|
||||
userId,
|
||||
},
|
||||
indexFrom: 0,
|
||||
count: 1,
|
||||
}, { dontCollect: true });
|
||||
if (mobile) {
|
||||
const converter = ConverterDict[type] && ConverterDict[type].toSms;
|
||||
if (converter) {
|
||||
const dispersedData = await converter(message, context);
|
||||
if (dispersedData) {
|
||||
return {
|
||||
id: await generateNewIdAsync(),
|
||||
data: dispersedData,
|
||||
channel: 'sms',
|
||||
data1: mobile,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
import { NotificationHandler } from './index';
|
||||
export declare const emailHandler: NotificationHandler;
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
||||
import { sendEmail } from '../email';
|
||||
export const emailHandler = async (notification, context) => {
|
||||
const { data, id } = notification;
|
||||
try {
|
||||
const result = await sendEmail(data, context);
|
||||
if (result?.success) {
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'succeed',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
},
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
else {
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'fail',
|
||||
data: {
|
||||
data2: {
|
||||
res: result?.error,
|
||||
},
|
||||
},
|
||||
filter: {
|
||||
id,
|
||||
},
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'fail',
|
||||
data: {
|
||||
data2: {
|
||||
res: err?.message,
|
||||
},
|
||||
},
|
||||
filter: {
|
||||
id,
|
||||
},
|
||||
}, { dontCollect: true });
|
||||
console.warn('发邮件消息失败', err);
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import { EntityDict } from "../../oak-app-domain";
|
||||
import { BRC } from "../../types/RuntimeCxt";
|
||||
import { Channel } from "../../types/Message";
|
||||
export type NotificationHandler = (notification: EntityDict['notification']['OpSchema'], context: BRC<EntityDict>) => Promise<void>;
|
||||
export type NotificationFailureHandler = (message: EntityDict['message']['Schema'], context: BRC<EntityDict>) => Promise<number>;
|
||||
export declare function registerNotificationHandler(channel: Channel, handler: NotificationHandler): void;
|
||||
export declare function getNotificationHandler(channel: Channel): NotificationHandler;
|
||||
export declare function registerNotificationFailureHandler(handler: NotificationFailureHandler): void;
|
||||
export declare function getNotificationFailureHandlers(): NotificationFailureHandler[];
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
import { assert } from "oak-domain/lib/utils/assert";
|
||||
import { wechatMpHandler } from './wechatMp';
|
||||
import { wechatPublicHandler } from './wechatPublic';
|
||||
import { smsHandler } from './sms';
|
||||
import { emailHandler } from './email';
|
||||
const notificationHandlers = {};
|
||||
const notificationFailureHandlers = [];
|
||||
export function registerNotificationHandler(channel, handler) {
|
||||
notificationHandlers[channel] = handler;
|
||||
}
|
||||
export function getNotificationHandler(channel) {
|
||||
const handler = notificationHandlers[channel];
|
||||
assert(handler, `通知渠道 ${channel} 的处理器未注册`);
|
||||
return handler;
|
||||
}
|
||||
export function registerNotificationFailureHandler(handler) {
|
||||
notificationFailureHandlers.push(handler);
|
||||
}
|
||||
export function getNotificationFailureHandlers() {
|
||||
return notificationFailureHandlers;
|
||||
}
|
||||
// 默认注册所有处理器
|
||||
registerNotificationHandler('wechatMp', wechatMpHandler);
|
||||
registerNotificationHandler('wechatPublic', wechatPublicHandler);
|
||||
registerNotificationHandler('sms', smsHandler);
|
||||
registerNotificationHandler('email', emailHandler);
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
import { NotificationHandler } from './index';
|
||||
export declare const smsHandler: NotificationHandler;
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
||||
import { sendSms } from '../sms';
|
||||
export const smsHandler = async (notification, context) => {
|
||||
const { data, data1, id } = notification;
|
||||
const [messageSystem] = await context.select('messageSystem', {
|
||||
data: {
|
||||
id: 1,
|
||||
message: {
|
||||
id: 1,
|
||||
type: 1,
|
||||
},
|
||||
},
|
||||
filter: {
|
||||
id: notification.messageSystemId,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
const { message } = messageSystem;
|
||||
const { type } = message;
|
||||
try {
|
||||
const result = await sendSms({
|
||||
messageType: type,
|
||||
templateParam: data.params,
|
||||
mobile: data1.mobile,
|
||||
}, context);
|
||||
if (result?.success === true) {
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'succeed',
|
||||
data: {
|
||||
data2: {
|
||||
res: result?.res || {}
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
else {
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'fail',
|
||||
data: {
|
||||
data2: {
|
||||
res: result?.res || {}
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'fail',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
console.warn('发短信消息失败', err);
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
import { NotificationHandler } from './index';
|
||||
export declare const wechatMpHandler: NotificationHandler;
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
||||
import WechatSDK from 'oak-external-sdk/lib/WechatSDK';
|
||||
import { composeUrl } from 'oak-domain/lib/utils/domain';
|
||||
export const wechatMpHandler = async (notification, context) => {
|
||||
const { data, templateId, messageSystemId, data1, id } = notification;
|
||||
const [messageSystem] = await context.select('messageSystem', {
|
||||
data: {
|
||||
id: 1,
|
||||
messageId: 1,
|
||||
message: {
|
||||
id: 1,
|
||||
userId: 1,
|
||||
router: 1,
|
||||
type: 1,
|
||||
},
|
||||
system: {
|
||||
id: 1,
|
||||
application$system: {
|
||||
$entity: 'application',
|
||||
data: {
|
||||
id: 1,
|
||||
type: 1,
|
||||
config: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
id: messageSystemId,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
const { system, message } = messageSystem;
|
||||
const { router } = message;
|
||||
const { application$system: applications } = system;
|
||||
const app = applications.find(ele => ele.type === 'wechatMp');
|
||||
const { config } = app;
|
||||
const { appId, appSecret } = config;
|
||||
const instance = WechatSDK.getInstance(appId, 'wechatMp', appSecret);
|
||||
let page;
|
||||
if (router) {
|
||||
const pathname = router.pathname;
|
||||
const url = pathname.startsWith('/')
|
||||
? `pages${pathname}/index`
|
||||
: `pages/${pathname}/index`;
|
||||
page = composeUrl(url, Object.assign({}, router.props, router.state));
|
||||
}
|
||||
// 根据当前环境决定消息推哪个版本
|
||||
const StateDict = {
|
||||
'development': 'developer',
|
||||
'staging': 'trial',
|
||||
'production': 'former',
|
||||
};
|
||||
try {
|
||||
await instance.sendSubscribedMessage({
|
||||
templateId: templateId,
|
||||
data: data,
|
||||
openId: data1.openId,
|
||||
page,
|
||||
state: StateDict[process.env.NODE_ENV],
|
||||
});
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'succeed',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
catch (err) {
|
||||
console.warn('发微信小程序消息失败', err);
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'fail',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
import { NotificationHandler } from './index';
|
||||
export declare const wechatPublicHandler: NotificationHandler;
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
||||
import WechatSDK from 'oak-external-sdk/lib/WechatSDK';
|
||||
import { composeUrl } from 'oak-domain/lib/utils/domain';
|
||||
import { composeDomainUrl } from '../domain';
|
||||
export const wechatPublicHandler = async (notification, context) => {
|
||||
const { data, templateId, messageSystemId, data1, id } = notification;
|
||||
const [messageSystem] = await context.select('messageSystem', {
|
||||
data: {
|
||||
id: 1,
|
||||
messageId: 1,
|
||||
message: {
|
||||
id: 1,
|
||||
userId: 1,
|
||||
router: 1,
|
||||
type: 1,
|
||||
},
|
||||
system: {
|
||||
id: 1,
|
||||
application$system: {
|
||||
$entity: 'application',
|
||||
data: {
|
||||
id: 1,
|
||||
type: 1,
|
||||
config: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
id: messageSystemId,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
const { system, message } = messageSystem;
|
||||
const { router } = message;
|
||||
const { application$system: applications } = system;
|
||||
const app = applications.find(ele => ele.type === 'wechatPublic');
|
||||
const { config, id: applicationId } = app;
|
||||
const { appId, appSecret } = config;
|
||||
const [domain] = await context.select('domain', {
|
||||
data: {
|
||||
id: 1,
|
||||
url: 1,
|
||||
apiPath: 1,
|
||||
protocol: 1,
|
||||
port: 1,
|
||||
},
|
||||
filter: {
|
||||
system: {
|
||||
application$system: {
|
||||
id: applicationId,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, { dontCollect: true });
|
||||
const instance = WechatSDK.getInstance(appId, 'wechatPublic', appSecret);
|
||||
const { openId, wechatMpAppId } = data1;
|
||||
let page;
|
||||
// message 用户不需要跳转页面
|
||||
if (router) {
|
||||
const pathname = router.pathname;
|
||||
if (wechatMpAppId) {
|
||||
const url = pathname.startsWith('/')
|
||||
? `pages${pathname}/index`
|
||||
: `pages/${pathname}/index`;
|
||||
page = composeUrl(url, Object.assign({}, router.props, router.state));
|
||||
}
|
||||
else {
|
||||
const url = composeDomainUrl(domain, pathname);
|
||||
page = composeUrl(url, Object.assign({}, router.props, router.state));
|
||||
}
|
||||
}
|
||||
try {
|
||||
await instance.sendTemplateMessage({
|
||||
openId,
|
||||
templateId: templateId,
|
||||
url: !wechatMpAppId ? page : undefined,
|
||||
data: data,
|
||||
miniProgram: wechatMpAppId
|
||||
? {
|
||||
appid: wechatMpAppId,
|
||||
pagepath: page,
|
||||
}
|
||||
: undefined,
|
||||
clientMsgId: id,
|
||||
});
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'succeed',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
catch (err) {
|
||||
console.warn('发微信公众号消息失败', err);
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'fail',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
};
|
||||
|
|
@ -1,11 +1,10 @@
|
|||
import { String } from 'oak-domain/lib/types/DataType';
|
||||
import { EntityShape } from 'oak-domain/lib/types/Entity';
|
||||
import { Channel } from '../types/Message';
|
||||
import { Schema as Application } from './Application';
|
||||
import { Schema as MessageSystem } from './MessageSystem';
|
||||
import { EntityDesc } from 'oak-domain/lib/types/EntityDesc';
|
||||
export interface Schema extends EntityShape {
|
||||
channel: Channel;
|
||||
channel: String<32>;
|
||||
application?: Application;
|
||||
data?: Object;
|
||||
messageSystem: MessageSystem;
|
||||
|
|
@ -18,6 +17,5 @@ export type IState = 'sending' | 'success' | 'failure';
|
|||
type Action = IAction;
|
||||
export declare const entityDesc: EntityDesc<Schema, Action, '', {
|
||||
iState: IState;
|
||||
channel: Schema['channel'];
|
||||
}>;
|
||||
export {};
|
||||
|
|
|
|||
|
|
@ -33,14 +33,14 @@ exports.entityDesc = {
|
|||
success: '发送成功',
|
||||
failure: '发送失败',
|
||||
},
|
||||
channel: {
|
||||
wechatPublic: '公众号',
|
||||
jPush: '极光推送',
|
||||
jim: '极光消息',
|
||||
wechatMp: '小程序',
|
||||
sms: '短信',
|
||||
email: '邮箱',
|
||||
}
|
||||
// channel: {
|
||||
// wechatPublic: '公众号',
|
||||
// jPush: '极光推送',
|
||||
// jim: '极光消息',
|
||||
// wechatMp: '小程序',
|
||||
// sms: '短信',
|
||||
// email: '邮箱',
|
||||
// }
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -55,14 +55,14 @@ exports.entityDesc = {
|
|||
success: '#008000',
|
||||
failure: '#9A9A9A',
|
||||
},
|
||||
channel: {
|
||||
wechatMp: '#008000',
|
||||
jPush: '#0000FF',
|
||||
jim: '#0000FF',
|
||||
wechatPublic: '#008000',
|
||||
sms: '#000000',
|
||||
email: '#000000',
|
||||
},
|
||||
// channel: {
|
||||
// wechatMp: '#008000',
|
||||
// jPush: '#0000FF',
|
||||
// jim: '#0000FF',
|
||||
// wechatPublic: '#008000',
|
||||
// sms: '#000000',
|
||||
// email: '#000000',
|
||||
// },
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,8 +6,10 @@ exports.desc = {
|
|||
attributes: {
|
||||
channel: {
|
||||
notNull: true,
|
||||
type: "enum",
|
||||
enumeration: ["wechatPublic", "jPush", "jim", "wechatMp", "sms", "email"]
|
||||
type: "varchar",
|
||||
params: {
|
||||
length: 32
|
||||
}
|
||||
},
|
||||
applicationId: {
|
||||
type: "ref",
|
||||
|
|
|
|||
|
|
@ -12,13 +12,13 @@ exports.style = {
|
|||
success: '#008000',
|
||||
failure: '#9A9A9A',
|
||||
},
|
||||
channel: {
|
||||
wechatMp: '#008000',
|
||||
jPush: '#0000FF',
|
||||
jim: '#0000FF',
|
||||
wechatPublic: '#008000',
|
||||
sms: '#000000',
|
||||
email: '#000000',
|
||||
},
|
||||
// channel: {
|
||||
// wechatMp: '#008000',
|
||||
// jPush: '#0000FF',
|
||||
// jim: '#0000FF',
|
||||
// wechatPublic: '#008000',
|
||||
// sms: '#000000',
|
||||
// email: '#000000',
|
||||
// },
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,10 +2,9 @@ import { ForeignKey } from "oak-domain/lib/types/DataType";
|
|||
import { Q_DateValue, Q_NumberValue, Q_StringValue, Q_EnumValue, NodeId, ExprOp, ExpressionKey } from "oak-domain/lib/types/Demand";
|
||||
import { MakeAction as OakMakeAction, EntityShape } from "oak-domain/lib/types/Entity";
|
||||
import { Action, ParticularAction, IState } from "./Action";
|
||||
import { Channel } from "../../types/Message";
|
||||
import { String } from "oak-domain/lib/types/DataType";
|
||||
export type OpSchema = EntityShape & {
|
||||
channel: Channel;
|
||||
channel: String<32>;
|
||||
applicationId?: ForeignKey<"application"> | null;
|
||||
data?: Object | null;
|
||||
messageSystemId: ForeignKey<"messageSystem">;
|
||||
|
|
@ -22,7 +21,7 @@ export type OpFilter = {
|
|||
$$createAt$$: Q_DateValue;
|
||||
$$seq$$: Q_NumberValue;
|
||||
$$updateAt$$: Q_DateValue;
|
||||
channel: Q_EnumValue<Channel>;
|
||||
channel: Q_StringValue;
|
||||
applicationId: Q_StringValue;
|
||||
data: Object;
|
||||
messageSystemId: Q_StringValue;
|
||||
|
|
|
|||
|
|
@ -19,14 +19,6 @@
|
|||
"sending": "发送中",
|
||||
"success": "发送成功",
|
||||
"failure": "发送失败"
|
||||
},
|
||||
"channel": {
|
||||
"wechatPublic": "公众号",
|
||||
"jPush": "极光推送",
|
||||
"jim": "极光消息",
|
||||
"wechatMp": "小程序",
|
||||
"sms": "短信",
|
||||
"email": "邮箱"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ exports.desc = {
|
|||
qrCodeType: {
|
||||
notNull: true,
|
||||
type: "enum",
|
||||
enumeration: ["wechatPublic", "wechatMpDomainUrl", "wechatMpWxaCode", "wechatPublicForMp", "webForWechatPublic"]
|
||||
enumeration: ["wechatMpDomainUrl", "wechatMpWxaCode", "wechatPublic", "wechatPublicForMp", "webForWechatPublic"]
|
||||
},
|
||||
expiresAt: {
|
||||
type: "datetime"
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ exports.desc = {
|
|||
qrCodeType: {
|
||||
notNull: true,
|
||||
type: "enum",
|
||||
enumeration: ["wechatPublic", "wechatMpDomainUrl", "wechatMpWxaCode", "wechatPublicForMp", "webForWechatPublic"]
|
||||
enumeration: ["wechatMpDomainUrl", "wechatMpWxaCode", "wechatPublic", "wechatPublicForMp", "webForWechatPublic"]
|
||||
},
|
||||
expiresAt: {
|
||||
type: "datetime"
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ exports.desc = {
|
|||
type: {
|
||||
notNull: true,
|
||||
type: "enum",
|
||||
enumeration: ["wechatPublic", "wechatMpDomainUrl", "wechatMpWxaCode", "wechatPublicForMp", "webForWechatPublic"]
|
||||
enumeration: ["wechatMpDomainUrl", "wechatMpWxaCode", "wechatPublic", "wechatPublicForMp", "webForWechatPublic"]
|
||||
},
|
||||
allowShare: {
|
||||
notNull: true,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,32 @@ export {
|
|||
*/
|
||||
registerMessageType, } from './aspects/template';
|
||||
export { registerWeChatPublicEventCallback, } from './endpoints/wechat';
|
||||
export { registerMessageNotificationConverters, registerMessageHandler, } from './utils/message';
|
||||
export {
|
||||
/**
|
||||
* 注册消息通知转换器
|
||||
* 用于将消息数据转换为特定渠道所需的格式
|
||||
* 例如: 将message转换为微信小程序、公众号、短信、邮件所需的数据格式
|
||||
*/
|
||||
registerMessageNotificationConverters,
|
||||
/**
|
||||
* 注册消息渠道处理器
|
||||
* 用于处理特定渠道的消息创建逻辑
|
||||
* 例如: 处理微信小程序、公众号、短信、邮件等渠道的消息生成
|
||||
*/
|
||||
registerMessageHandler, } from './utils/message';
|
||||
export {
|
||||
/**
|
||||
* 注册通知渠道处理器
|
||||
* 用于处理特定渠道的通知发送逻辑
|
||||
* 例如: 实际发送微信小程序、公众号、短信、邮件等渠道的通知
|
||||
*/
|
||||
registerNotificationHandler,
|
||||
/**
|
||||
* 注册通知失败处理器
|
||||
* 用于在所有通知渠道都失败后执行自定义的补救逻辑
|
||||
* 可以注册多个处理器,它们会依次执行
|
||||
* 例如: 在其他渠道失败后自动发送短信通知
|
||||
*/
|
||||
registerNotificationFailureHandler, } from './utils/notification';
|
||||
export { registSms, } from './utils/sms';
|
||||
export { registerCosBackend, } from './utils/cos/index.backend';
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
* 如需要注入,请在routine中编写注册逻辑,使用此处提供的注册方法进行注册
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.registerCosBackend = exports.registSms = exports.registerMessageHandler = exports.registerMessageNotificationConverters = exports.registerWeChatPublicEventCallback = exports.registerMessageType = void 0;
|
||||
exports.registerCosBackend = exports.registSms = exports.registerNotificationFailureHandler = exports.registerNotificationHandler = exports.registerMessageHandler = exports.registerMessageNotificationConverters = exports.registerWeChatPublicEventCallback = exports.registerMessageType = void 0;
|
||||
var template_1 = require("./aspects/template");
|
||||
/**
|
||||
* 注册消息类型
|
||||
|
|
@ -14,9 +14,32 @@ var wechat_1 = require("./endpoints/wechat");
|
|||
// 注册微信事件回调处理器endpoint
|
||||
Object.defineProperty(exports, "registerWeChatPublicEventCallback", { enumerable: true, get: function () { return wechat_1.registerWeChatPublicEventCallback; } });
|
||||
var message_1 = require("./utils/message");
|
||||
// 注册消息通知转换器trigger
|
||||
/**
|
||||
* 注册消息通知转换器
|
||||
* 用于将消息数据转换为特定渠道所需的格式
|
||||
* 例如: 将message转换为微信小程序、公众号、短信、邮件所需的数据格式
|
||||
*/
|
||||
Object.defineProperty(exports, "registerMessageNotificationConverters", { enumerable: true, get: function () { return message_1.registerMessageNotificationConverters; } });
|
||||
/**
|
||||
* 注册消息渠道处理器
|
||||
* 用于处理特定渠道的消息创建逻辑
|
||||
* 例如: 处理微信小程序、公众号、短信、邮件等渠道的消息生成
|
||||
*/
|
||||
Object.defineProperty(exports, "registerMessageHandler", { enumerable: true, get: function () { return message_1.registerMessageHandler; } });
|
||||
var notification_1 = require("./utils/notification");
|
||||
/**
|
||||
* 注册通知渠道处理器
|
||||
* 用于处理特定渠道的通知发送逻辑
|
||||
* 例如: 实际发送微信小程序、公众号、短信、邮件等渠道的通知
|
||||
*/
|
||||
Object.defineProperty(exports, "registerNotificationHandler", { enumerable: true, get: function () { return notification_1.registerNotificationHandler; } });
|
||||
/**
|
||||
* 注册通知失败处理器
|
||||
* 用于在所有通知渠道都失败后执行自定义的补救逻辑
|
||||
* 可以注册多个处理器,它们会依次执行
|
||||
* 例如: 在其他渠道失败后自动发送短信通知
|
||||
*/
|
||||
Object.defineProperty(exports, "registerNotificationFailureHandler", { enumerable: true, get: function () { return notification_1.registerNotificationFailureHandler; } });
|
||||
var sms_1 = require("./utils/sms");
|
||||
// 注册短信服务商实现
|
||||
Object.defineProperty(exports, "registSms", { enumerable: true, get: function () { return sms_1.registSms; } });
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
declare const _default: (import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "message", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "application", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "address", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "user", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "userEntityGrant", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatQrCode", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "notification", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatLogin", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "articleMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "article", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "parasite", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "extraFile", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "sessionMessage", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatPublicTag", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMpJump", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "system", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "passport", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthApplication", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthProvider", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthUser", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthUserAuthorization", import("../context/BackendRuntimeContext").BackendRuntimeContext<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "mobile", import("..").BRC<import("../oak-app-domain").EntityDict>>)[];
|
||||
declare const _default: (import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "address", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "application", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "article", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "articleMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "extraFile", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "user", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "userEntityGrant", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatQrCode", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "message", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "notification", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatLogin", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "parasite", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "sessionMessage", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatPublicTag", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMpJump", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "system", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "passport", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthApplication", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthProvider", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthUser", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthUserAuthorization", import("../context/BackendRuntimeContext").BackendRuntimeContext<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "mobile", import("..").BRC<import("../oak-app-domain").EntityDict>>)[];
|
||||
export default _default;
|
||||
|
|
|
|||
|
|
@ -1,25 +1,5 @@
|
|||
import { Trigger } from 'oak-domain/lib/types/Trigger';
|
||||
import { EntityDict } from '../oak-app-domain/EntityDict';
|
||||
import { BRC } from '../types/RuntimeCxt';
|
||||
import { BackendRuntimeContext } from '../context/BackendRuntimeContext';
|
||||
import { Router } from '../entities/Message';
|
||||
export declare function tryMakeSmsNotification(message: {
|
||||
userId?: string;
|
||||
type?: string;
|
||||
entity?: string;
|
||||
router?: Router | null;
|
||||
entityId?: string;
|
||||
}, context: BackendRuntimeContext<EntityDict>): Promise<any>;
|
||||
export declare function tryMakeEmailNotification(message: {
|
||||
userId?: string;
|
||||
type?: string;
|
||||
entity?: string;
|
||||
router?: Router | null;
|
||||
entityId?: string;
|
||||
}, context: BackendRuntimeContext<EntityDict>): Promise<{
|
||||
id: string;
|
||||
data: import("../types/Email").EmailOptions;
|
||||
channel: string;
|
||||
} | undefined>;
|
||||
declare const triggers: Trigger<EntityDict, 'message', BRC<EntityDict>>[];
|
||||
export default triggers;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.tryMakeSmsNotification = tryMakeSmsNotification;
|
||||
exports.tryMakeEmailNotification = tryMakeEmailNotification;
|
||||
const uuid_1 = require("oak-domain/lib/utils/uuid");
|
||||
const assert_1 = require("oak-domain/lib/utils/assert");
|
||||
const lodash_1 = require("oak-domain/lib/utils/lodash");
|
||||
|
|
@ -11,49 +9,6 @@ const InitialChannelByWeightMatrix = {
|
|||
medium: ['wechatMp', 'wechatPublic', 'email'],
|
||||
low: ['wechatMp', 'wechatPublic', 'email'],
|
||||
};
|
||||
async function tryMakeSmsNotification(message, context) {
|
||||
const { userId, type, entity, entityId, router } = message;
|
||||
(0, assert_1.assert)(userId);
|
||||
const [mobile] = await context.select('mobile', {
|
||||
data: {
|
||||
id: 1,
|
||||
mobile: 1,
|
||||
},
|
||||
filter: {
|
||||
userId,
|
||||
},
|
||||
indexFrom: 0,
|
||||
count: 1,
|
||||
}, { dontCollect: true });
|
||||
if (mobile) {
|
||||
const converter = message_1.ConverterDict[type] && message_1.ConverterDict[type].toSms;
|
||||
if (converter) {
|
||||
const dispersedData = await converter(message, context);
|
||||
if (dispersedData) {
|
||||
return {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
data: dispersedData,
|
||||
channel: 'sms',
|
||||
data1: mobile,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
async function tryMakeEmailNotification(message, context) {
|
||||
const { userId, type, entity, entityId, router } = message;
|
||||
const converter = message_1.ConverterDict[type] && message_1.ConverterDict[type].toEmail;
|
||||
if (converter) {
|
||||
const dispersedData = await converter(message, context);
|
||||
if (dispersedData) {
|
||||
return {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
data: dispersedData,
|
||||
channel: 'email',
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
async function createNotification(message, context) {
|
||||
const { restriction, userId, weight, type, entity, entityId, platformId, channels } = message;
|
||||
(0, assert_1.assert)(userId);
|
||||
|
|
|
|||
|
|
@ -1,270 +1,17 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const tslib_1 = require("tslib");
|
||||
const assert_1 = require("oak-domain/lib/utils/assert");
|
||||
const WechatSDK_1 = tslib_1.__importDefault(require("oak-external-sdk/lib/WechatSDK"));
|
||||
const domain_1 = require("oak-domain/lib/utils/domain");
|
||||
const uuid_1 = require("oak-domain/lib/utils/uuid");
|
||||
const sms_1 = require("../utils/sms");
|
||||
const email_1 = require("../utils/email");
|
||||
const message_1 = require("./message");
|
||||
const domain_2 = require("../utils/domain");
|
||||
const sms_1 = require("../utils/message/sms");
|
||||
const notification_1 = require("../utils/notification");
|
||||
async function sendNotification(notification, context) {
|
||||
const { data, templateId, channel, messageSystemId, data1, id } = notification;
|
||||
const [messageSystem] = await context.select('messageSystem', {
|
||||
data: {
|
||||
id: 1,
|
||||
messageId: 1,
|
||||
message: {
|
||||
id: 1,
|
||||
userId: 1,
|
||||
router: 1,
|
||||
type: 1,
|
||||
},
|
||||
system: {
|
||||
id: 1,
|
||||
application$system: {
|
||||
$entity: 'application',
|
||||
data: {
|
||||
id: 1,
|
||||
type: 1,
|
||||
config: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
id: messageSystemId,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
const { system, message } = messageSystem;
|
||||
const { router, userId, type } = message;
|
||||
const { application$system: applications, config } = system;
|
||||
switch (channel) {
|
||||
case 'wechatMp': {
|
||||
const app = applications.find(ele => ele.type === 'wechatMp');
|
||||
const { config } = app;
|
||||
const { appId, appSecret } = config;
|
||||
const instance = WechatSDK_1.default.getInstance(appId, 'wechatMp', appSecret);
|
||||
let page;
|
||||
if (router) {
|
||||
const pathname = router.pathname;
|
||||
const url = pathname.startsWith('/')
|
||||
? `pages${pathname}/index`
|
||||
: `pages/${pathname}/index`;
|
||||
page = (0, domain_1.composeUrl)(url, Object.assign({}, router.props, router.state));
|
||||
}
|
||||
// 根据当前环境决定消息推哪个版本
|
||||
const StateDict = {
|
||||
'development': 'developer',
|
||||
'staging': 'trial',
|
||||
'production': 'former',
|
||||
};
|
||||
try {
|
||||
await instance.sendSubscribedMessage({
|
||||
templateId: templateId,
|
||||
data: data,
|
||||
openId: data1.openId, // 在notification创建时就赋值了
|
||||
page,
|
||||
state: StateDict[process.env.NODE_ENV],
|
||||
});
|
||||
await context.operate('notification', {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'succeed',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
return 1;
|
||||
}
|
||||
catch (err) {
|
||||
console.warn('发微信小程序消息失败', err);
|
||||
await context.operate('notification', {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'fail',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
case 'wechatPublic': {
|
||||
const app = applications.find(ele => ele.type === 'wechatPublic');
|
||||
const { config, id: applicationId } = app;
|
||||
const { appId, appSecret } = config;
|
||||
const [domain] = await context.select('domain', {
|
||||
data: {
|
||||
id: 1,
|
||||
url: 1,
|
||||
apiPath: 1,
|
||||
protocol: 1,
|
||||
port: 1,
|
||||
},
|
||||
filter: {
|
||||
system: {
|
||||
application$system: {
|
||||
id: applicationId,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, { dontCollect: true });
|
||||
const instance = WechatSDK_1.default.getInstance(appId, 'wechatPublic', appSecret);
|
||||
const { openId, wechatMpAppId } = data1;
|
||||
let page;
|
||||
// message 用户不需要跳转页面
|
||||
if (router) {
|
||||
const pathname = router.pathname;
|
||||
if (wechatMpAppId) {
|
||||
const url = pathname.startsWith('/')
|
||||
? `pages${pathname}/index`
|
||||
: `pages/${pathname}/index`;
|
||||
page = (0, domain_1.composeUrl)(url, Object.assign({}, router.props, router.state));
|
||||
}
|
||||
else {
|
||||
const url = (0, domain_2.composeDomainUrl)(domain, pathname);
|
||||
page = (0, domain_1.composeUrl)(url, Object.assign({}, router.props, router.state));
|
||||
}
|
||||
}
|
||||
try {
|
||||
await instance.sendTemplateMessage({
|
||||
openId,
|
||||
templateId: templateId,
|
||||
url: !wechatMpAppId ? page : undefined,
|
||||
data: data,
|
||||
miniProgram: wechatMpAppId
|
||||
? {
|
||||
appid: wechatMpAppId,
|
||||
pagepath: page,
|
||||
}
|
||||
: undefined,
|
||||
clientMsgId: id,
|
||||
});
|
||||
await context.operate('notification', {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'succeed',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
return 1;
|
||||
}
|
||||
catch (err) {
|
||||
console.warn('发微信公众号消息失败', err);
|
||||
await context.operate('notification', {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'fail',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
case 'email': {
|
||||
try {
|
||||
const result = await (0, email_1.sendEmail)(data, context);
|
||||
if (result?.success) {
|
||||
await context.operate('notification', {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'succeed',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
},
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
else {
|
||||
await context.operate('notification', {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'fail',
|
||||
data: {
|
||||
data2: {
|
||||
res: result?.error,
|
||||
},
|
||||
},
|
||||
filter: {
|
||||
id,
|
||||
},
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
catch (err) {
|
||||
await context.operate('notification', {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'fail',
|
||||
data: {
|
||||
data2: {
|
||||
res: err?.message,
|
||||
},
|
||||
},
|
||||
filter: {
|
||||
id,
|
||||
},
|
||||
}, { dontCollect: true });
|
||||
console.warn('发邮件消息失败', err);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
default: {
|
||||
(0, assert_1.assert)(channel === 'sms');
|
||||
try {
|
||||
const result = await (0, sms_1.sendSms)({
|
||||
messageType: type,
|
||||
templateParam: data.params,
|
||||
mobile: data1.mobile,
|
||||
}, context);
|
||||
if (result?.success === true) {
|
||||
await context.operate('notification', {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'succeed',
|
||||
data: {
|
||||
data2: {
|
||||
res: result?.res || {}
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
else {
|
||||
await context.operate('notification', {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'fail',
|
||||
data: {
|
||||
data2: {
|
||||
res: result?.res || {}
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
await context.operate('notification', {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'fail',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
console.warn('发短信消息失败', err);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
const { channel } = notification;
|
||||
const handler = (0, notification_1.getNotificationHandler)(channel);
|
||||
await handler(notification, context);
|
||||
return 1;
|
||||
}
|
||||
async function tryCreateSmsNotification(message, context) {
|
||||
const smsNotification = await (0, message_1.tryMakeSmsNotification)(message, context);
|
||||
const smsNotification = await (0, sms_1.tryMakeSmsNotification)(message, context);
|
||||
if (smsNotification) {
|
||||
const { messageSystem$message } = message;
|
||||
for (const ms of messageSystem$message) {
|
||||
|
|
@ -418,8 +165,27 @@ const triggers = [
|
|||
closeRootMode();
|
||||
return 1;
|
||||
}
|
||||
if (message.weight === 'medium' && !smsTried && allFailed) {
|
||||
// 中级的消息,在其它途径都失败的情况下再发短信
|
||||
// 获取所有注册的失败处理器
|
||||
const failureHandlers = (0, notification_1.getNotificationFailureHandlers)();
|
||||
if (failureHandlers.length > 0) {
|
||||
// 如果有注册的失败处理器,执行所有处理器
|
||||
let totalResult = 0;
|
||||
for (const handler of failureHandlers) {
|
||||
try {
|
||||
const result = await handler(message, context);
|
||||
totalResult += result;
|
||||
}
|
||||
catch (err) {
|
||||
console.error('执行notification失败处理器时出错:', err);
|
||||
}
|
||||
}
|
||||
if (totalResult > 0) {
|
||||
closeRootMode();
|
||||
return totalResult;
|
||||
}
|
||||
}
|
||||
else if (message.weight === 'medium' && !smsTried && allFailed) {
|
||||
// 如果没有注册处理器,走原有逻辑:中级的消息,在其它途径都失败的情况下再发短信
|
||||
const result = await tryCreateSmsNotification(message, context);
|
||||
closeRootMode();
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export declare function createToDo<ED extends EntityDict & BaseEntityDict, T ext
|
|||
redirectTo: EntityDict['toDo']['OpSchema']['redirectTo'];
|
||||
entity: any;
|
||||
entityId: string;
|
||||
}, userIds?: string[]): Promise<1 | 0>;
|
||||
}, userIds?: string[]): Promise<0 | 1>;
|
||||
/**
|
||||
* 完成todo例程,当在entity对象上进行action操作时(操作条件是filter),将对应的todo完成
|
||||
* 必须在entity的action的后trigger中调用
|
||||
|
|
|
|||
|
|
@ -1,2 +1,16 @@
|
|||
import BackendRuntimeContext from '../../context/BackendRuntimeContext';
|
||||
import { Router } from '../../entities/Message';
|
||||
import { EntityDict } from '../../oak-app-domain';
|
||||
import { MessageHandler } from './index';
|
||||
export declare const emailHandler: MessageHandler;
|
||||
export declare function tryMakeEmailNotification(message: {
|
||||
userId?: string;
|
||||
type?: string;
|
||||
entity?: string;
|
||||
router?: Router | null;
|
||||
entityId?: string;
|
||||
}, context: BackendRuntimeContext<EntityDict>): Promise<{
|
||||
id: string;
|
||||
data: import("../../types/Email").EmailOptions;
|
||||
channel: string;
|
||||
} | undefined>;
|
||||
|
|
|
|||
|
|
@ -1,13 +1,29 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.emailHandler = void 0;
|
||||
const message_1 = require("../../triggers/message");
|
||||
exports.tryMakeEmailNotification = tryMakeEmailNotification;
|
||||
const uuid_1 = require("oak-domain/lib/utils/uuid");
|
||||
const index_1 = require("./index");
|
||||
const emailHandler = async ({ message, applications, system, messageTypeTemplates, context }) => {
|
||||
const notificationDatas = [];
|
||||
const emailNotification = await (0, message_1.tryMakeEmailNotification)(message, context);
|
||||
const emailNotification = await tryMakeEmailNotification(message, context);
|
||||
if (emailNotification) {
|
||||
notificationDatas.push(emailNotification);
|
||||
}
|
||||
return notificationDatas;
|
||||
};
|
||||
exports.emailHandler = emailHandler;
|
||||
async function tryMakeEmailNotification(message, context) {
|
||||
const { userId, type, entity, entityId, router } = message;
|
||||
const converter = index_1.ConverterDict[type] && index_1.ConverterDict[type].toEmail;
|
||||
if (converter) {
|
||||
const dispersedData = await converter(message, context);
|
||||
if (dispersedData) {
|
||||
return {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
data: dispersedData,
|
||||
channel: 'email',
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ exports.registerMessageNotificationConverters = registerMessageNotificationConve
|
|||
exports.registerMessageHandler = registerMessageHandler;
|
||||
exports.getMessageHandler = getMessageHandler;
|
||||
const assert_1 = require("oak-domain/lib/utils/assert");
|
||||
const wechatMp_1 = require("./wechatMp");
|
||||
const wechatPublic_1 = require("./wechatPublic");
|
||||
const sms_1 = require("./sms");
|
||||
const email_1 = require("./email");
|
||||
exports.ConverterDict = {};
|
||||
function registerMessageNotificationConverters(converter) {
|
||||
Object.keys(converter).forEach(key => {
|
||||
|
|
@ -20,3 +24,8 @@ function getMessageHandler(channel) {
|
|||
(0, assert_1.assert)(handler, `消息渠道 ${channel} 的处理器未注册`);
|
||||
return handler;
|
||||
}
|
||||
// 默认注册所有处理器
|
||||
registerMessageHandler('wechatMp', wechatMp_1.wechatMpHandler);
|
||||
registerMessageHandler('wechatPublic', wechatPublic_1.wechatPublicHandler);
|
||||
registerMessageHandler('sms', sms_1.smsHandler);
|
||||
registerMessageHandler('email', email_1.emailHandler);
|
||||
|
|
|
|||
|
|
@ -1,2 +1,12 @@
|
|||
import BackendRuntimeContext from '../../context/BackendRuntimeContext';
|
||||
import { Router } from '../../entities/Message';
|
||||
import { EntityDict } from '../../oak-app-domain';
|
||||
import { MessageHandler } from './index';
|
||||
export declare const smsHandler: MessageHandler;
|
||||
export declare function tryMakeSmsNotification(message: {
|
||||
userId?: string;
|
||||
type?: string;
|
||||
entity?: string;
|
||||
router?: Router | null;
|
||||
entityId?: string;
|
||||
}, context: BackendRuntimeContext<EntityDict>): Promise<any>;
|
||||
|
|
|
|||
|
|
@ -1,13 +1,46 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.smsHandler = void 0;
|
||||
const message_1 = require("../../triggers/message");
|
||||
exports.tryMakeSmsNotification = tryMakeSmsNotification;
|
||||
const tslib_1 = require("tslib");
|
||||
const assert_1 = tslib_1.__importDefault(require("assert"));
|
||||
const index_1 = require("./index");
|
||||
const uuid_1 = require("oak-domain/lib/utils/uuid");
|
||||
const smsHandler = async ({ message, applications, system, messageTypeTemplates, context }) => {
|
||||
const notificationDatas = [];
|
||||
const smsNotification = await (0, message_1.tryMakeSmsNotification)(message, context);
|
||||
const smsNotification = await tryMakeSmsNotification(message, context);
|
||||
if (smsNotification) {
|
||||
notificationDatas.push(smsNotification);
|
||||
}
|
||||
return notificationDatas;
|
||||
};
|
||||
exports.smsHandler = smsHandler;
|
||||
async function tryMakeSmsNotification(message, context) {
|
||||
const { userId, type, entity, entityId, router } = message;
|
||||
(0, assert_1.default)(userId);
|
||||
const [mobile] = await context.select('mobile', {
|
||||
data: {
|
||||
id: 1,
|
||||
mobile: 1,
|
||||
},
|
||||
filter: {
|
||||
userId,
|
||||
},
|
||||
indexFrom: 0,
|
||||
count: 1,
|
||||
}, { dontCollect: true });
|
||||
if (mobile) {
|
||||
const converter = index_1.ConverterDict[type] && index_1.ConverterDict[type].toSms;
|
||||
if (converter) {
|
||||
const dispersedData = await converter(message, context);
|
||||
if (dispersedData) {
|
||||
return {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
data: dispersedData,
|
||||
channel: 'sms',
|
||||
data1: mobile,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
import { NotificationHandler } from './index';
|
||||
export declare const emailHandler: NotificationHandler;
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.emailHandler = void 0;
|
||||
const uuid_1 = require("oak-domain/lib/utils/uuid");
|
||||
const email_1 = require("../email");
|
||||
const emailHandler = async (notification, context) => {
|
||||
const { data, id } = notification;
|
||||
try {
|
||||
const result = await (0, email_1.sendEmail)(data, context);
|
||||
if (result?.success) {
|
||||
await context.operate('notification', {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'succeed',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
},
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
else {
|
||||
await context.operate('notification', {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'fail',
|
||||
data: {
|
||||
data2: {
|
||||
res: result?.error,
|
||||
},
|
||||
},
|
||||
filter: {
|
||||
id,
|
||||
},
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
await context.operate('notification', {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'fail',
|
||||
data: {
|
||||
data2: {
|
||||
res: err?.message,
|
||||
},
|
||||
},
|
||||
filter: {
|
||||
id,
|
||||
},
|
||||
}, { dontCollect: true });
|
||||
console.warn('发邮件消息失败', err);
|
||||
}
|
||||
};
|
||||
exports.emailHandler = emailHandler;
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import { EntityDict } from "../../oak-app-domain";
|
||||
import { BRC } from "../../types/RuntimeCxt";
|
||||
import { Channel } from "../../types/Message";
|
||||
export type NotificationHandler = (notification: EntityDict['notification']['OpSchema'], context: BRC<EntityDict>) => Promise<void>;
|
||||
export type NotificationFailureHandler = (message: EntityDict['message']['Schema'], context: BRC<EntityDict>) => Promise<number>;
|
||||
export declare function registerNotificationHandler(channel: Channel, handler: NotificationHandler): void;
|
||||
export declare function getNotificationHandler(channel: Channel): NotificationHandler;
|
||||
export declare function registerNotificationFailureHandler(handler: NotificationFailureHandler): void;
|
||||
export declare function getNotificationFailureHandlers(): NotificationFailureHandler[];
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.registerNotificationHandler = registerNotificationHandler;
|
||||
exports.getNotificationHandler = getNotificationHandler;
|
||||
exports.registerNotificationFailureHandler = registerNotificationFailureHandler;
|
||||
exports.getNotificationFailureHandlers = getNotificationFailureHandlers;
|
||||
const assert_1 = require("oak-domain/lib/utils/assert");
|
||||
const wechatMp_1 = require("./wechatMp");
|
||||
const wechatPublic_1 = require("./wechatPublic");
|
||||
const sms_1 = require("./sms");
|
||||
const email_1 = require("./email");
|
||||
const notificationHandlers = {};
|
||||
const notificationFailureHandlers = [];
|
||||
function registerNotificationHandler(channel, handler) {
|
||||
notificationHandlers[channel] = handler;
|
||||
}
|
||||
function getNotificationHandler(channel) {
|
||||
const handler = notificationHandlers[channel];
|
||||
(0, assert_1.assert)(handler, `通知渠道 ${channel} 的处理器未注册`);
|
||||
return handler;
|
||||
}
|
||||
function registerNotificationFailureHandler(handler) {
|
||||
notificationFailureHandlers.push(handler);
|
||||
}
|
||||
function getNotificationFailureHandlers() {
|
||||
return notificationFailureHandlers;
|
||||
}
|
||||
// 默认注册所有处理器
|
||||
registerNotificationHandler('wechatMp', wechatMp_1.wechatMpHandler);
|
||||
registerNotificationHandler('wechatPublic', wechatPublic_1.wechatPublicHandler);
|
||||
registerNotificationHandler('sms', sms_1.smsHandler);
|
||||
registerNotificationHandler('email', email_1.emailHandler);
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
import { NotificationHandler } from './index';
|
||||
export declare const smsHandler: NotificationHandler;
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.smsHandler = void 0;
|
||||
const uuid_1 = require("oak-domain/lib/utils/uuid");
|
||||
const sms_1 = require("../sms");
|
||||
const smsHandler = async (notification, context) => {
|
||||
const { data, data1, id } = notification;
|
||||
const [messageSystem] = await context.select('messageSystem', {
|
||||
data: {
|
||||
id: 1,
|
||||
message: {
|
||||
id: 1,
|
||||
type: 1,
|
||||
},
|
||||
},
|
||||
filter: {
|
||||
id: notification.messageSystemId,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
const { message } = messageSystem;
|
||||
const { type } = message;
|
||||
try {
|
||||
const result = await (0, sms_1.sendSms)({
|
||||
messageType: type,
|
||||
templateParam: data.params,
|
||||
mobile: data1.mobile,
|
||||
}, context);
|
||||
if (result?.success === true) {
|
||||
await context.operate('notification', {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'succeed',
|
||||
data: {
|
||||
data2: {
|
||||
res: result?.res || {}
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
else {
|
||||
await context.operate('notification', {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'fail',
|
||||
data: {
|
||||
data2: {
|
||||
res: result?.res || {}
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
await context.operate('notification', {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'fail',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
console.warn('发短信消息失败', err);
|
||||
}
|
||||
};
|
||||
exports.smsHandler = smsHandler;
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
import { NotificationHandler } from './index';
|
||||
export declare const wechatMpHandler: NotificationHandler;
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.wechatMpHandler = void 0;
|
||||
const tslib_1 = require("tslib");
|
||||
const uuid_1 = require("oak-domain/lib/utils/uuid");
|
||||
const WechatSDK_1 = tslib_1.__importDefault(require("oak-external-sdk/lib/WechatSDK"));
|
||||
const domain_1 = require("oak-domain/lib/utils/domain");
|
||||
const wechatMpHandler = async (notification, context) => {
|
||||
const { data, templateId, messageSystemId, data1, id } = notification;
|
||||
const [messageSystem] = await context.select('messageSystem', {
|
||||
data: {
|
||||
id: 1,
|
||||
messageId: 1,
|
||||
message: {
|
||||
id: 1,
|
||||
userId: 1,
|
||||
router: 1,
|
||||
type: 1,
|
||||
},
|
||||
system: {
|
||||
id: 1,
|
||||
application$system: {
|
||||
$entity: 'application',
|
||||
data: {
|
||||
id: 1,
|
||||
type: 1,
|
||||
config: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
id: messageSystemId,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
const { system, message } = messageSystem;
|
||||
const { router } = message;
|
||||
const { application$system: applications } = system;
|
||||
const app = applications.find(ele => ele.type === 'wechatMp');
|
||||
const { config } = app;
|
||||
const { appId, appSecret } = config;
|
||||
const instance = WechatSDK_1.default.getInstance(appId, 'wechatMp', appSecret);
|
||||
let page;
|
||||
if (router) {
|
||||
const pathname = router.pathname;
|
||||
const url = pathname.startsWith('/')
|
||||
? `pages${pathname}/index`
|
||||
: `pages/${pathname}/index`;
|
||||
page = (0, domain_1.composeUrl)(url, Object.assign({}, router.props, router.state));
|
||||
}
|
||||
// 根据当前环境决定消息推哪个版本
|
||||
const StateDict = {
|
||||
'development': 'developer',
|
||||
'staging': 'trial',
|
||||
'production': 'former',
|
||||
};
|
||||
try {
|
||||
await instance.sendSubscribedMessage({
|
||||
templateId: templateId,
|
||||
data: data,
|
||||
openId: data1.openId,
|
||||
page,
|
||||
state: StateDict[process.env.NODE_ENV],
|
||||
});
|
||||
await context.operate('notification', {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'succeed',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
catch (err) {
|
||||
console.warn('发微信小程序消息失败', err);
|
||||
await context.operate('notification', {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'fail',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
};
|
||||
exports.wechatMpHandler = wechatMpHandler;
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
import { NotificationHandler } from './index';
|
||||
export declare const wechatPublicHandler: NotificationHandler;
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.wechatPublicHandler = void 0;
|
||||
const tslib_1 = require("tslib");
|
||||
const uuid_1 = require("oak-domain/lib/utils/uuid");
|
||||
const WechatSDK_1 = tslib_1.__importDefault(require("oak-external-sdk/lib/WechatSDK"));
|
||||
const domain_1 = require("oak-domain/lib/utils/domain");
|
||||
const domain_2 = require("../domain");
|
||||
const wechatPublicHandler = async (notification, context) => {
|
||||
const { data, templateId, messageSystemId, data1, id } = notification;
|
||||
const [messageSystem] = await context.select('messageSystem', {
|
||||
data: {
|
||||
id: 1,
|
||||
messageId: 1,
|
||||
message: {
|
||||
id: 1,
|
||||
userId: 1,
|
||||
router: 1,
|
||||
type: 1,
|
||||
},
|
||||
system: {
|
||||
id: 1,
|
||||
application$system: {
|
||||
$entity: 'application',
|
||||
data: {
|
||||
id: 1,
|
||||
type: 1,
|
||||
config: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
id: messageSystemId,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
const { system, message } = messageSystem;
|
||||
const { router } = message;
|
||||
const { application$system: applications } = system;
|
||||
const app = applications.find(ele => ele.type === 'wechatPublic');
|
||||
const { config, id: applicationId } = app;
|
||||
const { appId, appSecret } = config;
|
||||
const [domain] = await context.select('domain', {
|
||||
data: {
|
||||
id: 1,
|
||||
url: 1,
|
||||
apiPath: 1,
|
||||
protocol: 1,
|
||||
port: 1,
|
||||
},
|
||||
filter: {
|
||||
system: {
|
||||
application$system: {
|
||||
id: applicationId,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, { dontCollect: true });
|
||||
const instance = WechatSDK_1.default.getInstance(appId, 'wechatPublic', appSecret);
|
||||
const { openId, wechatMpAppId } = data1;
|
||||
let page;
|
||||
// message 用户不需要跳转页面
|
||||
if (router) {
|
||||
const pathname = router.pathname;
|
||||
if (wechatMpAppId) {
|
||||
const url = pathname.startsWith('/')
|
||||
? `pages${pathname}/index`
|
||||
: `pages/${pathname}/index`;
|
||||
page = (0, domain_1.composeUrl)(url, Object.assign({}, router.props, router.state));
|
||||
}
|
||||
else {
|
||||
const url = (0, domain_2.composeDomainUrl)(domain, pathname);
|
||||
page = (0, domain_1.composeUrl)(url, Object.assign({}, router.props, router.state));
|
||||
}
|
||||
}
|
||||
try {
|
||||
await instance.sendTemplateMessage({
|
||||
openId,
|
||||
templateId: templateId,
|
||||
url: !wechatMpAppId ? page : undefined,
|
||||
data: data,
|
||||
miniProgram: wechatMpAppId
|
||||
? {
|
||||
appid: wechatMpAppId,
|
||||
pagepath: page,
|
||||
}
|
||||
: undefined,
|
||||
clientMsgId: id,
|
||||
});
|
||||
await context.operate('notification', {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'succeed',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
catch (err) {
|
||||
console.warn('发微信公众号消息失败', err);
|
||||
await context.operate('notification', {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'fail',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
};
|
||||
exports.wechatPublicHandler = wechatPublicHandler;
|
||||
|
|
@ -9,7 +9,7 @@ import { Schema as MessageSystem } from './MessageSystem';
|
|||
import { EntityDesc } from 'oak-domain/lib/types/EntityDesc';
|
||||
|
||||
export interface Schema extends EntityShape {
|
||||
channel: Channel,
|
||||
channel: String<32>,
|
||||
application?: Application,
|
||||
data?: Object,
|
||||
messageSystem: MessageSystem,
|
||||
|
|
@ -33,7 +33,7 @@ const IActionDef: ActionDef<IAction, IState> = {
|
|||
|
||||
export const entityDesc: EntityDesc<Schema, Action, '', {
|
||||
iState: IState;
|
||||
channel: Schema['channel']
|
||||
// channel: Schema['channel']
|
||||
}> = {
|
||||
locales: {
|
||||
zh_CN: {
|
||||
|
|
@ -58,14 +58,14 @@ export const entityDesc: EntityDesc<Schema, Action, '', {
|
|||
success: '发送成功',
|
||||
failure: '发送失败',
|
||||
},
|
||||
channel: {
|
||||
wechatPublic: '公众号',
|
||||
jPush: '极光推送',
|
||||
jim: '极光消息',
|
||||
wechatMp: '小程序',
|
||||
sms: '短信',
|
||||
email: '邮箱',
|
||||
}
|
||||
// channel: {
|
||||
// wechatPublic: '公众号',
|
||||
// jPush: '极光推送',
|
||||
// jim: '极光消息',
|
||||
// wechatMp: '小程序',
|
||||
// sms: '短信',
|
||||
// email: '邮箱',
|
||||
// }
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -80,14 +80,14 @@ export const entityDesc: EntityDesc<Schema, Action, '', {
|
|||
success: '#008000',
|
||||
failure: '#9A9A9A',
|
||||
},
|
||||
channel: {
|
||||
wechatMp: '#008000',
|
||||
jPush: '#0000FF',
|
||||
jim: '#0000FF',
|
||||
wechatPublic: '#008000',
|
||||
sms: '#000000',
|
||||
email: '#000000',
|
||||
},
|
||||
// channel: {
|
||||
// wechatMp: '#008000',
|
||||
// jPush: '#0000FF',
|
||||
// jim: '#0000FF',
|
||||
// wechatPublic: '#008000',
|
||||
// sms: '#000000',
|
||||
// email: '#000000',
|
||||
// },
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -16,11 +16,36 @@ export {
|
|||
} from './endpoints/wechat';
|
||||
|
||||
export {
|
||||
// 注册消息通知转换器trigger
|
||||
/**
|
||||
* 注册消息通知转换器
|
||||
* 用于将消息数据转换为特定渠道所需的格式
|
||||
* 例如: 将message转换为微信小程序、公众号、短信、邮件所需的数据格式
|
||||
*/
|
||||
registerMessageNotificationConverters,
|
||||
/**
|
||||
* 注册消息渠道处理器
|
||||
* 用于处理特定渠道的消息创建逻辑
|
||||
* 例如: 处理微信小程序、公众号、短信、邮件等渠道的消息生成
|
||||
*/
|
||||
registerMessageHandler,
|
||||
} from './utils/message';
|
||||
|
||||
export {
|
||||
/**
|
||||
* 注册通知渠道处理器
|
||||
* 用于处理特定渠道的通知发送逻辑
|
||||
* 例如: 实际发送微信小程序、公众号、短信、邮件等渠道的通知
|
||||
*/
|
||||
registerNotificationHandler,
|
||||
/**
|
||||
* 注册通知失败处理器
|
||||
* 用于在所有通知渠道都失败后执行自定义的补救逻辑
|
||||
* 可以注册多个处理器,它们会依次执行
|
||||
* 例如: 在其他渠道失败后自动发送短信通知
|
||||
*/
|
||||
registerNotificationFailureHandler,
|
||||
} from './utils/notification';
|
||||
|
||||
export {
|
||||
// 注册短信服务商实现
|
||||
registSms,
|
||||
|
|
|
|||
|
|
@ -5,11 +5,8 @@ import { CreateOperationData as CreateMessageData } from '../oak-app-domain/Mess
|
|||
import { assert } from 'oak-domain/lib/utils/assert';
|
||||
import { BRC } from '../types/RuntimeCxt';
|
||||
import { Channel, Weight } from '../types/Message';
|
||||
import { BackendRuntimeContext } from '../context/BackendRuntimeContext';
|
||||
import { uniqBy } from 'oak-domain/lib/utils/lodash';
|
||||
import { Router } from '../entities/Message';
|
||||
import { ConverterDict, getMessageHandler } from '../utils/message';
|
||||
|
||||
import { getMessageHandler } from '../utils/message';
|
||||
|
||||
const InitialChannelByWeightMatrix: Record<Weight, Channel[]> = {
|
||||
high: ['wechatMp', 'wechatPublic', 'sms', 'email'],
|
||||
|
|
@ -17,69 +14,6 @@ const InitialChannelByWeightMatrix: Record<Weight, Channel[]> = {
|
|||
low: ['wechatMp', 'wechatPublic', 'email'],
|
||||
};
|
||||
|
||||
export async function tryMakeSmsNotification(message: {
|
||||
userId?: string;
|
||||
type?: string;
|
||||
entity?: string;
|
||||
router?: Router | null;
|
||||
entityId?: string;
|
||||
}, context: BackendRuntimeContext<EntityDict>) {
|
||||
const { userId, type, entity, entityId, router } = message;
|
||||
assert(userId);
|
||||
const [mobile] = await context.select('mobile', {
|
||||
data: {
|
||||
id: 1,
|
||||
mobile: 1,
|
||||
},
|
||||
filter: {
|
||||
userId,
|
||||
},
|
||||
indexFrom: 0,
|
||||
count: 1,
|
||||
}, { dontCollect: true });
|
||||
if (mobile) {
|
||||
const converter = ConverterDict[type!] && ConverterDict[type!].toSms;
|
||||
if (converter) {
|
||||
const dispersedData = await converter(message as EntityDict['message']['OpSchema'], context);
|
||||
if (dispersedData) {
|
||||
return {
|
||||
id: await generateNewIdAsync(),
|
||||
data: dispersedData,
|
||||
channel: 'sms',
|
||||
data1: mobile,
|
||||
} as any;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function tryMakeEmailNotification(
|
||||
message: {
|
||||
userId?: string;
|
||||
type?: string;
|
||||
entity?: string;
|
||||
router?: Router | null;
|
||||
entityId?: string;
|
||||
},
|
||||
context: BackendRuntimeContext<EntityDict>
|
||||
) {
|
||||
const { userId, type, entity, entityId, router } = message;
|
||||
const converter = ConverterDict[type!] && ConverterDict[type!].toEmail;
|
||||
if (converter) {
|
||||
const dispersedData = await converter(
|
||||
message as EntityDict['message']['OpSchema'],
|
||||
context
|
||||
);
|
||||
if (dispersedData) {
|
||||
return {
|
||||
id: await generateNewIdAsync(),
|
||||
data: dispersedData,
|
||||
channel: 'email',
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function createNotification(message: CreateMessageData, context: BRC<EntityDict>) {
|
||||
const { restriction, userId, weight, type, entity, entityId, platformId, channels } = message;
|
||||
assert(userId);
|
||||
|
|
|
|||
|
|
@ -3,307 +3,15 @@ import { EntityDict } from '../oak-app-domain/EntityDict';
|
|||
import { CreateOperationData as CreateNotificationData } from '../oak-app-domain/Notification/Schema';
|
||||
import { assert } from 'oak-domain/lib/utils/assert';
|
||||
import { BRC } from '../types/RuntimeCxt';
|
||||
import { WechatMpConfig, WechatPublicConfig, WebConfig } from '../oak-app-domain/Application/Schema';
|
||||
import WechatSDK, { WechatMpInstance, WechatPublicInstance } from 'oak-external-sdk/lib/WechatSDK';
|
||||
import { composeUrl } from 'oak-domain/lib/utils/domain';
|
||||
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
||||
import { sendSms } from '../utils/sms';
|
||||
import { sendEmail } from '../utils/email';
|
||||
import { tryMakeSmsNotification } from './message';
|
||||
import { composeDomainUrl } from '../utils/domain';
|
||||
import { tryMakeSmsNotification } from '../utils/message/sms';
|
||||
import { getNotificationHandler, getNotificationFailureHandlers } from '../utils/notification';
|
||||
|
||||
async function sendNotification(notification: EntityDict['notification']['OpSchema'], context: BRC<EntityDict>) {
|
||||
const { data, templateId, channel, messageSystemId, data1, id } = notification;
|
||||
const [messageSystem] = await context.select('messageSystem', {
|
||||
data: {
|
||||
id: 1,
|
||||
messageId: 1,
|
||||
message: {
|
||||
id: 1,
|
||||
userId: 1,
|
||||
router: 1,
|
||||
type: 1,
|
||||
},
|
||||
system: {
|
||||
id: 1,
|
||||
application$system: {
|
||||
$entity: 'application',
|
||||
data: {
|
||||
id: 1,
|
||||
type: 1,
|
||||
config: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
id: messageSystemId,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
const { system, message } = messageSystem!;
|
||||
const { router, userId, type } = message!;
|
||||
const { application$system: applications, config } = system!;
|
||||
switch (channel) {
|
||||
case 'wechatMp': {
|
||||
const app = applications!.find(
|
||||
ele => ele.type === 'wechatMp'
|
||||
);
|
||||
const { config } = app!;
|
||||
const { appId, appSecret } = config as WechatMpConfig;
|
||||
const instance = WechatSDK.getInstance(appId!, 'wechatMp', appSecret) as WechatMpInstance;
|
||||
let page;
|
||||
if (router) {
|
||||
const pathname = router.pathname;
|
||||
const url = pathname.startsWith('/')
|
||||
? `pages${pathname}/index`
|
||||
: `pages/${pathname}/index`;
|
||||
page = composeUrl(
|
||||
url,
|
||||
Object.assign({}, router!.props!, router!.state!)
|
||||
);
|
||||
}
|
||||
|
||||
// 根据当前环境决定消息推哪个版本
|
||||
const StateDict = {
|
||||
'development': 'developer',
|
||||
'staging': 'trial',
|
||||
'production': 'former',
|
||||
}
|
||||
try {
|
||||
await instance.sendSubscribedMessage({
|
||||
templateId: templateId!,
|
||||
data: data!,
|
||||
openId: (data1 as { openId: string }).openId, // 在notification创建时就赋值了
|
||||
page,
|
||||
state: StateDict[process.env.NODE_ENV as 'development'] as 'developer',
|
||||
});
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'succeed',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
return 1;
|
||||
}
|
||||
catch (err) {
|
||||
console.warn('发微信小程序消息失败', err);
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'fail',
|
||||
data: {
|
||||
},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
case 'wechatPublic': {
|
||||
const app = applications!.find(
|
||||
ele => ele.type === 'wechatPublic'
|
||||
);
|
||||
const { config, id: applicationId } = app!;
|
||||
const { appId, appSecret } = config as WechatPublicConfig;
|
||||
const [domain] = await context.select(
|
||||
'domain',
|
||||
{
|
||||
data: {
|
||||
id: 1,
|
||||
url: 1,
|
||||
apiPath: 1,
|
||||
protocol: 1,
|
||||
port: 1,
|
||||
},
|
||||
filter: {
|
||||
system: {
|
||||
application$system: {
|
||||
id: applicationId,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{ dontCollect: true }
|
||||
);
|
||||
const instance = WechatSDK.getInstance(appId!, 'wechatPublic', appSecret) as WechatPublicInstance;
|
||||
const { openId, wechatMpAppId } = data1 as {
|
||||
openId: string,
|
||||
wechatMpAppId?: string,
|
||||
};
|
||||
|
||||
let page;
|
||||
// message 用户不需要跳转页面
|
||||
if (router) {
|
||||
const pathname = router.pathname;
|
||||
|
||||
if (wechatMpAppId) {
|
||||
const url = pathname.startsWith('/')
|
||||
? `pages${pathname}/index`
|
||||
: `pages/${pathname}/index`;
|
||||
|
||||
page = composeUrl(
|
||||
url,
|
||||
Object.assign({}, router!.props!, router!.state!)
|
||||
);
|
||||
} else {
|
||||
const url = composeDomainUrl(
|
||||
domain as EntityDict['domain']['Schema'],
|
||||
pathname
|
||||
);
|
||||
page = composeUrl(
|
||||
url,
|
||||
Object.assign({}, router!.props!, router!.state!)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
await instance.sendTemplateMessage({
|
||||
openId,
|
||||
templateId: templateId!,
|
||||
url: !wechatMpAppId ? page : undefined,
|
||||
data: data!,
|
||||
miniProgram: wechatMpAppId
|
||||
? {
|
||||
appid: wechatMpAppId,
|
||||
pagepath: page as string,
|
||||
}
|
||||
: undefined,
|
||||
clientMsgId: id,
|
||||
});
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'succeed',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
return 1;
|
||||
}
|
||||
catch (err) {
|
||||
console.warn('发微信公众号消息失败', err);
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'fail',
|
||||
data: {
|
||||
},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
case 'email': {
|
||||
try {
|
||||
const result = await sendEmail(data as any, context);
|
||||
if (result?.success) {
|
||||
await context.operate(
|
||||
'notification',
|
||||
{
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'succeed',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
},
|
||||
},
|
||||
{ dontCollect: true }
|
||||
);
|
||||
} else {
|
||||
await context.operate(
|
||||
'notification',
|
||||
{
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'fail',
|
||||
data: {
|
||||
data2: {
|
||||
res: result?.error,
|
||||
},
|
||||
},
|
||||
filter: {
|
||||
id,
|
||||
},
|
||||
},
|
||||
{ dontCollect: true }
|
||||
);
|
||||
}
|
||||
return 1;
|
||||
} catch (err: any) {
|
||||
await context.operate(
|
||||
'notification',
|
||||
{
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'fail',
|
||||
data: {
|
||||
data2: {
|
||||
res: err?.message,
|
||||
},
|
||||
},
|
||||
filter: {
|
||||
id,
|
||||
},
|
||||
},
|
||||
{ dontCollect: true }
|
||||
);
|
||||
console.warn('发邮件消息失败', err);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
default: {
|
||||
assert(channel === 'sms');
|
||||
try {
|
||||
const result = await sendSms({
|
||||
messageType: type!,
|
||||
templateParam: (data as { params: any }).params!,
|
||||
mobile: (data1 as { mobile: string }).mobile,
|
||||
}, context)
|
||||
if (result?.success === true) {
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'succeed',
|
||||
data: {
|
||||
data2: {
|
||||
res: result?.res || {}
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
} else {
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'fail',
|
||||
data: {
|
||||
data2: {
|
||||
res: result?.res || {}
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
} catch (err) {
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'fail',
|
||||
data: {
|
||||
},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
console.warn('发短信消息失败', err);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
const { channel } = notification;
|
||||
const handler = getNotificationHandler(channel!);
|
||||
await handler(notification, context);
|
||||
return 1;
|
||||
}
|
||||
|
||||
async function tryCreateSmsNotification(message: EntityDict['message']['Schema'], context: BRC<EntityDict>) {
|
||||
|
|
@ -473,8 +181,26 @@ const triggers: Trigger<EntityDict, 'notification', BRC<EntityDict>>[] = [
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (message.weight === 'medium' && !smsTried && allFailed) {
|
||||
// 中级的消息,在其它途径都失败的情况下再发短信
|
||||
// 获取所有注册的失败处理器
|
||||
const failureHandlers = getNotificationFailureHandlers();
|
||||
|
||||
if (failureHandlers.length > 0) {
|
||||
// 如果有注册的失败处理器,执行所有处理器
|
||||
let totalResult = 0;
|
||||
for (const handler of failureHandlers) {
|
||||
try {
|
||||
const result = await handler(message as EntityDict['message']['Schema'], context);
|
||||
totalResult += result;
|
||||
} catch (err) {
|
||||
console.error('执行notification失败处理器时出错:', err);
|
||||
}
|
||||
}
|
||||
if (totalResult > 0) {
|
||||
closeRootMode();
|
||||
return totalResult;
|
||||
}
|
||||
} else if (message.weight === 'medium' && !smsTried && allFailed) {
|
||||
// 如果没有注册处理器,走原有逻辑:中级的消息,在其它途径都失败的情况下再发短信
|
||||
const result = await tryCreateSmsNotification(
|
||||
message as EntityDict['message']['Schema'],
|
||||
context
|
||||
|
|
@ -482,6 +208,7 @@ const triggers: Trigger<EntityDict, 'notification', BRC<EntityDict>>[] = [
|
|||
closeRootMode();
|
||||
return result;
|
||||
}
|
||||
|
||||
// 标识消息发送失败
|
||||
if (allFailed) {
|
||||
await context.operate(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
||||
import BackendRuntimeContext from '../../context/BackendRuntimeContext';
|
||||
import { Router } from '../../entities/Message';
|
||||
import { EntityDict } from '../../oak-app-domain';
|
||||
import { MessageHandler } from './index';
|
||||
import { tryMakeEmailNotification } from '../../triggers/message';
|
||||
import { ConverterDict, MessageHandler } from './index';
|
||||
|
||||
export const emailHandler: MessageHandler = async ({ message, applications, system, messageTypeTemplates, context }) => {
|
||||
const notificationDatas: Omit<EntityDict['notification']['CreateOperationData'], 'messageSystemId'>[] = [];
|
||||
|
|
@ -14,3 +16,31 @@ export const emailHandler: MessageHandler = async ({ message, applications, syst
|
|||
|
||||
return notificationDatas;
|
||||
};
|
||||
|
||||
|
||||
export async function tryMakeEmailNotification(
|
||||
message: {
|
||||
userId?: string;
|
||||
type?: string;
|
||||
entity?: string;
|
||||
router?: Router | null;
|
||||
entityId?: string;
|
||||
},
|
||||
context: BackendRuntimeContext<EntityDict>
|
||||
) {
|
||||
const { userId, type, entity, entityId, router } = message;
|
||||
const converter = ConverterDict[type!] && ConverterDict[type!].toEmail;
|
||||
if (converter) {
|
||||
const dispersedData = await converter(
|
||||
message as EntityDict['message']['OpSchema'],
|
||||
context
|
||||
);
|
||||
if (dispersedData) {
|
||||
return {
|
||||
id: await generateNewIdAsync(),
|
||||
data: dispersedData,
|
||||
channel: 'email',
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,10 @@ import { CreateOperationData as CreateMessageData } from '../../oak-app-domain/M
|
|||
import { Channel, MessageNotificationConverter } from "../../types/Message";
|
||||
import { assert } from "oak-domain/lib/utils/assert";
|
||||
import BackendRuntimeContext from "../../context/BackendRuntimeContext";
|
||||
import { wechatMpHandler } from './wechatMp';
|
||||
import { wechatPublicHandler } from './wechatPublic';
|
||||
import { smsHandler } from './sms';
|
||||
import { emailHandler } from './email';
|
||||
|
||||
export type MessageHandler = (options: {
|
||||
message: CreateMessageData,
|
||||
|
|
@ -33,3 +37,9 @@ export function getMessageHandler(channel: Channel): MessageHandler {
|
|||
assert(handler, `消息渠道 ${channel} 的处理器未注册`);
|
||||
return handler;
|
||||
}
|
||||
|
||||
// 默认注册所有处理器
|
||||
registerMessageHandler('wechatMp', wechatMpHandler);
|
||||
registerMessageHandler('wechatPublic', wechatPublicHandler);
|
||||
registerMessageHandler('sms', smsHandler);
|
||||
registerMessageHandler('email', emailHandler);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import assert from 'assert';
|
||||
import BackendRuntimeContext from '../../context/BackendRuntimeContext';
|
||||
import { Router } from '../../entities/Message';
|
||||
import { EntityDict } from '../../oak-app-domain';
|
||||
import { MessageHandler } from './index';
|
||||
import { tryMakeSmsNotification } from '../../triggers/message';
|
||||
import { ConverterDict, MessageHandler } from './index';
|
||||
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
||||
|
||||
export const smsHandler: MessageHandler = async ({ message, applications, system, messageTypeTemplates, context }) => {
|
||||
const notificationDatas: Omit<EntityDict['notification']['CreateOperationData'], 'messageSystemId'>[] = [];
|
||||
|
|
@ -12,3 +15,39 @@ export const smsHandler: MessageHandler = async ({ message, applications, system
|
|||
|
||||
return notificationDatas;
|
||||
};
|
||||
|
||||
export async function tryMakeSmsNotification(message: {
|
||||
userId?: string;
|
||||
type?: string;
|
||||
entity?: string;
|
||||
router?: Router | null;
|
||||
entityId?: string;
|
||||
}, context: BackendRuntimeContext<EntityDict>) {
|
||||
const { userId, type, entity, entityId, router } = message;
|
||||
assert(userId);
|
||||
const [mobile] = await context.select('mobile', {
|
||||
data: {
|
||||
id: 1,
|
||||
mobile: 1,
|
||||
},
|
||||
filter: {
|
||||
userId,
|
||||
},
|
||||
indexFrom: 0,
|
||||
count: 1,
|
||||
}, { dontCollect: true });
|
||||
if (mobile) {
|
||||
const converter = ConverterDict[type!] && ConverterDict[type!].toSms;
|
||||
if (converter) {
|
||||
const dispersedData = await converter(message as EntityDict['message']['OpSchema'], context);
|
||||
if (dispersedData) {
|
||||
return {
|
||||
id: await generateNewIdAsync(),
|
||||
data: dispersedData,
|
||||
channel: 'sms',
|
||||
data1: mobile,
|
||||
} as any;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
||||
import { EntityDict } from '../../oak-app-domain';
|
||||
import { BRC } from '../../types/RuntimeCxt';
|
||||
import { sendEmail } from '../email';
|
||||
import { NotificationHandler } from './index';
|
||||
|
||||
export const emailHandler: NotificationHandler = async (notification, context) => {
|
||||
const { data, id } = notification;
|
||||
|
||||
try {
|
||||
const result = await sendEmail(data as any, context);
|
||||
if (result?.success) {
|
||||
await context.operate(
|
||||
'notification',
|
||||
{
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'succeed',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
},
|
||||
},
|
||||
{ dontCollect: true }
|
||||
);
|
||||
} else {
|
||||
await context.operate(
|
||||
'notification',
|
||||
{
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'fail',
|
||||
data: {
|
||||
data2: {
|
||||
res: result?.error,
|
||||
},
|
||||
},
|
||||
filter: {
|
||||
id,
|
||||
},
|
||||
},
|
||||
{ dontCollect: true }
|
||||
);
|
||||
}
|
||||
} catch (err: any) {
|
||||
await context.operate(
|
||||
'notification',
|
||||
{
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'fail',
|
||||
data: {
|
||||
data2: {
|
||||
res: err?.message,
|
||||
},
|
||||
},
|
||||
filter: {
|
||||
id,
|
||||
},
|
||||
},
|
||||
{ dontCollect: true }
|
||||
);
|
||||
console.warn('发邮件消息失败', err);
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
import { EntityDict } from "../../oak-app-domain";
|
||||
import { BRC } from "../../types/RuntimeCxt";
|
||||
import { Channel } from "../../types/Message";
|
||||
import { assert } from "oak-domain/lib/utils/assert";
|
||||
import { wechatMpHandler } from './wechatMp';
|
||||
import { wechatPublicHandler } from './wechatPublic';
|
||||
import { smsHandler } from './sms';
|
||||
import { emailHandler } from './email';
|
||||
|
||||
export type NotificationHandler = (
|
||||
notification: EntityDict['notification']['OpSchema'],
|
||||
context: BRC<EntityDict>
|
||||
) => Promise<void>;
|
||||
|
||||
export type NotificationFailureHandler = (
|
||||
message: EntityDict['message']['Schema'],
|
||||
context: BRC<EntityDict>
|
||||
) => Promise<number>;
|
||||
|
||||
const notificationHandlers: Record<Channel, NotificationHandler> = {} as Record<Channel, NotificationHandler>;
|
||||
const notificationFailureHandlers: NotificationFailureHandler[] = [];
|
||||
|
||||
export function registerNotificationHandler(channel: Channel, handler: NotificationHandler) {
|
||||
notificationHandlers[channel] = handler;
|
||||
}
|
||||
|
||||
export function getNotificationHandler(channel: Channel): NotificationHandler {
|
||||
const handler = notificationHandlers[channel];
|
||||
assert(handler, `通知渠道 ${channel} 的处理器未注册`);
|
||||
return handler;
|
||||
}
|
||||
|
||||
export function registerNotificationFailureHandler(handler: NotificationFailureHandler) {
|
||||
notificationFailureHandlers.push(handler);
|
||||
}
|
||||
|
||||
export function getNotificationFailureHandlers(): NotificationFailureHandler[] {
|
||||
return notificationFailureHandlers;
|
||||
}
|
||||
|
||||
// 默认注册所有处理器
|
||||
registerNotificationHandler('wechatMp', wechatMpHandler);
|
||||
registerNotificationHandler('wechatPublic', wechatPublicHandler);
|
||||
registerNotificationHandler('sms', smsHandler);
|
||||
registerNotificationHandler('email', emailHandler);
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
||||
import { EntityDict } from '../../oak-app-domain';
|
||||
import { BRC } from '../../types/RuntimeCxt';
|
||||
import { sendSms } from '../sms';
|
||||
import { NotificationHandler } from './index';
|
||||
|
||||
export const smsHandler: NotificationHandler = async (notification, context) => {
|
||||
const { data, data1, id } = notification;
|
||||
|
||||
const [messageSystem] = await context.select('messageSystem', {
|
||||
data: {
|
||||
id: 1,
|
||||
message: {
|
||||
id: 1,
|
||||
type: 1,
|
||||
},
|
||||
},
|
||||
filter: {
|
||||
id: notification.messageSystemId,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
|
||||
const { message } = messageSystem!;
|
||||
const { type } = message!;
|
||||
|
||||
try {
|
||||
const result = await sendSms({
|
||||
messageType: type!,
|
||||
templateParam: (data as { params: any }).params!,
|
||||
mobile: (data1 as { mobile: string }).mobile,
|
||||
}, context);
|
||||
|
||||
if (result?.success === true) {
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'succeed',
|
||||
data: {
|
||||
data2: {
|
||||
res: result?.res || {}
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
} else {
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'fail',
|
||||
data: {
|
||||
data2: {
|
||||
res: result?.res || {}
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
} catch (err) {
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'fail',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
console.warn('发短信消息失败', err);
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
||||
import { EntityDict } from '../../oak-app-domain';
|
||||
import { BRC } from '../../types/RuntimeCxt';
|
||||
import { WechatMpConfig } from '../../oak-app-domain/Application/Schema';
|
||||
import WechatSDK, { WechatMpInstance } from 'oak-external-sdk/lib/WechatSDK';
|
||||
import { composeUrl } from 'oak-domain/lib/utils/domain';
|
||||
import { NotificationHandler } from './index';
|
||||
|
||||
export const wechatMpHandler: NotificationHandler = async (notification, context) => {
|
||||
const { data, templateId, messageSystemId, data1, id } = notification;
|
||||
|
||||
const [messageSystem] = await context.select('messageSystem', {
|
||||
data: {
|
||||
id: 1,
|
||||
messageId: 1,
|
||||
message: {
|
||||
id: 1,
|
||||
userId: 1,
|
||||
router: 1,
|
||||
type: 1,
|
||||
},
|
||||
system: {
|
||||
id: 1,
|
||||
application$system: {
|
||||
$entity: 'application',
|
||||
data: {
|
||||
id: 1,
|
||||
type: 1,
|
||||
config: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
id: messageSystemId,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
|
||||
const { system, message } = messageSystem!;
|
||||
const { router } = message!;
|
||||
const { application$system: applications } = system!;
|
||||
|
||||
const app = applications!.find(
|
||||
ele => ele.type === 'wechatMp'
|
||||
);
|
||||
const { config } = app!;
|
||||
const { appId, appSecret } = config as WechatMpConfig;
|
||||
const instance = WechatSDK.getInstance(appId!, 'wechatMp', appSecret) as WechatMpInstance;
|
||||
|
||||
let page;
|
||||
if (router) {
|
||||
const pathname = router.pathname;
|
||||
const url = pathname.startsWith('/')
|
||||
? `pages${pathname}/index`
|
||||
: `pages/${pathname}/index`;
|
||||
page = composeUrl(
|
||||
url,
|
||||
Object.assign({}, router!.props!, router!.state!)
|
||||
);
|
||||
}
|
||||
|
||||
// 根据当前环境决定消息推哪个版本
|
||||
const StateDict = {
|
||||
'development': 'developer',
|
||||
'staging': 'trial',
|
||||
'production': 'former',
|
||||
}
|
||||
|
||||
try {
|
||||
await instance.sendSubscribedMessage({
|
||||
templateId: templateId!,
|
||||
data: data!,
|
||||
openId: (data1 as { openId: string }).openId,
|
||||
page,
|
||||
state: StateDict[process.env.NODE_ENV as 'development'] as 'developer',
|
||||
});
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'succeed',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
} catch (err) {
|
||||
console.warn('发微信小程序消息失败', err);
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'fail',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
||||
import { EntityDict } from '../../oak-app-domain';
|
||||
import { BRC } from '../../types/RuntimeCxt';
|
||||
import { WechatPublicConfig } from '../../oak-app-domain/Application/Schema';
|
||||
import WechatSDK, { WechatPublicInstance } from 'oak-external-sdk/lib/WechatSDK';
|
||||
import { composeUrl } from 'oak-domain/lib/utils/domain';
|
||||
import { composeDomainUrl } from '../domain';
|
||||
import { NotificationHandler } from './index';
|
||||
|
||||
export const wechatPublicHandler: NotificationHandler = async (notification, context) => {
|
||||
const { data, templateId, messageSystemId, data1, id } = notification;
|
||||
|
||||
const [messageSystem] = await context.select('messageSystem', {
|
||||
data: {
|
||||
id: 1,
|
||||
messageId: 1,
|
||||
message: {
|
||||
id: 1,
|
||||
userId: 1,
|
||||
router: 1,
|
||||
type: 1,
|
||||
},
|
||||
system: {
|
||||
id: 1,
|
||||
application$system: {
|
||||
$entity: 'application',
|
||||
data: {
|
||||
id: 1,
|
||||
type: 1,
|
||||
config: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
id: messageSystemId,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
|
||||
const { system, message } = messageSystem!;
|
||||
const { router } = message!;
|
||||
const { application$system: applications } = system!;
|
||||
|
||||
const app = applications!.find(
|
||||
ele => ele.type === 'wechatPublic'
|
||||
);
|
||||
const { config, id: applicationId } = app!;
|
||||
const { appId, appSecret } = config as WechatPublicConfig;
|
||||
|
||||
const [domain] = await context.select(
|
||||
'domain',
|
||||
{
|
||||
data: {
|
||||
id: 1,
|
||||
url: 1,
|
||||
apiPath: 1,
|
||||
protocol: 1,
|
||||
port: 1,
|
||||
},
|
||||
filter: {
|
||||
system: {
|
||||
application$system: {
|
||||
id: applicationId,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{ dontCollect: true }
|
||||
);
|
||||
|
||||
const instance = WechatSDK.getInstance(appId!, 'wechatPublic', appSecret) as WechatPublicInstance;
|
||||
const { openId, wechatMpAppId } = data1 as {
|
||||
openId: string,
|
||||
wechatMpAppId?: string,
|
||||
};
|
||||
|
||||
let page;
|
||||
// message 用户不需要跳转页面
|
||||
if (router) {
|
||||
const pathname = router.pathname;
|
||||
|
||||
if (wechatMpAppId) {
|
||||
const url = pathname.startsWith('/')
|
||||
? `pages${pathname}/index`
|
||||
: `pages/${pathname}/index`;
|
||||
|
||||
page = composeUrl(
|
||||
url,
|
||||
Object.assign({}, router!.props!, router!.state!)
|
||||
);
|
||||
} else {
|
||||
const url = composeDomainUrl(
|
||||
domain as EntityDict['domain']['Schema'],
|
||||
pathname
|
||||
);
|
||||
page = composeUrl(
|
||||
url,
|
||||
Object.assign({}, router!.props!, router!.state!)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await instance.sendTemplateMessage({
|
||||
openId,
|
||||
templateId: templateId!,
|
||||
url: !wechatMpAppId ? page : undefined,
|
||||
data: data!,
|
||||
miniProgram: wechatMpAppId
|
||||
? {
|
||||
appid: wechatMpAppId,
|
||||
pagepath: page as string,
|
||||
}
|
||||
: undefined,
|
||||
clientMsgId: id,
|
||||
});
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'succeed',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
} catch (err) {
|
||||
console.warn('发微信公众号消息失败', err);
|
||||
await context.operate('notification', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'fail',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
}
|
||||
};
|
||||
Loading…
Reference in New Issue