wechatQrCode的逻辑实现(部分)
This commit is contained in:
parent
b0ff174e44
commit
2f580ad8eb
|
|
@ -6,12 +6,18 @@ export declare abstract class GeneralRuntimeContext<ED extends EntityDict> exten
|
|||
private getTokenFn;
|
||||
private scene;
|
||||
constructor(store: RowStore<ED, GeneralRuntimeContext<ED>>, appId: string, getToken: () => Promise<string | undefined>, scene: string);
|
||||
getApplicationId(): string;
|
||||
getApplication(): Promise<import("oak-domain/lib/types").SelectRowShape<import("oak-app-domain/Application/Schema").Schema, {
|
||||
id: 1;
|
||||
name: 1;
|
||||
config: 1;
|
||||
type: 1;
|
||||
systemId: 1;
|
||||
system: {
|
||||
id: 1;
|
||||
name: 1;
|
||||
config: 1;
|
||||
};
|
||||
}>>;
|
||||
getToken(): Promise<import("oak-domain/lib/types").SelectRowShape<ED["token"]["Schema"], {
|
||||
id: 1;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@ class GeneralRuntimeContext extends UniversalContext_1.UniversalContext {
|
|||
this.getTokenFn = getToken;
|
||||
this.scene = scene;
|
||||
}
|
||||
getApplicationId() {
|
||||
return this.applicationId;
|
||||
}
|
||||
async getApplication() {
|
||||
const { result: [application] } = await this.rowStore.select('application', {
|
||||
data: {
|
||||
|
|
@ -20,6 +23,11 @@ class GeneralRuntimeContext extends UniversalContext_1.UniversalContext {
|
|||
config: 1,
|
||||
type: 1,
|
||||
systemId: 1,
|
||||
system: {
|
||||
id: 1,
|
||||
name: 1,
|
||||
config: 1,
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
id: this.applicationId,
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ async function getUploadInfo(params, context) {
|
|||
}, context);
|
||||
try {
|
||||
const { config: systemConfig } = system;
|
||||
const originConfig = systemConfig?.Cos[origin];
|
||||
const originConfig = systemConfig.Cos[origin];
|
||||
const instance = new ExternalUploadClazz[origin](originConfig);
|
||||
const uploadInfo = await instance.getUploadInfo(fileName);
|
||||
return uploadInfo;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
import { EntityDict } from "oak-app-domain";
|
||||
import { WechatQrCodeProps } from 'oak-app-domain/WechatQrCode/Schema';
|
||||
import { GeneralRuntimeContext } from "../RuntimeContext";
|
||||
export declare function createWechatQrCode<ED extends EntityDict, T extends keyof ED, Cxt extends GeneralRuntimeContext<ED>>(options: {
|
||||
entity: T;
|
||||
entityId: string;
|
||||
applicationId: string;
|
||||
tag?: string;
|
||||
lifetimeLength?: number;
|
||||
permanent?: boolean;
|
||||
props: WechatQrCodeProps;
|
||||
}, context: Cxt): Promise<Omit<Omit<import("oak-app-domain/WechatQrCode/Schema").OpSchema, "applicationId" | "entity" | "entityId">, import("oak-domain/lib/types").InstinctiveAttributes> & {
|
||||
id: string;
|
||||
} & {
|
||||
applicationId: string;
|
||||
application?: import("oak-app-domain/Application/Schema").UpdateOperation | undefined;
|
||||
} & {
|
||||
[K: string]: any;
|
||||
} & {
|
||||
[k: string]: any;
|
||||
}>;
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createWechatQrCode = void 0;
|
||||
const assert_1 = __importDefault(require("assert"));
|
||||
async function createWechatQrCode(options, context) {
|
||||
const { entity, entityId, applicationId, tag, lifetimeLength, permanent, props } = options;
|
||||
const { type: appType, config } = await context.getApplication();
|
||||
if (appType === 'wechatMp') {
|
||||
const { qrCodePrefix } = config;
|
||||
const id = await generateNewId();
|
||||
if (qrCodePrefix) {
|
||||
// 设置了域名跳转,优先使用域名 + id来生成对应的ur
|
||||
const data = {
|
||||
id,
|
||||
type: 'wechatMpDomainUrl',
|
||||
tag,
|
||||
entity,
|
||||
entityId,
|
||||
applicationId,
|
||||
permanent: true,
|
||||
url: `${qrCodePrefix}/id`,
|
||||
expired: false,
|
||||
props,
|
||||
};
|
||||
await context.rowStore.operate('wechatQrCode', {
|
||||
action: 'create',
|
||||
data,
|
||||
}, context);
|
||||
return data;
|
||||
}
|
||||
else {
|
||||
// 没有域名跳转,使用小程序码
|
||||
// todo这里如果有同组的公众号,应该优先使用公众号的关注链接
|
||||
const data = {
|
||||
id,
|
||||
type: 'wechatMpWxaCode',
|
||||
tag,
|
||||
entity,
|
||||
entityId,
|
||||
applicationId,
|
||||
permanent: false,
|
||||
expired: false,
|
||||
props,
|
||||
};
|
||||
await context.rowStore.operate('wechatQrCode', {
|
||||
action: 'create',
|
||||
data,
|
||||
}, context);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
else {
|
||||
(0, assert_1.default)(appType === 'wechatPublic');
|
||||
// 还未实现,记得
|
||||
throw new Error('method not implemented yet');
|
||||
}
|
||||
}
|
||||
exports.createWechatQrCode = createWechatQrCode;
|
||||
|
|
@ -1,2 +1,7 @@
|
|||
export declare const ROOT_ROLE_ID = "oak-root-role";
|
||||
export declare const ROOT_USER_ID = "oak-root-user";
|
||||
export declare const DefaultConfig: {
|
||||
userEntityGrant: {
|
||||
lifetimeLength: number;
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ROOT_USER_ID = exports.ROOT_ROLE_ID = void 0;
|
||||
exports.DefaultConfig = exports.ROOT_USER_ID = exports.ROOT_ROLE_ID = void 0;
|
||||
exports.ROOT_ROLE_ID = 'oak-root-role';
|
||||
exports.ROOT_USER_ID = 'oak-root-user';
|
||||
exports.DefaultConfig = {
|
||||
userEntityGrant: {
|
||||
lifetimeLength: 3600 * 1000,
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ export declare type WechatMpConfig = {
|
|||
type: 'wechatMp';
|
||||
appId: string;
|
||||
appSecret: string;
|
||||
qrCodePrefix?: string;
|
||||
};
|
||||
export declare type WebConfig = {
|
||||
type: 'web';
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { String, Text } from 'oak-domain/lib/types/DataType';
|
||||
import { EntityShape } from 'oak-domain/lib/types/Entity';
|
||||
export declare type SystemConfig = {
|
||||
Cos: {
|
||||
qiniu: {
|
||||
Cos?: {
|
||||
qiniu?: {
|
||||
accessKey: string;
|
||||
secretKey: string;
|
||||
uploadHost: string;
|
||||
|
|
@ -10,11 +10,14 @@ export declare type SystemConfig = {
|
|||
domain: string;
|
||||
};
|
||||
};
|
||||
Map: {
|
||||
amap: {
|
||||
Map?: {
|
||||
amap?: {
|
||||
webApiKey: string;
|
||||
};
|
||||
};
|
||||
UserEntityGrant?: {
|
||||
lifetimeLength: number;
|
||||
};
|
||||
};
|
||||
export interface Schema extends EntityShape {
|
||||
name: String<32>;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { String, Text } from 'oak-domain/lib/types/DataType';
|
||||
import { String, Text, Datetime } from 'oak-domain/lib/types/DataType';
|
||||
import { EntityShape } from 'oak-domain/lib/types/Entity';
|
||||
import { Schema as User } from './User';
|
||||
import { Schema as WechatQrCode } from './WechatQrCode';
|
||||
|
|
@ -8,8 +8,9 @@ export interface Schema extends EntityShape {
|
|||
relation: String<32>;
|
||||
action: String<32>;
|
||||
remark?: Text;
|
||||
uuid: String<32>;
|
||||
granter: User;
|
||||
grantee?: User;
|
||||
files: Array<WechatQrCode>;
|
||||
expiresAt?: Datetime;
|
||||
expired?: Boolean;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,5 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const IActionDef = {
|
||||
stm: {
|
||||
confirm: ['init', 'expire'],
|
||||
},
|
||||
is: 'init'
|
||||
};
|
||||
const indexes = [
|
||||
{
|
||||
name: 'index_entity_entityId',
|
||||
|
|
@ -22,8 +16,11 @@ const indexes = [
|
|||
name: 'index_uuid',
|
||||
attributes: [
|
||||
{
|
||||
name: 'uuid',
|
||||
name: 'expired',
|
||||
},
|
||||
{
|
||||
name: 'expiresAt',
|
||||
}
|
||||
],
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,18 +1,22 @@
|
|||
import { String, Text, Datetime, Boolean } from 'oak-domain/lib/types/DataType';
|
||||
import { EntityShape } from 'oak-domain/lib/types/Entity';
|
||||
import { Schema as Application } from './Application';
|
||||
export declare type WechatQrCodeProps = {
|
||||
pathname: string;
|
||||
props?: Record<string, any>;
|
||||
state?: Record<string, any>;
|
||||
};
|
||||
export interface Schema extends EntityShape {
|
||||
entity: String<32>;
|
||||
entityId: String<64>;
|
||||
type?: String<32>;
|
||||
expiresAt: Datetime;
|
||||
expired: Boolean;
|
||||
autoExtend: Boolean;
|
||||
sceneStr?: Text;
|
||||
type: 'wechatMpDomainUrl' | 'wechatMpWxaCode' | 'wechatPublic' | 'wechatPublicForMp';
|
||||
tag?: String<32>;
|
||||
expiresAt?: Datetime;
|
||||
expired?: Boolean;
|
||||
ticket?: Text;
|
||||
url?: String<64>;
|
||||
isPermanent: Boolean;
|
||||
permanent: Boolean;
|
||||
buffer?: Text;
|
||||
application: Application;
|
||||
props?: Object;
|
||||
props: WechatQrCodeProps;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const indexes = [
|
||||
{
|
||||
name: 'index_entity_entityId',
|
||||
name: 'index_entity_entityId_tag',
|
||||
attributes: [
|
||||
{
|
||||
name: 'entity',
|
||||
|
|
@ -10,6 +10,9 @@ const indexes = [
|
|||
{
|
||||
name: 'entityId',
|
||||
},
|
||||
{
|
||||
name: 'tag',
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { Feature } from 'oak-frontend-base';
|
|||
import { Aspect, Context, DeduceCreateOperationData } from 'oak-domain/lib/types';
|
||||
export declare class ExtraFile<ED extends EntityDict, Cxt extends Context<ED>, AD extends Record<string, Aspect<ED, Cxt>>> extends Feature<ED, Cxt, AD> {
|
||||
constructor();
|
||||
upload(extraFile: DeduceCreateOperationData<ED['extraFile']['Schema']>, scene: string): Promise<{
|
||||
upload(extraFile: DeduceCreateOperationData<EntityDict['extraFile']['OpSchema']>, scene: string): Promise<{
|
||||
url: string;
|
||||
bucket: string;
|
||||
}>;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { EntityDict as BaseEntityDict } from 'oak-app-domain/EntityDict';
|
||||
import { Trigger } from 'oak-domain/lib/types';
|
||||
declare const _default: (Trigger<BaseEntityDict, "address", import("..").GeneralRuntimeContext<BaseEntityDict>> | Trigger<BaseEntityDict, "user", import("..").GeneralRuntimeContext<BaseEntityDict>> | Trigger<BaseEntityDict, "userEntityGrant", import("..").GeneralRuntimeContext<BaseEntityDict>>)[];
|
||||
declare const _default: (Trigger<BaseEntityDict, "address", import("..").GeneralRuntimeContext<BaseEntityDict>> | Trigger<BaseEntityDict, "user", import("..").GeneralRuntimeContext<BaseEntityDict>> | Trigger<BaseEntityDict, "userEntityGrant", import("..").GeneralRuntimeContext<BaseEntityDict>> | Trigger<BaseEntityDict, "wechatQrCode", import("..").GeneralRuntimeContext<BaseEntityDict>>)[];
|
||||
export default _default;
|
||||
|
|
|
|||
|
|
@ -6,4 +6,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|||
const address_1 = __importDefault(require("./address"));
|
||||
const user_1 = __importDefault(require("./user"));
|
||||
const userEntityGrant_1 = __importDefault(require("./userEntityGrant"));
|
||||
exports.default = [...address_1.default, ...user_1.default, ...userEntityGrant_1.default];
|
||||
const wechatQrCode_1 = __importDefault(require("./wechatQrCode"));
|
||||
exports.default = [...address_1.default, ...user_1.default, ...userEntityGrant_1.default, ...wechatQrCode_1.default];
|
||||
|
|
|
|||
|
|
@ -6,9 +6,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|||
const lodash_1 = require("lodash");
|
||||
const types_1 = require("oak-domain/lib/types");
|
||||
const assert_1 = __importDefault(require("assert"));
|
||||
const constants_1 = require("../constants");
|
||||
const wechatQrCode_1 = require("../aspects/wechatQrCode");
|
||||
const triggers = [
|
||||
{
|
||||
name: '当创建userEntityGrant时,查询是否有未过期的实体',
|
||||
name: '当创建userEntityGrant时,查询是否有未过期可重用的对象',
|
||||
entity: 'userEntityGrant',
|
||||
action: 'create',
|
||||
when: 'before',
|
||||
|
|
@ -16,8 +18,9 @@ const triggers = [
|
|||
const { data, filter } = operation;
|
||||
const fn = async (userEntityGrantData) => {
|
||||
const { userId } = (await context.getToken());
|
||||
const { id: applicationId, config: appConfig, system: { config: SystemConfig } } = (await context.getApplication());
|
||||
(0, assert_1.default)(userId);
|
||||
const { action, entity, entityId, relation } = userEntityGrantData;
|
||||
const { action, entity, entityId, relation, id } = userEntityGrantData;
|
||||
const { result } = await context.rowStore.select('userEntityGrant', {
|
||||
data: {
|
||||
id: 1,
|
||||
|
|
@ -25,11 +28,14 @@ const triggers = [
|
|||
entity: 1,
|
||||
entityId: 1,
|
||||
relation: 1,
|
||||
iState: 1,
|
||||
expired: 1,
|
||||
granterId: 1,
|
||||
},
|
||||
filter: {
|
||||
iState: 'init',
|
||||
expired: false,
|
||||
expiresAt: {
|
||||
$gt: Date.now() - 600 * 1000,
|
||||
},
|
||||
action,
|
||||
entity,
|
||||
entityId,
|
||||
|
|
@ -42,9 +48,26 @@ const triggers = [
|
|||
if (result.length) {
|
||||
throw new types_1.OakCongruentRowExists(result[0], '有可重用的userEntityGrant');
|
||||
}
|
||||
const expiresAt = Date.now() + (SystemConfig.UserEntityGrant?.lifetimeLength || constants_1.DefaultConfig.userEntityGrant.lifetimeLength);
|
||||
(0, lodash_1.assign)(userEntityGrantData, {
|
||||
granterId: userId,
|
||||
expiresAt,
|
||||
expired: false,
|
||||
});
|
||||
// 如果是微信体系的应用,为之创建一个默认的weChatQrCode
|
||||
if (['wechatPublic', 'wechatMp'].includes(appConfig.type)) {
|
||||
await (0, wechatQrCode_1.createWechatQrCode)({
|
||||
entity: 'userEntityGrant',
|
||||
entityId: id,
|
||||
applicationId,
|
||||
props: {
|
||||
pathname: 'pages/userEntityGrant/confirm',
|
||||
props: {
|
||||
oakId: id,
|
||||
},
|
||||
}
|
||||
}, context);
|
||||
}
|
||||
};
|
||||
if (data instanceof Array) {
|
||||
(0, assert_1.default)('授权不存在一对多的情况');
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
import { EntityDict } from 'oak-app-domain/EntityDict';
|
||||
import { Trigger } from 'oak-domain/lib/types/Trigger';
|
||||
import { GeneralRuntimeContext } from '../RuntimeContext';
|
||||
declare const triggers: Trigger<EntityDict, 'wechatQrCode', GeneralRuntimeContext<EntityDict>>[];
|
||||
export default triggers;
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const assert_1 = __importDefault(require("assert"));
|
||||
const oak_wechat_sdk_1 = require("oak-wechat-sdk");
|
||||
const uuid_1 = require("oak-domain/lib/utils/uuid");
|
||||
const lodash_1 = require("lodash");
|
||||
const triggers = [
|
||||
{
|
||||
name: '选择userEntityGrant时,动态生成需要的数据',
|
||||
entity: 'wechatQrCode',
|
||||
action: 'select',
|
||||
when: 'after',
|
||||
fn: async ({ result }, context, params) => {
|
||||
let count = 0;
|
||||
const application = await context.getApplication();
|
||||
const { type, config } = application;
|
||||
(0, assert_1.default)(type === 'wechatMp' || config.type === 'wechatMp');
|
||||
const config2 = config;
|
||||
const { appId, appSecret } = config2;
|
||||
for (const code of result) {
|
||||
const { type, expired, url, id } = code;
|
||||
if (type === 'wechatMpWxaCode') {
|
||||
// 小程序码去实时获取(暂时不考虑缓存)
|
||||
const wechatInstance = oak_wechat_sdk_1.WechatSDK.getInstance(appId, appSecret, 'wechatMp');
|
||||
const buffer = await wechatInstance.getMpUnlimitWxaCode({
|
||||
scene: (0, uuid_1.shrinkUuidTo32Bytes)(id),
|
||||
page: 'pages/index/index', // todo,这里用其它的页面微信服务器拒绝,因为没发布。应该是 pages/wechatQrCode/scan/index
|
||||
});
|
||||
// 把arrayBuffer转成字符串返回
|
||||
const str = String.fromCharCode(...new Uint8Array(buffer));
|
||||
(0, lodash_1.assign)(code, {
|
||||
buffer: str,
|
||||
});
|
||||
}
|
||||
else if (expired) {
|
||||
// 如果过期了,在这里生成新的临时码并修改值(公众号)
|
||||
throw new Error('not implemented yet');
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
},
|
||||
];
|
||||
exports.default = triggers;
|
||||
|
|
@ -17,6 +17,10 @@ export abstract class GeneralRuntimeContext<ED extends EntityDict> extends Unive
|
|||
this.scene = scene;
|
||||
}
|
||||
|
||||
getApplicationId() {
|
||||
return this.applicationId;
|
||||
}
|
||||
|
||||
async getApplication () {
|
||||
const { result: [application] } = await this.rowStore.select('application', {
|
||||
data: {
|
||||
|
|
@ -24,12 +28,28 @@ export abstract class GeneralRuntimeContext<ED extends EntityDict> extends Unive
|
|||
name: 1,
|
||||
config: 1,
|
||||
type: 1,
|
||||
systemId: 1,
|
||||
systemId: 1,
|
||||
system: {
|
||||
id: 1,
|
||||
name: 1,
|
||||
config: 1,
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
id: this.applicationId,
|
||||
}
|
||||
}, this) as SelectionResult<EntityDict['application']['Schema'], {id: 1, name: 1, config: 1, type: 1, systemId: 1}>;
|
||||
}, this) as SelectionResult<EntityDict['application']['Schema'], {
|
||||
id: 1,
|
||||
name: 1,
|
||||
config: 1,
|
||||
type: 1,
|
||||
systemId: 1,
|
||||
system: {
|
||||
id: 1,
|
||||
name: 1,
|
||||
config: 1,
|
||||
},
|
||||
}>;
|
||||
|
||||
return application;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export async function getUploadInfo<ED extends EntityDict, Cxt extends GeneralRu
|
|||
}, context);
|
||||
try {
|
||||
const { config: systemConfig } = system;
|
||||
const originConfig = (systemConfig as SystemConfig)?.Cos[
|
||||
const originConfig = (systemConfig as SystemConfig).Cos![
|
||||
origin as keyof typeof systemConfig
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
import assert from "assert";
|
||||
import { EntityDict } from "oak-app-domain";
|
||||
import { WechatMpConfig } from "oak-app-domain/Application/Schema";
|
||||
import { CreateOperationData as CreateWechatQrcodeData, WechatQrCodeProps } from 'oak-app-domain/WechatQrCode/Schema';
|
||||
import { GeneralRuntimeContext } from "../RuntimeContext";
|
||||
|
||||
export async function createWechatQrCode<ED extends EntityDict, T extends keyof ED, Cxt extends GeneralRuntimeContext<ED>>(options: {
|
||||
entity: T;
|
||||
entityId: string;
|
||||
applicationId: string;
|
||||
tag?: string;
|
||||
lifetimeLength?: number;
|
||||
permanent?: boolean;
|
||||
props: WechatQrCodeProps;
|
||||
}, context: Cxt) {
|
||||
const { entity, entityId, applicationId, tag, lifetimeLength, permanent, props } = options;
|
||||
const { type: appType, config } = await context.getApplication();
|
||||
|
||||
if (appType === 'wechatMp') {
|
||||
const { qrCodePrefix } = (<WechatMpConfig>config);
|
||||
const id = await generateNewId();
|
||||
if (qrCodePrefix) {
|
||||
// 设置了域名跳转,优先使用域名 + id来生成对应的ur
|
||||
const data: CreateWechatQrcodeData = {
|
||||
id,
|
||||
type: 'wechatMpDomainUrl',
|
||||
tag,
|
||||
entity,
|
||||
entityId,
|
||||
applicationId,
|
||||
permanent: true,
|
||||
url: `${qrCodePrefix}/id`,
|
||||
expired: false,
|
||||
props,
|
||||
};
|
||||
await context.rowStore.operate('wechatQrCode', {
|
||||
action: 'create',
|
||||
data,
|
||||
}, context);
|
||||
|
||||
return data;
|
||||
}
|
||||
else {
|
||||
// 没有域名跳转,使用小程序码
|
||||
// todo这里如果有同组的公众号,应该优先使用公众号的关注链接
|
||||
const data: CreateWechatQrcodeData = {
|
||||
id,
|
||||
type: 'wechatMpWxaCode',
|
||||
tag,
|
||||
entity,
|
||||
entityId,
|
||||
applicationId,
|
||||
permanent: false,
|
||||
expired: false,
|
||||
props,
|
||||
};
|
||||
|
||||
await context.rowStore.operate('wechatQrCode', {
|
||||
action: 'create',
|
||||
data,
|
||||
}, context);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
else {
|
||||
assert(appType === 'wechatPublic');
|
||||
// 还未实现,记得
|
||||
throw new Error('method not implemented yet');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,2 +1,8 @@
|
|||
export const ROOT_ROLE_ID = 'oak-root-role';
|
||||
export const ROOT_USER_ID = 'oak-root-user';
|
||||
export const ROOT_USER_ID = 'oak-root-user';
|
||||
|
||||
export const DefaultConfig = {
|
||||
userEntityGrant: {
|
||||
lifetimeLength: 3600 * 1000,
|
||||
},
|
||||
};
|
||||
|
|
@ -7,6 +7,7 @@ export type WechatMpConfig = {
|
|||
type: 'wechatMp';
|
||||
appId: string;
|
||||
appSecret: string;
|
||||
qrCodePrefix?: string; // 扫描二维码跳转的前缀(在小程序后台配置,必须统一跳转到weCharQrCode/scan/index)
|
||||
};
|
||||
|
||||
export type WebConfig = {
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import { String, Int, Datetime, Image, Boolean, Text } from 'oak-domain/lib/type
|
|||
import { EntityShape } from 'oak-domain/lib/types/Entity';
|
||||
|
||||
export type SystemConfig = {
|
||||
Cos: {
|
||||
qiniu: {
|
||||
Cos?: {
|
||||
qiniu?: {
|
||||
accessKey: string;
|
||||
secretKey: string;
|
||||
uploadHost: string; //七牛上传域名
|
||||
|
|
@ -11,11 +11,14 @@ export type SystemConfig = {
|
|||
domain: string;
|
||||
};
|
||||
};
|
||||
Map: {
|
||||
amap: {
|
||||
Map?: {
|
||||
amap?: {
|
||||
webApiKey: string; // 高德访问rest服务接口的key
|
||||
};
|
||||
};
|
||||
UserEntityGrant?: {
|
||||
lifetimeLength: number; // 授权的过期时间(ms)
|
||||
};
|
||||
};
|
||||
|
||||
export interface Schema extends EntityShape {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import { String, Text } from 'oak-domain/lib/types/DataType';
|
||||
import { String, Text, Datetime } from 'oak-domain/lib/types/DataType';
|
||||
import { EntityShape } from 'oak-domain/lib/types/Entity';
|
||||
import { Index } from 'oak-domain/lib/types/Storage';
|
||||
import { Schema as User } from './User';
|
||||
import { Schema as WechatQrCode } from './WechatQrCode';
|
||||
import { ActionDef } from 'oak-domain/lib/types/Action';
|
||||
|
||||
export interface Schema extends EntityShape {
|
||||
entity: String<32>;
|
||||
|
|
@ -11,24 +10,13 @@ export interface Schema extends EntityShape {
|
|||
relation: String<32>;
|
||||
action: String<32>;
|
||||
remark?: Text;
|
||||
uuid: String<32>;
|
||||
granter: User;
|
||||
grantee?: User;
|
||||
files: Array<WechatQrCode>;
|
||||
expiresAt?: Datetime;
|
||||
expired?: Boolean;
|
||||
}
|
||||
|
||||
type IAction = 'confirm';
|
||||
type IState = | 'init'| 'expire';
|
||||
|
||||
const IActionDef: ActionDef<IAction, IState> = {
|
||||
stm: {
|
||||
confirm: ['init', 'expire'],
|
||||
},
|
||||
is: 'init'
|
||||
};
|
||||
|
||||
type Action = IAction;
|
||||
|
||||
const indexes: Index<Schema>[] = [
|
||||
{
|
||||
name: 'index_entity_entityId',
|
||||
|
|
@ -45,8 +33,11 @@ const indexes: Index<Schema>[] = [
|
|||
name: 'index_uuid',
|
||||
attributes: [
|
||||
{
|
||||
name: 'uuid',
|
||||
name: 'expired',
|
||||
},
|
||||
{
|
||||
name: 'expiresAt',
|
||||
}
|
||||
],
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -3,25 +3,30 @@ import { EntityShape } from 'oak-domain/lib/types/Entity';
|
|||
import { Index } from 'oak-domain/lib/types/Storage';
|
||||
import { Schema as Application } from './Application';
|
||||
|
||||
export type WechatQrCodeProps = {
|
||||
pathname: string;
|
||||
props?: Record<string, any>;
|
||||
state?: Record<string, any>;
|
||||
};
|
||||
|
||||
export interface Schema extends EntityShape {
|
||||
entity: String<32>;
|
||||
entityId: String<64>;
|
||||
type?: String<32>; //类型
|
||||
expiresAt: Datetime; // 过期时间
|
||||
expired: Boolean; //是否过期
|
||||
autoExtend: Boolean;
|
||||
sceneStr?: Text;
|
||||
type: 'wechatMpDomainUrl' | 'wechatMpWxaCode' | 'wechatPublic' | 'wechatPublicForMp',
|
||||
tag?: String<32>; // 调用者加的tag
|
||||
expiresAt?: Datetime; // 过期时间
|
||||
expired?: Boolean; //是否过期
|
||||
ticket?: Text;
|
||||
url?: String<64>;
|
||||
isPermanent: Boolean; //是否永久码
|
||||
permanent: Boolean; //是否永久码
|
||||
buffer?: Text; // 若没有url,使用buffer存储生成的小程序码数据(base64)
|
||||
application: Application;
|
||||
props?: Object;
|
||||
props: WechatQrCodeProps;
|
||||
}
|
||||
|
||||
const indexes: Index<Schema>[] = [
|
||||
{
|
||||
name: 'index_entity_entityId',
|
||||
name: 'index_entity_entityId_tag',
|
||||
attributes: [
|
||||
{
|
||||
name: 'entity',
|
||||
|
|
@ -29,6 +34,9 @@ const indexes: Index<Schema>[] = [
|
|||
{
|
||||
name: 'entityId',
|
||||
},
|
||||
{
|
||||
name: 'tag',
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export class ExtraFile<
|
|||
super();
|
||||
}
|
||||
@Action
|
||||
async upload(extraFile: DeduceCreateOperationData<ED['extraFile']['Schema']>, scene: string) {
|
||||
async upload(extraFile: DeduceCreateOperationData<EntityDict['extraFile']['OpSchema']>, scene: string) {
|
||||
try {
|
||||
const { origin, extra1: filePath, filename: fileName } = extraFile;
|
||||
const uploadInfo =
|
||||
|
|
|
|||
|
|
@ -4,5 +4,6 @@ import { Trigger } from 'oak-domain/lib/types';
|
|||
import addressTriggers from './address';
|
||||
import userTriggers from './user';
|
||||
import userEntityGrantTriggers from './userEntityGrant';
|
||||
import wechatQrCodeTriggers from './wechatQrCode';
|
||||
|
||||
export default [...addressTriggers, ...userTriggers, ...userEntityGrantTriggers];
|
||||
export default [...addressTriggers, ...userTriggers, ...userEntityGrantTriggers, ...wechatQrCodeTriggers];
|
||||
|
|
|
|||
|
|
@ -6,10 +6,12 @@ import { CreateOperationData as CreateUserEntityGrantData } from 'oak-app-domain
|
|||
import { assign, keys } from 'lodash';
|
||||
import { OakCongruentRowExists } from 'oak-domain/lib/types';
|
||||
import assert from 'assert';
|
||||
import { DefaultConfig } from '../constants';
|
||||
import { createWechatQrCode } from '../aspects/wechatQrCode';
|
||||
|
||||
const triggers: Trigger<EntityDict, 'userEntityGrant', GeneralRuntimeContext<EntityDict>>[] = [
|
||||
{
|
||||
name: '当创建userEntityGrant时,查询是否有未过期的实体',
|
||||
name: '当创建userEntityGrant时,查询是否有未过期可重用的对象',
|
||||
entity: 'userEntityGrant',
|
||||
action: 'create',
|
||||
when: 'before',
|
||||
|
|
@ -17,8 +19,9 @@ const triggers: Trigger<EntityDict, 'userEntityGrant', GeneralRuntimeContext<Ent
|
|||
const { data, filter } = operation;
|
||||
const fn = async (userEntityGrantData: CreateUserEntityGrantData) => {
|
||||
const { userId } = (await context.getToken())!;
|
||||
const { id: applicationId, config: appConfig, system: { config: SystemConfig }} = (await context.getApplication());
|
||||
assert(userId);
|
||||
const { action, entity, entityId, relation} = userEntityGrantData;
|
||||
const { action, entity, entityId, relation, id } = userEntityGrantData;
|
||||
const { result } = await context.rowStore.select('userEntityGrant', {
|
||||
data: {
|
||||
id: 1,
|
||||
|
|
@ -26,11 +29,14 @@ const triggers: Trigger<EntityDict, 'userEntityGrant', GeneralRuntimeContext<Ent
|
|||
entity: 1,
|
||||
entityId: 1,
|
||||
relation: 1,
|
||||
iState: 1,
|
||||
expired: 1,
|
||||
granterId: 1,
|
||||
},
|
||||
filter: {
|
||||
iState: 'init',
|
||||
expired: false,
|
||||
expiresAt: {
|
||||
$gt: Date.now() - 600 * 1000,
|
||||
}, // 至少有10分钟有效期的
|
||||
action,
|
||||
entity,
|
||||
entityId,
|
||||
|
|
@ -44,9 +50,27 @@ const triggers: Trigger<EntityDict, 'userEntityGrant', GeneralRuntimeContext<Ent
|
|||
throw new OakCongruentRowExists(result[0] as any, '有可重用的userEntityGrant');
|
||||
}
|
||||
|
||||
const expiresAt = Date.now() + (SystemConfig.UserEntityGrant?.lifetimeLength || DefaultConfig.userEntityGrant.lifetimeLength);
|
||||
|
||||
assign(userEntityGrantData, {
|
||||
granterId: userId,
|
||||
expiresAt,
|
||||
expired: false,
|
||||
});
|
||||
// 如果是微信体系的应用,为之创建一个默认的weChatQrCode
|
||||
if (['wechatPublic', 'wechatMp'].includes(appConfig.type)) {
|
||||
await createWechatQrCode({
|
||||
entity: 'userEntityGrant',
|
||||
entityId: id,
|
||||
applicationId,
|
||||
props: {
|
||||
pathname: 'pages/userEntityGrant/confirm',
|
||||
props: {
|
||||
oakId: id,
|
||||
},
|
||||
}
|
||||
}, context);
|
||||
}
|
||||
}
|
||||
if (data instanceof Array) {
|
||||
assert('授权不存在一对多的情况')
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
import { EntityDict } from 'oak-app-domain/EntityDict';
|
||||
import { SelectTriggerAfter, Trigger } from 'oak-domain/lib/types/Trigger';
|
||||
import { GeneralRuntimeContext } from '../RuntimeContext';
|
||||
|
||||
import assert from 'assert';
|
||||
import { WechatSDK } from 'oak-wechat-sdk';
|
||||
import { WechatMpConfig } from 'oak-app-domain/Application/Schema';
|
||||
import { shrinkUuidTo32Bytes } from 'oak-domain/lib/utils/uuid';
|
||||
import { assign } from 'lodash';
|
||||
|
||||
const triggers: Trigger<EntityDict, 'wechatQrCode', GeneralRuntimeContext<EntityDict>>[] = [
|
||||
{
|
||||
name: '选择userEntityGrant时,动态生成需要的数据',
|
||||
entity: 'wechatQrCode',
|
||||
action: 'select',
|
||||
when: 'after',
|
||||
fn: async ({ result }, context, params) => {
|
||||
let count = 0;
|
||||
const application = await context.getApplication();
|
||||
const { type, config } = application;
|
||||
|
||||
assert(type === 'wechatMp' || config.type === 'wechatMp');
|
||||
const config2 = config as WechatMpConfig;
|
||||
const { appId, appSecret } = config2;
|
||||
for (const code of result) {
|
||||
const { type, expired, url, id } = code;
|
||||
if (type === 'wechatMpWxaCode') {
|
||||
// 小程序码去实时获取(暂时不考虑缓存)
|
||||
const wechatInstance = WechatSDK.getInstance(appId, appSecret, 'wechatMp');
|
||||
const buffer = await wechatInstance.getMpUnlimitWxaCode({
|
||||
scene: shrinkUuidTo32Bytes(id),
|
||||
page: 'pages/index/index', // todo,这里用其它的页面微信服务器拒绝,因为没发布。应该是 pages/wechatQrCode/scan/index
|
||||
});
|
||||
// 把arrayBuffer转成字符串返回
|
||||
const str = String.fromCharCode(...new Uint8Array(buffer));
|
||||
assign(code, {
|
||||
buffer: str,
|
||||
});
|
||||
}
|
||||
else if (expired) {
|
||||
// 如果过期了,在这里生成新的临时码并修改值(公众号)
|
||||
throw new Error('not implemented yet');
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
} as SelectTriggerAfter<EntityDict, 'wechatQrCode', GeneralRuntimeContext<EntityDict>>,
|
||||
];
|
||||
export default triggers;
|
||||
|
|
@ -131,7 +131,8 @@ OakComponent({
|
|||
filename: filename,
|
||||
},
|
||||
beforeExecute: async (updateData) => {
|
||||
const { url, bucket } = await this.features.extraFile.upload(updateData as DeduceCreateOperationData<EntityDict['extraFile']['Schema']>, "extraFile:gallery:upload");
|
||||
const { url, bucket } = await this.features.extraFile.upload(
|
||||
updateData as DeduceCreateOperationData<EntityDict['extraFile']['Schema']>, "extraFile:gallery:upload");
|
||||
Object.assign(updateData, {
|
||||
bucket,
|
||||
extra1: url,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ OakPage({
|
|||
relation: 1,
|
||||
action: 1,
|
||||
remark: 1,
|
||||
uuid: 1,
|
||||
granterId: 1,
|
||||
granteeId: 1,
|
||||
wechatQrCode$entity: {
|
||||
|
|
@ -21,15 +20,9 @@ OakPage({
|
|||
type: 1,//类型
|
||||
expiresAt: 1,// 过期时间
|
||||
expired: 1, //是否过期
|
||||
autoExtend: 1,
|
||||
sceneStr: 1,
|
||||
ticket: 1,
|
||||
url: 1,
|
||||
isPermanent: 1, //是否永久码
|
||||
},
|
||||
filter: {
|
||||
entity: 'userEntityGrant',
|
||||
expired: false,
|
||||
buffer: 1,
|
||||
},
|
||||
indexFrom: 0,
|
||||
count: 1,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ OakPage({
|
|||
relation: 1,
|
||||
action: 1,
|
||||
remark: 1,
|
||||
uuid: 1,
|
||||
granterId: 1,
|
||||
granteeId: 1,
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue