extraFile-gallery在创建filter的时候有bug
This commit is contained in:
parent
bb911c759a
commit
d9a73291ac
|
|
@ -21,7 +21,7 @@ function createWechatQrCode(options, context) {
|
|||
return tslib_1.__generator(this, function (_f) {
|
||||
switch (_f.label) {
|
||||
case 0:
|
||||
entity = options.entity, entityId = options.entityId, tag = options.tag, _a = options.lifetimeLength, lifetimeLength = _a === void 0 ? 300 * 10000 : _a, _b = options.permanent, permanent = _b === void 0 ? false : _b, props = options.props;
|
||||
entity = options.entity, entityId = options.entityId, tag = options.tag, _a = options.lifetimeLength, lifetimeLength = _a === void 0 ? 2592000 * 1000 : _a, _b = options.permanent, permanent = _b === void 0 ? false : _b, props = options.props;
|
||||
applicationId = context.getApplicationId();
|
||||
(0, assert_1.assert)(applicationId);
|
||||
return [4 /*yield*/, context.select('system', {
|
||||
|
|
@ -109,6 +109,10 @@ function createWechatQrCode(options, context) {
|
|||
if (!appId || !appType) {
|
||||
throw new Error('无法生成二维码,找不到此system下的服务号或者小程序信息');
|
||||
}
|
||||
if (lifetimeLength > 2592000 * 1000 && !permanent) {
|
||||
// 二维码的过期应当由上层对象来自主控制以保持一致性
|
||||
throw new Error('二维码过期时间太长,不能长于30天');
|
||||
}
|
||||
data = {
|
||||
id: id,
|
||||
type: appType,
|
||||
|
|
@ -127,7 +131,6 @@ function createWechatQrCode(options, context) {
|
|||
application = applications.find(function (ele) { return ele.id === data.applicationId; });
|
||||
(0, assert_1.assert)(application);
|
||||
applicationType = application.type, config = application.config;
|
||||
console.log(process.env.OAK_PLATFORM, process.env.NODE_ENV);
|
||||
_e = type;
|
||||
switch (_e) {
|
||||
case 'wechatMpWxaCode': return [3 /*break*/, 2];
|
||||
|
|
|
|||
|
|
@ -45,6 +45,22 @@ exports.default = OakComponent({
|
|||
wechatMp: {
|
||||
externalClasses: ['oak-class', 'oak-item-class'],
|
||||
},
|
||||
filters: [
|
||||
{
|
||||
filter: function (_a) {
|
||||
var props = _a.props;
|
||||
var tag1 = props.tag1, tag2 = props.tag2;
|
||||
var filter1 = {};
|
||||
if (tag1) {
|
||||
Object.assign(filter1, { tag1: tag1 });
|
||||
}
|
||||
if (tag2) {
|
||||
Object.assign(filter1, { tag2: tag2 });
|
||||
}
|
||||
return filter1;
|
||||
}
|
||||
}
|
||||
],
|
||||
properties: {
|
||||
removeLater: Boolean,
|
||||
autoUpload: {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ export declare class BackendRuntimeContext<ED extends EntityDict> extends AsyncC
|
|||
protected amIReallyRoot?: boolean;
|
||||
protected rootMode?: boolean;
|
||||
setTokenValue(tokenValue: string): Promise<void>;
|
||||
setApplication(appId: string): Promise<void>;
|
||||
protected initialize(data?: SerializedData): Promise<void>;
|
||||
getApplicationId(): ED["application"]["Schema"]["id"] | undefined;
|
||||
getSystemId(): ED["application"]["Schema"]["systemId"] | undefined;
|
||||
|
|
|
|||
|
|
@ -87,22 +87,12 @@ var BackendRuntimeContext = /** @class */ (function (_super) {
|
|||
});
|
||||
});
|
||||
};
|
||||
BackendRuntimeContext.prototype.initialize = function (data) {
|
||||
BackendRuntimeContext.prototype.setApplication = function (appId) {
|
||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||
var appId, tokenValue, result, err_1;
|
||||
var result;
|
||||
return tslib_1.__generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
if (!data) return [3 /*break*/, 11];
|
||||
return [4 /*yield*/, this.begin()];
|
||||
case 1:
|
||||
_a.sent();
|
||||
_a.label = 2;
|
||||
case 2:
|
||||
_a.trys.push([2, 8, , 10]);
|
||||
appId = data.a, tokenValue = data.t;
|
||||
if (!appId) return [3 /*break*/, 4];
|
||||
return [4 /*yield*/, this.select('application', {
|
||||
case 0: return [4 /*yield*/, this.select('application', {
|
||||
data: {
|
||||
id: 1,
|
||||
name: 1,
|
||||
|
|
@ -132,33 +122,57 @@ var BackendRuntimeContext = /** @class */ (function (_super) {
|
|||
dontCollect: true,
|
||||
blockTrigger: true,
|
||||
})];
|
||||
case 3:
|
||||
case 1:
|
||||
result = _a.sent();
|
||||
(0, assert_1.default)(result.length > 0, "\u6784\u5EFABackendRuntimeContext\u5BF9\u5E94appId\u300C".concat(appId, "\u300D\u627E\u4E0D\u5230application"));
|
||||
this.application = result[0];
|
||||
return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
BackendRuntimeContext.prototype.initialize = function (data) {
|
||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||
var appId, tokenValue, promises, err_1;
|
||||
return tslib_1.__generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
if (!data) return [3 /*break*/, 9];
|
||||
return [4 /*yield*/, this.begin()];
|
||||
case 1:
|
||||
_a.sent();
|
||||
_a.label = 2;
|
||||
case 2:
|
||||
_a.trys.push([2, 6, , 8]);
|
||||
appId = data.a, tokenValue = data.t;
|
||||
promises = [];
|
||||
if (appId) {
|
||||
promises.push(this.setApplication(appId));
|
||||
}
|
||||
if (tokenValue) {
|
||||
promises.push(this.setTokenValue(tokenValue));
|
||||
}
|
||||
if (!(promises.length > 0)) return [3 /*break*/, 4];
|
||||
return [4 /*yield*/, Promise.all(promises)];
|
||||
case 3:
|
||||
_a.sent();
|
||||
_a.label = 4;
|
||||
case 4:
|
||||
if (!tokenValue) return [3 /*break*/, 6];
|
||||
return [4 /*yield*/, this.setTokenValue(tokenValue)];
|
||||
case 4: return [4 /*yield*/, this.commit()];
|
||||
case 5:
|
||||
_a.sent();
|
||||
_a.label = 6;
|
||||
case 6: return [4 /*yield*/, this.commit()];
|
||||
case 7:
|
||||
_a.sent();
|
||||
return [3 /*break*/, 10];
|
||||
case 8:
|
||||
return [3 /*break*/, 8];
|
||||
case 6:
|
||||
err_1 = _a.sent();
|
||||
return [4 /*yield*/, this.rollback()];
|
||||
case 9:
|
||||
case 7:
|
||||
_a.sent();
|
||||
throw err_1;
|
||||
case 10: return [3 /*break*/, 12];
|
||||
case 11:
|
||||
case 8: return [3 /*break*/, 10];
|
||||
case 9:
|
||||
// 否则是后台模式,默认用root
|
||||
this.rootMode = true;
|
||||
_a.label = 12;
|
||||
case 12: return [2 /*return*/];
|
||||
_a.label = 10;
|
||||
case 10: return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import { registerWeChatPublicEventCallback } from './wechat';
|
||||
declare const _default: {
|
||||
[x: string]: import("oak-domain/lib/types").Endpoint<import("../general-app-domain").EntityDict, import("../types/RuntimeCxt").BRC>;
|
||||
};
|
||||
export default _default;
|
||||
export { registerWeChatPublicEventCallback, };
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.registerWeChatPublicEventCallback = void 0;
|
||||
var tslib_1 = require("tslib");
|
||||
var wechat_1 = tslib_1.__importDefault(require("./wechat"));
|
||||
var wechat_1 = tslib_1.__importStar(require("./wechat"));
|
||||
Object.defineProperty(exports, "registerWeChatPublicEventCallback", { enumerable: true, get: function () { return wechat_1.registerWeChatPublicEventCallback; } });
|
||||
exports.default = tslib_1.__assign({}, wechat_1.default);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { Endpoint } from 'oak-domain/lib/types/Endpoint';
|
||||
import { EntityDict } from '../general-app-domain';
|
||||
import { BRC } from '../types/RuntimeCxt';
|
||||
import { WechatPublicEventData } from 'oak-external-sdk';
|
||||
export declare function registerWeChatPublicEventCallback(appId: string, callback: (data: WechatPublicEventData, context: BRC) => void): void;
|
||||
declare const endpoints: Record<string, Endpoint<EntityDict, BRC>>;
|
||||
export default endpoints;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,15 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.registerWeChatPublicEventCallback = void 0;
|
||||
var tslib_1 = require("tslib");
|
||||
var url_1 = tslib_1.__importDefault(require("url"));
|
||||
var assert_1 = tslib_1.__importDefault(require("assert"));
|
||||
var node_url_1 = require("node:url");
|
||||
var node_path_1 = tslib_1.__importDefault(require("node:path"));
|
||||
var sha1_1 = tslib_1.__importDefault(require("sha1"));
|
||||
var xml2json_1 = tslib_1.__importDefault(require("xml2json"));
|
||||
var oak_external_sdk_1 = require("oak-external-sdk");
|
||||
var uuid_1 = require("oak-domain/lib/utils/uuid");
|
||||
var domain_1 = require("../utils/domain");
|
||||
function assertFromWeChat(query, config) {
|
||||
var _a;
|
||||
var signature = query.signature, nonce = query.nonce, timestamp = query.timestamp;
|
||||
|
|
@ -15,31 +22,377 @@ function assertFromWeChat(query, config) {
|
|||
var sha1Sign = (0, sha1_1.default)(sign);
|
||||
return signature === sha1Sign;
|
||||
}
|
||||
var CALLBACK = {};
|
||||
function registerWeChatPublicEventCallback(appId, callback) {
|
||||
(0, assert_1.default)(!CALLBACK.hasOwnProperty(appId));
|
||||
CALLBACK[appId] = callback;
|
||||
}
|
||||
exports.registerWeChatPublicEventCallback = registerWeChatPublicEventCallback;
|
||||
/**
|
||||
* 用户取关事件,注意要容wechatUser不存在的情况
|
||||
* @param openId
|
||||
* @param context
|
||||
* @returns
|
||||
*/
|
||||
function setUserUnsubscribed(openId, context) {
|
||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||
var list, weChatUser;
|
||||
return tslib_1.__generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, context.select('wechatUser', {
|
||||
data: {
|
||||
id: 1,
|
||||
subscribed: 1,
|
||||
subscribedAt: 1,
|
||||
},
|
||||
filter: {
|
||||
applicationId: context.getApplicationId(),
|
||||
openId: openId,
|
||||
},
|
||||
indexFrom: 0,
|
||||
count: 10,
|
||||
}, { dontCollect: true })];
|
||||
case 1:
|
||||
list = _a.sent();
|
||||
if (!(list && list.length > 0)) return [3 /*break*/, 4];
|
||||
(0, assert_1.default)(list.length === 1);
|
||||
weChatUser = list[0];
|
||||
if (!weChatUser.subscribed) return [3 /*break*/, 3];
|
||||
return [4 /*yield*/, context.operate('wechatUser', {
|
||||
action: 'update',
|
||||
data: {
|
||||
subscribed: false,
|
||||
unsubscribeAt: Date.now(),
|
||||
},
|
||||
id: weChatUser.id,
|
||||
}, { dontCollect: true, dontCreateOper: true })];
|
||||
case 2:
|
||||
_a.sent();
|
||||
_a.label = 3;
|
||||
case 3: return [3 /*break*/, 6];
|
||||
case 4: return [4 /*yield*/, context.operate('wechatUser', {
|
||||
action: 'create',
|
||||
data: {
|
||||
subscribed: false,
|
||||
applicationId: context.getApplicationId(),
|
||||
openId: openId,
|
||||
},
|
||||
}, { dontCollect: true, dontCreateOper: true })];
|
||||
case 5:
|
||||
_a.sent();
|
||||
_a.label = 6;
|
||||
case 6: return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
function setUserSubscribed(openId, eventKey, context) {
|
||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||
var applicationId, list, now, data, doUpdate, sceneStr, wcqId, _a, weChatQrCode, entity, entityId, _b, _c, userEntityGrant, _d, id, granter, expired, entity2, _e, domain, url, name_1, application, _f, type, config, _g, appId, appSecret, wechatInstance;
|
||||
var _this = this;
|
||||
return tslib_1.__generator(this, function (_h) {
|
||||
switch (_h.label) {
|
||||
case 0:
|
||||
applicationId = context.getApplicationId();
|
||||
return [4 /*yield*/, context.select('wechatUser', {
|
||||
data: {
|
||||
id: 1,
|
||||
subscribed: 1,
|
||||
subscribedAt: 1,
|
||||
},
|
||||
filter: {
|
||||
applicationId: applicationId,
|
||||
openId: openId,
|
||||
},
|
||||
indexFrom: 0,
|
||||
count: 1,
|
||||
}, { dontCollect: true })];
|
||||
case 1:
|
||||
list = _h.sent();
|
||||
now = Date.now();
|
||||
data = {
|
||||
activeAt: now,
|
||||
};
|
||||
doUpdate = function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
||||
var wechatUser;
|
||||
return tslib_1.__generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
if (!(list && list.length > 0)) return [3 /*break*/, 2];
|
||||
(0, assert_1.default)(list.length === 1);
|
||||
wechatUser = list[0];
|
||||
if (!wechatUser.subscribed) {
|
||||
Object.assign(data, {
|
||||
subscribed: true,
|
||||
subscribeAt: now,
|
||||
});
|
||||
}
|
||||
return [4 /*yield*/, context.operate('wechatUser', {
|
||||
action: 'update',
|
||||
data: data,
|
||||
filter: {
|
||||
id: wechatUser.id,
|
||||
},
|
||||
}, { dontCollect: true, dontCreateOper: true })];
|
||||
case 1: return [2 /*return*/, _a.sent()];
|
||||
case 2:
|
||||
Object.assign(data, {
|
||||
subscribed: true,
|
||||
subscribeAt: now,
|
||||
applicationId: applicationId,
|
||||
openId: openId,
|
||||
});
|
||||
return [4 /*yield*/, context.operate('wechatUser', {
|
||||
action: 'create',
|
||||
data: data,
|
||||
}, { dontCollect: true })];
|
||||
case 3:
|
||||
// 这里试着直接把user也创建出来,by Xc 20190720
|
||||
/**
|
||||
* 这里不能创建user,否则会出现一个weChatUser有openId和userId,却没有unionId
|
||||
* 当同一个user先从小程序登录,再从公众号登录时就会生成两个user
|
||||
*/
|
||||
/* return warden.insertEntity(tables.user, {
|
||||
state: UserState.normal,
|
||||
activeAt: Date.now(),
|
||||
}, txn).then(
|
||||
(user) => {
|
||||
assign(data, { userId: user.id });
|
||||
return warden.insertEntity(tables.weChatUser, data, txn);
|
||||
}
|
||||
);*/
|
||||
return [2 /*return*/, _a.sent()];
|
||||
}
|
||||
});
|
||||
}); };
|
||||
if (!eventKey) return [3 /*break*/, 9];
|
||||
sceneStr = void 0;
|
||||
if (eventKey.startsWith('qrscene_')) {
|
||||
sceneStr = eventKey.slice(eventKey.indexOf('qrscene_') + 8);
|
||||
}
|
||||
else {
|
||||
sceneStr = eventKey;
|
||||
}
|
||||
wcqId = (0, uuid_1.expandUuidTo36Bytes)(sceneStr);
|
||||
return [4 /*yield*/, context.select('wechatQrCode', {
|
||||
data: {
|
||||
id: 1,
|
||||
entity: 1,
|
||||
entityId: 1,
|
||||
},
|
||||
filter: {
|
||||
id: wcqId,
|
||||
},
|
||||
indexFrom: 0,
|
||||
count: 10,
|
||||
}, { dontCollect: true })];
|
||||
case 2:
|
||||
_a = tslib_1.__read.apply(void 0, [_h.sent(), 1]), weChatQrCode = _a[0];
|
||||
if (!weChatQrCode) return [3 /*break*/, 8];
|
||||
entity = weChatQrCode.entity, entityId = weChatQrCode.entityId;
|
||||
_b = entity;
|
||||
switch (_b) {
|
||||
case 'user': return [3 /*break*/, 3];
|
||||
case 'userEntityGrant': return [3 /*break*/, 4];
|
||||
}
|
||||
return [3 /*break*/, 7];
|
||||
case 3:
|
||||
{
|
||||
// 裂变获得的用户
|
||||
if (list[0] && !list[0].userId) {
|
||||
Object.assign(data, { userId: entityId });
|
||||
}
|
||||
return [3 /*break*/, 7];
|
||||
}
|
||||
_h.label = 4;
|
||||
case 4: return [4 /*yield*/, context.select('userEntityGrant', {
|
||||
data: {
|
||||
id: 1,
|
||||
},
|
||||
filter: {
|
||||
id: entityId,
|
||||
}
|
||||
}, { dontCollect: true })];
|
||||
case 5:
|
||||
_c = tslib_1.__read.apply(void 0, [_h.sent(), 1]), userEntityGrant = _c[0];
|
||||
_d = userEntityGrant, id = _d.id, granter = _d.granter, expired = _d.expired, entity2 = _d.entity;
|
||||
return [4 /*yield*/, context.select('domain', {
|
||||
data: {
|
||||
id: 1,
|
||||
url: 1,
|
||||
apiPath: 1,
|
||||
protocol: 1,
|
||||
port: 1,
|
||||
},
|
||||
filter: {
|
||||
systemId: {
|
||||
$in: {
|
||||
entity: 'application',
|
||||
data: {
|
||||
systemId: 1,
|
||||
},
|
||||
filter: {
|
||||
id: applicationId,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, { dontCollect: true })];
|
||||
case 6:
|
||||
_e = tslib_1.__read.apply(void 0, [_h.sent(), 1]), domain = _e[0];
|
||||
(0, assert_1.default)(domain, "\u5904\u7406userEntityGrant\u65F6\uFF0C\u627E\u4E0D\u5230\u5BF9\u5E94\u7684domain\uFF0CapplicationId\u662F\u300C".concat(applicationId, "\u300D"));
|
||||
url = (0, domain_1.composeDomainUrl)(domain, 'wechatQrCode/scan', {
|
||||
scene: sceneStr,
|
||||
});
|
||||
(0, assert_1.default)(!expired); // 如果生成的wechatQrCode没过期,userEntityGrant就不可能过期。
|
||||
name_1 = granter.name || granter.nickname;
|
||||
application = context.getApplication();
|
||||
_f = application, type = _f.type, config = _f.config;
|
||||
(0, assert_1.default)(type === 'wechatPublic');
|
||||
_g = config, appId = _g.appId, appSecret = _g.appSecret;
|
||||
wechatInstance = oak_external_sdk_1.WechatSDK.getInstance(appId, appSecret, 'wechatPublic');
|
||||
wechatInstance.sendServeMessage({
|
||||
openId: openId,
|
||||
type: 'news',
|
||||
url: url,
|
||||
title: "".concat(name_1, "\u7ED9\u60A8\u521B\u5EFA\u4E86\u4E00\u4E2A\u6388\u6743"),
|
||||
description: '请接受',
|
||||
picurl: 'http://img95.699pic.com/element/40018/2473.png_860.png',
|
||||
});
|
||||
_h.label = 7;
|
||||
case 7: return [3 /*break*/, 9];
|
||||
case 8:
|
||||
console.warn("\u7EBF\u4E0A\u6709\u626B\u63CF\u4E8C\u7EF4\u7801\u573A\u666F\u503C\uFF0C\u4F46\u627E\u4E0D\u5230\u5BF9\u5E94\u7684qrCode\uFF0CeventKey\u662F".concat(eventKey));
|
||||
_h.label = 9;
|
||||
case 9: return [4 /*yield*/, doUpdate()];
|
||||
case 10:
|
||||
_h.sent();
|
||||
return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
function onWeChatPublicEvent(data, context) {
|
||||
var ToUserName = data.ToUserName, FromUserName = data.FromUserName, CreateTime = data.CreateTime, MsgType = data.MsgType, Event = data.Event, Content = data.Content, EventKey = data.EventKey;
|
||||
var appId = context.getApplicationId();
|
||||
var evt;
|
||||
// 如果有应用注入的事件回调则处理之,不依赖其返回
|
||||
if (CALLBACK[appId]) {
|
||||
CALLBACK[appId](data, context);
|
||||
}
|
||||
if (Event) {
|
||||
var event_1 = Event.toLowerCase();
|
||||
switch (event_1) {
|
||||
case 'subscribe':
|
||||
setUserSubscribed(FromUserName, EventKey, context);
|
||||
evt = "\u7528\u6237".concat(FromUserName, "\u5173\u6CE8\u516C\u4F17\u53F7");
|
||||
break;
|
||||
case 'scan':
|
||||
setUserSubscribed(FromUserName, EventKey, context);
|
||||
evt = "\u7528\u6237".concat(FromUserName, "\u518D\u6B21\u626B\u63CF\u5E26").concat(EventKey, "\u952E\u503C\u7684\u4E8C\u7EF4\u7801");
|
||||
break;
|
||||
case 'unsubscribe': {
|
||||
setUserUnsubscribed(FromUserName, context);
|
||||
evt = "\u7528\u6237".concat(FromUserName, "\u53D6\u5173");
|
||||
break;
|
||||
}
|
||||
case 'location': {
|
||||
evt = "\u7528\u6237".concat(FromUserName, "\u4E0A\u4F20\u4E86\u5730\u7406\u4F4D\u7F6E\u4FE1\u606F");
|
||||
break;
|
||||
}
|
||||
case 'click': {
|
||||
evt = "\u7528\u6237".concat(FromUserName, "\u70B9\u51FB\u83DC\u5355\u3010").concat(EventKey, "\u3011");
|
||||
break;
|
||||
}
|
||||
case 'view': {
|
||||
evt = "\u7528\u6237".concat(FromUserName, "\u70B9\u51FB\u83DC\u5355\u8DF3\u8F6C\u94FE\u63A5\u3010").concat(EventKey, "\u3011");
|
||||
break;
|
||||
}
|
||||
case 'templatesendjobfinish': {
|
||||
// 模板消息发送完成,去更新对应的messageSent对象
|
||||
// 这个在线上测试没法通过,返回的msgId不符合,不知道为什么
|
||||
var msgId = data.MsgID, status_1 = data.Status, openId = data.FromUserName;
|
||||
evt = "\u5E94\u7528".concat(appId, "\u7684\u7528\u6237").concat(FromUserName, "\u53D1\u6765\u4E86").concat(Event, "\u4E8B\u4EF6\uFF0C\u5185\u5BB9\u662F").concat(JSON.stringify(data));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
evt = "\u5E94\u7528".concat(appId, "\u7684\u7528\u6237").concat(FromUserName, "\u53D1\u6765\u4E86").concat(Event, "\u4E8B\u4EF6\uFF0C\u5185\u5BB9\u662F").concat(JSON.stringify(data));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
console.log(evt);
|
||||
}
|
||||
return {
|
||||
content: '',
|
||||
contentType: 'application/text',
|
||||
};
|
||||
}
|
||||
(0, assert_1.default)(MsgType);
|
||||
var content = '<xml>' +
|
||||
"<ToUserName>".concat(FromUserName, "</ToUserName>") +
|
||||
"<FromUserName>".concat(ToUserName, "</FromUserName>") +
|
||||
"<CreateTime>".concat(CreateTime, "</CreateTime>") +
|
||||
'<MsgType>transfer_customer_service</MsgType>' +
|
||||
'</xml>';
|
||||
switch (MsgType) {
|
||||
case 'text':
|
||||
case 'link': {
|
||||
evt = "\u63A5\u6536\u5230\u6765\u81EA\u7528\u6237\u7684\u6587\u5B57\u6D88\u606F\uFF1A".concat(Content);
|
||||
break;
|
||||
}
|
||||
case 'image': {
|
||||
evt = "\u63A5\u6536\u5230\u6765\u81EA\u7528\u6237\u7684\u56FE\u7247\u6D88\u606F\uFF1A".concat(Content);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
evt = "\u63A5\u6536\u5230\u6765\u81EA\u7528\u6237\u7684".concat(MsgType, "\u578B\u6D88\u606F");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
console.log(evt);
|
||||
}
|
||||
return {
|
||||
content: content,
|
||||
contentType: 'application/xml',
|
||||
};
|
||||
}
|
||||
var endpoints = {
|
||||
wechatApi: {
|
||||
wechatPublicEvent: [{
|
||||
name: '微信公众号回调接口',
|
||||
method: 'post',
|
||||
params: ['appId'],
|
||||
fn: function (context, params, body, req, headers) { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
|
||||
var appId;
|
||||
return tslib_1.__generator(this, function (_a) {
|
||||
appId = params.appId;
|
||||
throw new Error('not implemented yet');
|
||||
});
|
||||
}); },
|
||||
},
|
||||
wechatVerify: {
|
||||
name: '微信公众号验证接口',
|
||||
method: 'get',
|
||||
// params: ['applicationId'],
|
||||
fn: function (context, params, body, req, headers) { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
|
||||
var query, applicationId, _a, application, isWeChat;
|
||||
fn: function (context, params, headers, req, body) { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
|
||||
var appId, data, _a, content, contentType;
|
||||
return tslib_1.__generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
query = url_1.default.parse(req.url, true, false).query;
|
||||
applicationId = query.applicationId;
|
||||
if (!applicationId) {
|
||||
appId = params.appId;
|
||||
if (!appId) {
|
||||
throw new Error('applicationId参数不存在');
|
||||
}
|
||||
return [4 /*yield*/, context.setApplication(appId)];
|
||||
case 1:
|
||||
_b.sent();
|
||||
data = xml2json_1.default.toJson(body, { object: true });
|
||||
_a = onWeChatPublicEvent(data, context), content = _a.content, contentType = _a.contentType;
|
||||
return [2 /*return*/, content];
|
||||
}
|
||||
});
|
||||
}); },
|
||||
}, {
|
||||
name: '微信公众号验证接口',
|
||||
method: 'get',
|
||||
params: ['appId'],
|
||||
fn: function (context, params, body, req, headers) { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
|
||||
var searchParams, appId, _a, application, signature, timestamp, nonce, isWeChat, echostr;
|
||||
return tslib_1.__generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
searchParams = new node_url_1.URL(node_path_1.default.join('http://', req.headers.host, req.url)).searchParams;
|
||||
appId = params.appId;
|
||||
if (!appId) {
|
||||
throw new Error('applicationId参数不存在');
|
||||
}
|
||||
return [4 /*yield*/, context.select('application', {
|
||||
|
|
@ -48,17 +401,21 @@ var endpoints = {
|
|||
config: 1,
|
||||
},
|
||||
filter: {
|
||||
id: applicationId,
|
||||
id: appId,
|
||||
},
|
||||
}, {})];
|
||||
case 1:
|
||||
_a = tslib_1.__read.apply(void 0, [_b.sent(), 1]), application = _a[0];
|
||||
if (!application) {
|
||||
throw new Error("\u672A\u627E\u5230".concat(applicationId, "\u5BF9\u5E94\u7684app"));
|
||||
throw new Error("\u672A\u627E\u5230".concat(appId, "\u5BF9\u5E94\u7684app"));
|
||||
}
|
||||
isWeChat = assertFromWeChat(query, application.config);
|
||||
signature = searchParams.get('signature');
|
||||
timestamp = searchParams.get('timestamp');
|
||||
nonce = searchParams.get('nonce');
|
||||
isWeChat = assertFromWeChat({ signature: signature, timestamp: timestamp, nonce: nonce }, application.config);
|
||||
if (isWeChat) {
|
||||
return [2 /*return*/, query.echostr];
|
||||
echostr = searchParams.get('echostr');
|
||||
return [2 /*return*/, echostr];
|
||||
}
|
||||
else {
|
||||
throw new Error('Verify Failed');
|
||||
|
|
@ -67,6 +424,6 @@ var endpoints = {
|
|||
}
|
||||
});
|
||||
}); },
|
||||
}
|
||||
}],
|
||||
};
|
||||
exports.default = endpoints;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { String, Text, Image, Datetime } from 'oak-domain/lib/types/DataType';
|
||||
import { Schema as ExtraFile } from './ExtraFile';
|
||||
import { Schema as WechatQrCode } from './WechatQrCode';
|
||||
import { EntityShape } from 'oak-domain/lib/types/Entity';
|
||||
export interface Schema extends EntityShape {
|
||||
name?: String<16>;
|
||||
|
|
@ -13,4 +14,5 @@ export interface Schema extends EntityShape {
|
|||
idNumber?: String<32>;
|
||||
ref?: Schema;
|
||||
files: Array<ExtraFile>;
|
||||
codes: Array<WechatQrCode>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ var locale = {
|
|||
files: '相关文件',
|
||||
userState: '用户状态',
|
||||
idState: '身份验证状态',
|
||||
codes: '微信分享二维码',
|
||||
},
|
||||
action: {
|
||||
activate: '激活',
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export interface Schema extends EntityShape {
|
|||
remark?: Text;
|
||||
granter: User;
|
||||
grantee?: User;
|
||||
files: Array<WechatQrCode>;
|
||||
codes: Array<WechatQrCode>;
|
||||
expiresAt?: Datetime;
|
||||
expired?: Boolean;
|
||||
redirectTo?: Object;
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ var locale = {
|
|||
remark: '备注',
|
||||
grantee: '领取人',
|
||||
granter: '授权人',
|
||||
files: '微信码',
|
||||
codes: '微信码',
|
||||
expired: '是否过期',
|
||||
expiresAt: '过期时间',
|
||||
redirectTo: '重定向页面',
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ import watchers from './watchers';
|
|||
import data from "./data";
|
||||
export * from './exceptionHandlers';
|
||||
import { registerMessagePropsConverter } from './triggers/message';
|
||||
export { checkers, triggers, watchers, data, aspectDict, registerMessagePropsConverter, };
|
||||
import { registerWeChatPublicEventCallback } from './endpoints';
|
||||
export { checkers, triggers, watchers, data, aspectDict, registerMessagePropsConverter, registerWeChatPublicEventCallback, };
|
||||
export * from './types/Exception';
|
||||
export { composeFileUrl, decomposeFileUrl } from './utils/extraFile';
|
||||
export * from './data/DEV-CONFIG';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.decomposeFileUrl = exports.composeFileUrl = exports.registerMessagePropsConverter = exports.aspectDict = exports.data = exports.watchers = exports.triggers = exports.checkers = void 0;
|
||||
exports.decomposeFileUrl = exports.composeFileUrl = exports.registerWeChatPublicEventCallback = exports.registerMessagePropsConverter = exports.aspectDict = exports.data = exports.watchers = exports.triggers = exports.checkers = void 0;
|
||||
var tslib_1 = require("tslib");
|
||||
var aspects_1 = require("./aspects");
|
||||
Object.defineProperty(exports, "aspectDict", { enumerable: true, get: function () { return aspects_1.aspectDict; } });
|
||||
|
|
@ -15,6 +15,8 @@ exports.data = data_1.default;
|
|||
tslib_1.__exportStar(require("./exceptionHandlers"), exports);
|
||||
var message_1 = require("./triggers/message");
|
||||
Object.defineProperty(exports, "registerMessagePropsConverter", { enumerable: true, get: function () { return message_1.registerMessagePropsConverter; } });
|
||||
var endpoints_1 = require("./endpoints");
|
||||
Object.defineProperty(exports, "registerWeChatPublicEventCallback", { enumerable: true, get: function () { return endpoints_1.registerWeChatPublicEventCallback; } });
|
||||
tslib_1.__exportStar(require("./types/Exception"), exports);
|
||||
var extraFile_1 = require("./utils/extraFile");
|
||||
Object.defineProperty(exports, "composeFileUrl", { enumerable: true, get: function () { return extraFile_1.composeFileUrl; } });
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ exports.default = OakComponent({
|
|||
props: 1,
|
||||
},
|
||||
isList: true,
|
||||
properties: {
|
||||
scene: String,
|
||||
},
|
||||
filters: [
|
||||
{
|
||||
filter: function (_a) {
|
||||
|
|
|
|||
|
|
@ -32,6 +32,11 @@ var triggers = [
|
|||
granterId: userId,
|
||||
expired: false,
|
||||
});
|
||||
if (!userEntityGrantData.expiresAt) {
|
||||
Object.assign(userEntityGrantData, {
|
||||
expiresAt: Date.now() + 300 * 1000,
|
||||
});
|
||||
}
|
||||
// 为之创建微信体系下的一个weChatQrCode
|
||||
return [4 /*yield*/, (0, wechatQrCode_1.createWechatQrCode)({
|
||||
entity: 'userEntityGrant',
|
||||
|
|
@ -42,6 +47,7 @@ var triggers = [
|
|||
oakId: id,
|
||||
},
|
||||
},
|
||||
lifetimeLength: userEntityGrantData.expiresAt - Date.now() + 300 * 1000, // wechatQrCode的过期由trigger负责,可以放长一些
|
||||
}, context)];
|
||||
case 1:
|
||||
// 为之创建微信体系下的一个weChatQrCode
|
||||
|
|
@ -233,6 +239,55 @@ var triggers = [
|
|||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '当userEntityGrant过期时,使其相关的wechatQrCode也过期',
|
||||
entity: 'userEntityGrant',
|
||||
action: 'update',
|
||||
check: function (operation) {
|
||||
var data = operation.data;
|
||||
return !!(data.expired);
|
||||
},
|
||||
when: 'after',
|
||||
fn: function (_a, context, params) {
|
||||
var operation = _a.operation;
|
||||
return tslib_1.__awaiter(void 0, void 0, void 0, function () {
|
||||
var filter, wechatQrCodes, ids;
|
||||
return tslib_1.__generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
filter = operation.filter;
|
||||
return [4 /*yield*/, context.select('wechatQrCode', {
|
||||
data: {
|
||||
id: 1,
|
||||
expired: 1,
|
||||
},
|
||||
filter: {
|
||||
userEntityGrant: filter,
|
||||
expired: false,
|
||||
},
|
||||
}, { dontCollect: true })];
|
||||
case 1:
|
||||
wechatQrCodes = _b.sent();
|
||||
ids = wechatQrCodes.map(function (ele) { return ele.id; });
|
||||
return [4 /*yield*/, context.operate('wechatQrCode', {
|
||||
action: 'update',
|
||||
data: {
|
||||
expired: true,
|
||||
},
|
||||
filter: {
|
||||
id: {
|
||||
$in: ids,
|
||||
},
|
||||
},
|
||||
}, { dontCollect: true })];
|
||||
case 2:
|
||||
_b.sent();
|
||||
return [2 /*return*/, ids.length];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
];
|
||||
exports.default = triggers;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
import { Schema as Domain } from '../general-app-domain/Domain/Schema';
|
||||
export declare function composeDomainUrl(domain: Domain, url?: string, props?: Record<string, string>): string;
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.composeDomainUrl = void 0;
|
||||
var tslib_1 = require("tslib");
|
||||
function composeDomainUrl(domain, url, props) {
|
||||
var e_1, _a;
|
||||
var port = domain.port, protocol = domain.protocol, apiPath = domain.apiPath, domainUrl = domain.url;
|
||||
var Url = "".concat(protocol, "://").concat(domainUrl);
|
||||
if (port) {
|
||||
Url += ":".concat(port);
|
||||
}
|
||||
if (url) {
|
||||
Url += "/".concat(url);
|
||||
if (props) {
|
||||
var k = Object.keys(props);
|
||||
if (k.length > 0) {
|
||||
Url += '?';
|
||||
try {
|
||||
for (var k_1 = tslib_1.__values(k), k_1_1 = k_1.next(); !k_1_1.done; k_1_1 = k_1.next()) {
|
||||
var k2 = k_1_1.value;
|
||||
Url += "".concat(k2, "=").concat(encodeURI(props[k2]));
|
||||
}
|
||||
}
|
||||
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (k_1_1 && !k_1_1.done && (_a = k_1.return)) _a.call(k_1);
|
||||
}
|
||||
finally { if (e_1) throw e_1.error; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Url;
|
||||
}
|
||||
exports.composeDomainUrl = composeDomainUrl;
|
||||
|
|
@ -25,7 +25,8 @@
|
|||
"react-scripts": "^5.0.1",
|
||||
"sha1": "^1.1.1",
|
||||
"tslib": "^2.4.0",
|
||||
"uuid": "^8.3.2"
|
||||
"uuid": "^8.3.2",
|
||||
"xml2json": "^0.12.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ant-design/icons": "^4.7.0",
|
||||
|
|
@ -54,6 +55,7 @@
|
|||
"@types/react-dom": "^18.0.5",
|
||||
"@types/sha1": "^1.1.3",
|
||||
"@types/uuid": "^8.3.0",
|
||||
"@types/xml2json": "^0.11.4",
|
||||
"antd": "^5.0.0",
|
||||
"antd-mobile": "^5.24.1",
|
||||
"antd-mobile-icons": "^0.3.0",
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export async function createWechatQrCode<ED extends EntityDict, T extends keyof
|
|||
permanent?: boolean;
|
||||
props: WechatQrCodeProps;
|
||||
}, context: Cxt) {
|
||||
const { entity, entityId, tag, lifetimeLength = 300 * 10000, permanent = false, props } = options;
|
||||
const { entity, entityId, tag, lifetimeLength = 2592000 * 1000, permanent = false, props } = options;
|
||||
const applicationId = context.getApplicationId();
|
||||
assert(applicationId);
|
||||
const [system] = await context.select(
|
||||
|
|
@ -134,6 +134,10 @@ export async function createWechatQrCode<ED extends EntityDict, T extends keyof
|
|||
if (!appId || !appType) {
|
||||
throw new Error('无法生成二维码,找不到此system下的服务号或者小程序信息');
|
||||
}
|
||||
if (lifetimeLength > 2592000 * 1000 && !permanent) {
|
||||
// 二维码的过期应当由上层对象来自主控制以保持一致性
|
||||
throw new Error('二维码过期时间太长,不能长于30天');
|
||||
}
|
||||
|
||||
const data: CreateWechatQrcodeData = {
|
||||
id,
|
||||
|
|
@ -161,7 +165,7 @@ export async function createWechatQrCode<ED extends EntityDict, T extends keyof
|
|||
|
||||
const { type: applicationType, config } = application;
|
||||
|
||||
console.log(process.env.OAK_PLATFORM, process.env.NODE_ENV);
|
||||
// console.log(process.env.OAK_PLATFORM, process.env.NODE_ENV);
|
||||
switch (type) {
|
||||
case 'wechatMpWxaCode': {
|
||||
assert(
|
||||
|
|
|
|||
|
|
@ -45,6 +45,22 @@ export default OakComponent({
|
|||
wechatMp: {
|
||||
externalClasses: ['oak-class', 'oak-item-class'],
|
||||
},
|
||||
filters: [
|
||||
{
|
||||
filter({ props }) {
|
||||
const { tag1, tag2 } = props;
|
||||
const filter1 = {};
|
||||
if (tag1) {
|
||||
Object.assign(filter1, { tag1 });
|
||||
}
|
||||
if (tag2) {
|
||||
Object.assign(filter1, { tag2 });
|
||||
}
|
||||
return filter1;
|
||||
}
|
||||
}
|
||||
|
||||
],
|
||||
properties: {
|
||||
removeLater: Boolean,
|
||||
autoUpload: {
|
||||
|
|
|
|||
|
|
@ -85,12 +85,7 @@ export class BackendRuntimeContext<ED extends EntityDict> extends AsyncContext<E
|
|||
this.token = token;
|
||||
}
|
||||
|
||||
protected async initialize(data?: SerializedData) {
|
||||
if (data) {
|
||||
await this.begin();
|
||||
try {
|
||||
const { a: appId, t: tokenValue } = data;
|
||||
if (appId) {
|
||||
async setApplication(appId: string) {
|
||||
const result = await this.select(
|
||||
'application',
|
||||
{
|
||||
|
|
@ -128,8 +123,21 @@ export class BackendRuntimeContext<ED extends EntityDict> extends AsyncContext<E
|
|||
assert(result.length > 0, `构建BackendRuntimeContext对应appId「${appId}」找不到application`);
|
||||
this.application = result[0];
|
||||
}
|
||||
|
||||
protected async initialize(data?: SerializedData) {
|
||||
if (data) {
|
||||
await this.begin();
|
||||
try {
|
||||
const { a: appId, t: tokenValue } = data;
|
||||
const promises: Promise<void>[] = [];
|
||||
if (appId) {
|
||||
promises.push(this.setApplication(appId));
|
||||
}
|
||||
if (tokenValue) {
|
||||
await this.setTokenValue(tokenValue);
|
||||
promises.push(this.setTokenValue(tokenValue));
|
||||
}
|
||||
if (promises.length > 0) {
|
||||
await Promise.all(promises);
|
||||
}
|
||||
await this.commit();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import wechat from './wechat';
|
||||
import wechat, { registerWeChatPublicEventCallback } from './wechat';
|
||||
|
||||
|
||||
export default {
|
||||
...wechat,
|
||||
};
|
||||
|
||||
export {
|
||||
registerWeChatPublicEventCallback,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,17 +1,25 @@
|
|||
import assert from 'assert';
|
||||
import { URL } from 'node:url';
|
||||
import Path from 'node:path';
|
||||
import sha1 from 'sha1';
|
||||
import parser from 'xml2json';
|
||||
import { Endpoint } from 'oak-domain/lib/types/Endpoint';
|
||||
import {
|
||||
WechatSDK,
|
||||
WechatMpInstance,
|
||||
WechatPublicInstance,
|
||||
} from 'oak-external-sdk';
|
||||
import { EntityDict } from '../general-app-domain';
|
||||
import { BRC } from '../types/RuntimeCxt';
|
||||
import URL from 'url';
|
||||
import { WechatPublicConfig } from '../entities/Application';
|
||||
import sha1 from 'sha1';
|
||||
import { AccountBookOutlined } from '@ant-design/icons';
|
||||
import { WechatPublicEventData } from 'oak-external-sdk';
|
||||
import { expandUuidTo36Bytes } from 'oak-domain/lib/utils/uuid';
|
||||
import { composeDomainUrl } from '../utils/domain';
|
||||
|
||||
type VerifyQuery = {
|
||||
applicationId: string,
|
||||
signature: string,
|
||||
nonce: string,
|
||||
timestamp: string,
|
||||
echostr: string,
|
||||
}
|
||||
|
||||
function assertFromWeChat(query: VerifyQuery, config: WechatPublicConfig) {
|
||||
|
|
@ -26,24 +34,378 @@ function assertFromWeChat(query: VerifyQuery, config: WechatPublicConfig) {
|
|||
return signature === sha1Sign;
|
||||
}
|
||||
|
||||
const CALLBACK: Record<string, (data: WechatPublicEventData, context: BRC) => void> = {};
|
||||
|
||||
export function registerWeChatPublicEventCallback(appId: string, callback: (data: WechatPublicEventData, context: BRC) => void) {
|
||||
assert(!CALLBACK.hasOwnProperty(appId));
|
||||
|
||||
CALLBACK[appId] = callback;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 用户取关事件,注意要容wechatUser不存在的情况
|
||||
* @param openId
|
||||
* @param context
|
||||
* @returns
|
||||
*/
|
||||
async function setUserUnsubscribed(openId: string, context: BRC) {
|
||||
const list = await context.select(
|
||||
'wechatUser',
|
||||
{
|
||||
data: {
|
||||
id: 1,
|
||||
subscribed: 1,
|
||||
subscribedAt: 1,
|
||||
},
|
||||
filter: {
|
||||
applicationId: context.getApplicationId(),
|
||||
openId,
|
||||
},
|
||||
indexFrom: 0,
|
||||
count: 10,
|
||||
},
|
||||
{ dontCollect: true },
|
||||
);
|
||||
if (list && list.length > 0) {
|
||||
assert(list.length === 1);
|
||||
const weChatUser = list[0];
|
||||
|
||||
if (weChatUser.subscribed) {
|
||||
await context.operate(
|
||||
'wechatUser',
|
||||
{
|
||||
action: 'update',
|
||||
data: {
|
||||
subscribed: false,
|
||||
unsubscribeAt: Date.now(),
|
||||
},
|
||||
id: weChatUser.id,
|
||||
},
|
||||
{ dontCollect: true, dontCreateOper: true },
|
||||
);
|
||||
}
|
||||
} else {
|
||||
await context.operate(
|
||||
'wechatUser',
|
||||
{
|
||||
action: 'create',
|
||||
data: {
|
||||
subscribed: false,
|
||||
applicationId: context.getApplicationId(),
|
||||
openId,
|
||||
},
|
||||
},
|
||||
{ dontCollect: true, dontCreateOper: true },
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
async function setUserSubscribed(openId: string, eventKey: string, context: BRC) {
|
||||
const applicationId = context.getApplicationId();
|
||||
const list = await context.select(
|
||||
'wechatUser',
|
||||
{
|
||||
data: {
|
||||
id: 1,
|
||||
subscribed: 1,
|
||||
subscribedAt: 1,
|
||||
},
|
||||
filter: {
|
||||
applicationId,
|
||||
openId,
|
||||
},
|
||||
indexFrom: 0,
|
||||
count: 1,
|
||||
},
|
||||
{ dontCollect: true },
|
||||
);
|
||||
|
||||
const now = Date.now();
|
||||
const data = {
|
||||
activeAt: now,
|
||||
};
|
||||
|
||||
const doUpdate = async () => {
|
||||
if (list && list.length > 0) {
|
||||
assert(list.length === 1);
|
||||
const wechatUser = list[0];
|
||||
|
||||
if (!wechatUser.subscribed) {
|
||||
Object.assign(data, {
|
||||
subscribed: true,
|
||||
subscribeAt: now,
|
||||
});
|
||||
}
|
||||
|
||||
return await context.operate('wechatUser', {
|
||||
action: 'update',
|
||||
data,
|
||||
filter: {
|
||||
id: wechatUser.id,
|
||||
},
|
||||
}, { dontCollect: true, dontCreateOper: true });
|
||||
}
|
||||
|
||||
Object.assign(data, {
|
||||
subscribed: true,
|
||||
subscribeAt: now,
|
||||
applicationId,
|
||||
openId,
|
||||
});
|
||||
|
||||
// 这里试着直接把user也创建出来,by Xc 20190720
|
||||
/**
|
||||
* 这里不能创建user,否则会出现一个weChatUser有openId和userId,却没有unionId
|
||||
* 当同一个user先从小程序登录,再从公众号登录时就会生成两个user
|
||||
*/
|
||||
/* return warden.insertEntity(tables.user, {
|
||||
state: UserState.normal,
|
||||
activeAt: Date.now(),
|
||||
}, txn).then(
|
||||
(user) => {
|
||||
assign(data, { userId: user.id });
|
||||
return warden.insertEntity(tables.weChatUser, data, txn);
|
||||
}
|
||||
);*/
|
||||
return await context.operate('wechatUser', {
|
||||
action: 'create',
|
||||
data,
|
||||
}, { dontCollect: true });
|
||||
};
|
||||
|
||||
if (eventKey) {
|
||||
// 如果带着场景值,需要查找对应的二维码,如果有公共逻辑在这里处理
|
||||
let sceneStr;
|
||||
if (eventKey.startsWith('qrscene_')) {
|
||||
sceneStr = eventKey.slice(eventKey.indexOf('qrscene_') + 8);
|
||||
}
|
||||
else {
|
||||
sceneStr = eventKey;
|
||||
}
|
||||
|
||||
// sceneStr是id压缩后的字符串
|
||||
const wcqId = expandUuidTo36Bytes(sceneStr);
|
||||
|
||||
const [weChatQrCode] = await context.select(
|
||||
'wechatQrCode',
|
||||
{
|
||||
data: {
|
||||
id: 1,
|
||||
entity: 1,
|
||||
entityId: 1,
|
||||
},
|
||||
filter: {
|
||||
id: wcqId,
|
||||
},
|
||||
indexFrom: 0,
|
||||
count: 10,
|
||||
},
|
||||
{ dontCollect: true },
|
||||
);
|
||||
if (weChatQrCode) {
|
||||
const { entity, entityId } = weChatQrCode;
|
||||
switch (entity) {
|
||||
case 'user': {
|
||||
// 裂变获得的用户
|
||||
if (list[0] && !list[0].userId) {
|
||||
Object.assign(data, { userId: entityId });
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'userEntityGrant': {
|
||||
// 授权过来的用户,推送接受分享的客服消息给他
|
||||
const [userEntityGrant] = await context.select(
|
||||
'userEntityGrant',
|
||||
{
|
||||
data: {
|
||||
id: 1,
|
||||
},
|
||||
filter: {
|
||||
id: entityId,
|
||||
}
|
||||
},
|
||||
{ dontCollect: true },
|
||||
);
|
||||
const { id, granter, expired, entity: entity2 } = userEntityGrant!;
|
||||
|
||||
const [domain] = await context.select('domain', {
|
||||
data: {
|
||||
id: 1,
|
||||
url: 1,
|
||||
apiPath: 1,
|
||||
protocol: 1,
|
||||
port: 1,
|
||||
},
|
||||
filter: {
|
||||
systemId: {
|
||||
$in: {
|
||||
entity: 'application',
|
||||
data: {
|
||||
systemId: 1,
|
||||
},
|
||||
filter: {
|
||||
id: applicationId,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
assert(domain, `处理userEntityGrant时,找不到对应的domain,applicationId是「${applicationId}」`);
|
||||
const url = composeDomainUrl(domain as EntityDict['domain']['Schema'], 'wechatQrCode/scan', {
|
||||
scene: sceneStr,
|
||||
});
|
||||
|
||||
assert(!expired); // 如果生成的wechatQrCode没过期,userEntityGrant就不可能过期。
|
||||
const name = granter!.name || granter!.nickname;
|
||||
const application = context.getApplication();
|
||||
const { type, config } = application!;
|
||||
assert(type === 'wechatPublic');
|
||||
const { appId, appSecret } = config as WechatPublicConfig;
|
||||
|
||||
const wechatInstance = WechatSDK.getInstance(
|
||||
appId,
|
||||
appSecret,
|
||||
'wechatPublic'
|
||||
) as WechatPublicInstance;
|
||||
|
||||
wechatInstance.sendServeMessage({
|
||||
openId,
|
||||
type: 'news',
|
||||
url,
|
||||
title: `${name}给您创建了一个授权`,
|
||||
description: '请接受',
|
||||
picurl: 'http://img95.699pic.com/element/40018/2473.png_860.png',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.warn(`线上有扫描二维码场景值,但找不到对应的qrCode,eventKey是${eventKey}`);
|
||||
}
|
||||
}
|
||||
await doUpdate();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
function onWeChatPublicEvent(data: WechatPublicEventData, context: BRC) {
|
||||
const { ToUserName, FromUserName, CreateTime, MsgType, Event, Content, EventKey } = data;
|
||||
|
||||
const appId = context.getApplicationId()!;
|
||||
let evt: string;
|
||||
|
||||
// 如果有应用注入的事件回调则处理之,不依赖其返回
|
||||
if (CALLBACK[appId]) {
|
||||
CALLBACK[appId](data, context);
|
||||
}
|
||||
if (Event) {
|
||||
const event = Event.toLowerCase();
|
||||
switch (event) {
|
||||
case 'subscribe':
|
||||
setUserSubscribed(FromUserName, EventKey, context);
|
||||
evt = `用户${FromUserName}关注公众号`;
|
||||
break;
|
||||
case 'scan':
|
||||
setUserSubscribed(FromUserName, EventKey, context);
|
||||
evt = `用户${FromUserName}再次扫描带${EventKey}键值的二维码`;
|
||||
break;
|
||||
case 'unsubscribe': {
|
||||
setUserUnsubscribed(FromUserName, context);
|
||||
evt = `用户${FromUserName}取关`;
|
||||
break;
|
||||
}
|
||||
case 'location': {
|
||||
evt = `用户${FromUserName}上传了地理位置信息`;
|
||||
break;
|
||||
}
|
||||
case 'click': {
|
||||
evt = `用户${FromUserName}点击菜单【${EventKey}】`;
|
||||
break;
|
||||
}
|
||||
case 'view': {
|
||||
evt = `用户${FromUserName}点击菜单跳转链接【${EventKey}】`;
|
||||
break;
|
||||
}
|
||||
case 'templatesendjobfinish': {
|
||||
// 模板消息发送完成,去更新对应的messageSent对象
|
||||
// 这个在线上测试没法通过,返回的msgId不符合,不知道为什么
|
||||
const { MsgID: msgId, Status: status, FromUserName: openId } = data;
|
||||
evt = `应用${appId}的用户${FromUserName}发来了${Event}事件,内容是${JSON.stringify(data)}`;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
evt = `应用${appId}的用户${FromUserName}发来了${Event}事件,内容是${JSON.stringify(data)}`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
console.log(evt);
|
||||
}
|
||||
return {
|
||||
content: '',
|
||||
contentType: 'application/text',
|
||||
};
|
||||
}
|
||||
|
||||
assert(MsgType);
|
||||
const content = '<xml>' +
|
||||
`<ToUserName>${FromUserName}</ToUserName>` +
|
||||
`<FromUserName>${ToUserName}</FromUserName>` +
|
||||
`<CreateTime>${CreateTime}</CreateTime>` +
|
||||
'<MsgType>transfer_customer_service</MsgType>' +
|
||||
'</xml>';
|
||||
|
||||
switch (MsgType) {
|
||||
case 'text':
|
||||
case 'link': {
|
||||
evt = `接收到来自用户的文字消息:${Content}`;
|
||||
break;
|
||||
}
|
||||
case 'image': {
|
||||
evt = `接收到来自用户的图片消息:${Content}`;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
evt = `接收到来自用户的${MsgType}型消息`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
console.log(evt);
|
||||
}
|
||||
return {
|
||||
content,
|
||||
contentType: 'application/xml',
|
||||
};
|
||||
}
|
||||
|
||||
const endpoints: Record<string, Endpoint<EntityDict, BRC>> = {
|
||||
wechatApi: {
|
||||
wechatPublicEvent: [{
|
||||
name: '微信公众号回调接口',
|
||||
method: 'post',
|
||||
params: ['appId'],
|
||||
fn: async (context, params, body, req, headers) => {
|
||||
const { appId } = params as { appId: string };
|
||||
throw new Error('not implemented yet');
|
||||
fn: async (context, params, headers, req, body) => {
|
||||
const { appId } = params;
|
||||
if (!appId) {
|
||||
throw new Error('applicationId参数不存在');
|
||||
}
|
||||
await context.setApplication(appId);
|
||||
const data = parser.toJson(body, { object: true });
|
||||
const { content, contentType } = onWeChatPublicEvent(data as any, context);
|
||||
return content;
|
||||
},
|
||||
},
|
||||
wechatVerify: {
|
||||
}, {
|
||||
name: '微信公众号验证接口',
|
||||
method: 'get',
|
||||
// params: ['applicationId'],
|
||||
params: ['appId'],
|
||||
fn: async (context, params, body, req, headers) => {
|
||||
const query = URL.parse(req.url as string, true, false).query as VerifyQuery;
|
||||
const { applicationId } = query;
|
||||
if (!applicationId) {
|
||||
const { searchParams } = new URL(Path.join('http://', req.headers.host!, req.url!));
|
||||
const { appId } = params;
|
||||
if (!appId) {
|
||||
throw new Error('applicationId参数不存在');
|
||||
}
|
||||
const [application] = await context.select(
|
||||
|
|
@ -54,23 +416,27 @@ const endpoints: Record<string, Endpoint<EntityDict, BRC>> = {
|
|||
config: 1,
|
||||
},
|
||||
filter: {
|
||||
id: applicationId,
|
||||
id: appId,
|
||||
},
|
||||
},
|
||||
{}
|
||||
);
|
||||
if (!application) {
|
||||
throw new Error(`未找到${applicationId}对应的app`);
|
||||
throw new Error(`未找到${appId}对应的app`);
|
||||
}
|
||||
const isWeChat = assertFromWeChat(query, application.config as WechatPublicConfig);
|
||||
const signature = searchParams.get('signature')!;
|
||||
const timestamp = searchParams.get('timestamp')!;
|
||||
const nonce = searchParams.get('nonce')!;
|
||||
const isWeChat = assertFromWeChat({ signature, timestamp, nonce }, application.config as WechatPublicConfig);
|
||||
if (isWeChat) {
|
||||
return query.echostr;
|
||||
const echostr = searchParams.get('echostr')!;
|
||||
return echostr;
|
||||
}
|
||||
else {
|
||||
throw new Error('Verify Failed');
|
||||
}
|
||||
},
|
||||
}
|
||||
}],
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { ActionDef } from 'oak-domain/lib/types/Action';
|
|||
import { LocaleDef } from 'oak-domain/lib/types/Locale';
|
||||
import { Index } from 'oak-domain/lib/types/Storage';
|
||||
import { Schema as ExtraFile } from './ExtraFile';
|
||||
import { Schema as System } from './System';
|
||||
import { Schema as WechatQrCode } from './WechatQrCode';
|
||||
import { EntityShape } from 'oak-domain/lib/types/Entity';
|
||||
|
||||
export interface Schema extends EntityShape {
|
||||
|
|
@ -18,6 +18,7 @@ export interface Schema extends EntityShape {
|
|||
idNumber?: String<32>;
|
||||
ref?: Schema;
|
||||
files: Array<ExtraFile>;
|
||||
codes: Array<WechatQrCode>;
|
||||
};
|
||||
|
||||
type IdAction = 'verify' | 'accept' | 'reject';
|
||||
|
|
@ -93,6 +94,7 @@ const locale: LocaleDef<Schema, Action, '', {
|
|||
files: '相关文件',
|
||||
userState: '用户状态',
|
||||
idState: '身份验证状态',
|
||||
codes: '微信分享二维码',
|
||||
},
|
||||
action: {
|
||||
activate: '激活',
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export interface Schema extends EntityShape {
|
|||
remark?: Text;
|
||||
granter: User;
|
||||
grantee?: User;
|
||||
files: Array<WechatQrCode>;
|
||||
codes: Array<WechatQrCode>;
|
||||
expiresAt?: Datetime;
|
||||
expired?: Boolean;
|
||||
redirectTo?: Object;
|
||||
|
|
@ -67,7 +67,7 @@ const locale: LocaleDef<
|
|||
remark: '备注',
|
||||
grantee: '领取人',
|
||||
granter: '授权人',
|
||||
files: '微信码',
|
||||
codes: '微信码',
|
||||
expired: '是否过期',
|
||||
expiresAt: '过期时间',
|
||||
redirectTo: '重定向页面',
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import watchers from './watchers';
|
|||
import data from "./data";
|
||||
export * from './exceptionHandlers';
|
||||
import { registerMessagePropsConverter } from './triggers/message';
|
||||
import { registerWeChatPublicEventCallback } from './endpoints';
|
||||
|
||||
export {
|
||||
checkers,
|
||||
|
|
@ -13,6 +14,7 @@ export {
|
|||
data,
|
||||
aspectDict,
|
||||
registerMessagePropsConverter,
|
||||
registerWeChatPublicEventCallback,
|
||||
};
|
||||
|
||||
export * from './types/Exception';
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@ export default OakComponent(
|
|||
props: 1,
|
||||
},
|
||||
isList: true,
|
||||
properties: {
|
||||
scene: String,
|
||||
},
|
||||
filters: [
|
||||
{
|
||||
filter: ({ props }) => {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,11 @@ const triggers: Trigger<EntityDict, 'userEntityGrant', RuntimeCxt>[] = [
|
|||
granterId: userId,
|
||||
expired: false,
|
||||
});
|
||||
if (!userEntityGrantData.expiresAt) {
|
||||
Object.assign(userEntityGrantData, {
|
||||
expiresAt: Date.now() + 300 * 1000,
|
||||
});
|
||||
}
|
||||
// 为之创建微信体系下的一个weChatQrCode
|
||||
await createWechatQrCode(
|
||||
{
|
||||
|
|
@ -38,6 +43,7 @@ const triggers: Trigger<EntityDict, 'userEntityGrant', RuntimeCxt>[] = [
|
|||
oakId: id,
|
||||
},
|
||||
},
|
||||
lifetimeLength: (<number>userEntityGrantData.expiresAt)! - Date.now() + 300 * 1000, // wechatQrCode的过期由trigger负责,可以放长一些
|
||||
},
|
||||
context as BackendRuntimeContext<EntityDict>
|
||||
);
|
||||
|
|
@ -216,6 +222,42 @@ const triggers: Trigger<EntityDict, 'userEntityGrant', RuntimeCxt>[] = [
|
|||
return 1;
|
||||
}
|
||||
}
|
||||
} as UpdateTrigger<EntityDict, 'userEntityGrant', RuntimeCxt>,
|
||||
{
|
||||
name: '当userEntityGrant过期时,使其相关的wechatQrCode也过期',
|
||||
entity: 'userEntityGrant',
|
||||
action: 'update',
|
||||
check: (operation) => {
|
||||
const { data } = operation;
|
||||
return !!(data.expired);
|
||||
},
|
||||
when: 'after',
|
||||
fn: async ({ operation }, context, params) => {
|
||||
const { filter } = operation;
|
||||
const wechatQrCodes = await context.select('wechatQrCode', {
|
||||
data: {
|
||||
id: 1,
|
||||
expired: 1,
|
||||
},
|
||||
filter: {
|
||||
userEntityGrant: filter,
|
||||
expired: false,
|
||||
},
|
||||
}, { dontCollect: true });
|
||||
const ids = wechatQrCodes.map(ele => ele.id);
|
||||
await context.operate('wechatQrCode', {
|
||||
action: 'update',
|
||||
data: {
|
||||
expired: true,
|
||||
},
|
||||
filter: {
|
||||
id: {
|
||||
$in: ids,
|
||||
},
|
||||
},
|
||||
}, { dontCollect: true });
|
||||
return ids.length;
|
||||
}
|
||||
} as UpdateTrigger<EntityDict, 'userEntityGrant', RuntimeCxt>
|
||||
];
|
||||
export default triggers;
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
import { Schema as Domain } from '../general-app-domain/Domain/Schema';
|
||||
|
||||
export function composeDomainUrl(domain: Domain, url?: string, props?: Record<string, string>) {
|
||||
const { port, protocol, apiPath, url: domainUrl } = domain;
|
||||
let Url = `${protocol}://${domainUrl}`;
|
||||
if (port) {
|
||||
Url += `:${port}`;
|
||||
}
|
||||
if (url) {
|
||||
Url += `/${url}`;
|
||||
if (props) {
|
||||
const k = Object.keys(props);
|
||||
if (k.length > 0) {
|
||||
Url += '?';
|
||||
for (const k2 of k) {
|
||||
Url += `${k2}=${encodeURI(props[k2])}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Url;
|
||||
}
|
||||
Loading…
Reference in New Issue