模板消息配置

This commit is contained in:
wenjiarui 2023-10-11 18:59:52 +08:00
parent 986aa17d48
commit a4856cf0f0
45 changed files with 719 additions and 50 deletions

View File

@ -180,5 +180,8 @@ export type GeneralAspectDict<ED extends EntityDict, Cxt extends BackendRuntimeC
applicationId: string;
id: number;
}, context: Cxt) => Promise<any>;
syncMessageTemplate: (params: {
applicationId: string;
}, context: Cxt) => Promise<any>;
};
export default GeneralAspectDict;

View File

@ -3,6 +3,7 @@ import { getInfoByUrl } from './extraFile';
import { getApplication, signatureJsSDK, uploadWechatMedia } from './application';
import { updateConfig, updateApplicationConfig } from './config';
import { updateStyle } from './style2';
import { syncMessageTemplate } from './template';
import { mergeUser, getChangePasswordChannels, updateUserPassword } from './user';
import { createWechatLogin } from './wechatLogin';
import { unbindingWechat } from './wechatUser';
@ -52,5 +53,6 @@ declare const aspectDict: {
getTags: typeof getTags;
editTag: typeof editTag;
deleteTag: typeof deleteTag;
syncMessageTemplate: typeof syncMessageTemplate;
};
export default aspectDict;

View File

@ -3,6 +3,7 @@ import { getInfoByUrl } from './extraFile';
import { getApplication, signatureJsSDK, uploadWechatMedia, } from './application';
import { updateConfig, updateApplicationConfig } from './config';
import { updateStyle } from './style2';
import { syncMessageTemplate } from './template';
import { mergeUser, getChangePasswordChannels, updateUserPassword } from './user';
import { createWechatLogin } from './wechatLogin';
import { unbindingWechat } from './wechatUser';
@ -52,5 +53,6 @@ const aspectDict = {
getTags,
editTag,
deleteTag,
syncMessageTemplate
};
export default aspectDict;

12
es/aspects/template.d.ts vendored Normal file
View File

@ -0,0 +1,12 @@
import { BackendRuntimeContext } from '../context/BackendRuntimeContext';
import { EntityDict } from '../oak-app-domain';
export declare function syncMessageTemplate<ED extends EntityDict, Cxt extends BackendRuntimeContext<ED>>(params: {
applicationId: string;
}, context: Cxt): Promise<{
content: string;
deputy_industry?: string | undefined;
example?: string | undefined;
primary_industry?: string | undefined;
template_id: string;
title: string;
}[]>;

91
es/aspects/template.js Normal file
View File

@ -0,0 +1,91 @@
import { assert } from 'oak-domain/lib/utils/assert';
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
import { WechatSDK, } from 'oak-external-sdk';
function analyseContent(content) {
let content2 = content;
let result = {};
let pos1 = content2.indexOf('');
while (pos1 !== -1) {
let prefix = content2.slice(0, pos1);
let suffix = content2.slice(pos1 + 1);
let keyNameStart = prefix.lastIndexOf('}}');
let keyName = (prefix.slice(keyNameStart === -1 ? 0 : keyNameStart + 2)).trim();
let valueNameStart = suffix.indexOf('{{');
let valueNameEnd = suffix.indexOf('.');
let valueName = suffix.slice(valueNameStart + 2, valueNameEnd).trim();
Object.assign(result, {
[keyName]: valueName,
});
let nextStart = suffix.indexOf('}}');
content2 = suffix.slice(nextStart + 2);
pos1 = content2.indexOf('');
}
return result;
}
export async function syncMessageTemplate(params, context) {
const applicationId = params?.applicationId;
const [application] = await context.select('application', {
data: {
id: 1,
systemId: 1,
type: 1,
config: 1,
},
filter: {
id: applicationId,
}
}, {});
const { type, config } = application;
assert(type === 'wechatPublic', '当前只支持微信公众号的消息配置');
let appId, appSecret;
const config2 = config;
appId = config2.appId;
appSecret = config2.appSecret;
const wechatInstance = WechatSDK.getInstance(appId, 'wechatPublic', appSecret);
const callbackData = await wechatInstance.getAllPrivateTemplate();
const { template_list } = callbackData;
const WechatPublicTemplateList = await context.select('wechatPublicTemplate', {
data: {
id: 1,
wechatId: 1,
},
filter: {
applicationId,
}
}, {});
const existsTemplateIds = WechatPublicTemplateList.map((ele) => ele.wechatId);
const newTemplateList = template_list.filter((ele) => !existsTemplateIds.includes(ele.template_id));
const newTemplateIds = template_list.map(ele => ele.template_id);
const removeTemplateList = WechatPublicTemplateList.filter(ele => !newTemplateIds.includes(ele.wechatId));
for (const template of newTemplateList) {
await context.operate('wechatPublicTemplate', {
id: await generateNewIdAsync(),
action: 'create',
data: {
id: await generateNewIdAsync(),
applicationId,
wechatId: template.template_id,
title: template.title,
primaryIndustry: template.primary_industry,
deputyIndustry: template.deputy_industry,
content: template.content,
example: template.example,
syncAt: Date.now(),
param: analyseContent(template.content)
}
}, {});
}
if (removeTemplateList.length > 0) {
await context.operate('wechatPublicTemplate', {
id: await generateNewIdAsync(),
action: 'remove',
data: {},
filter: {
id: {
$in: removeTemplateList.map((ele) => ele.id)
}
}
}, {});
}
return template_list;
}

View File

@ -1,5 +1,6 @@
{
"detail": "应用详情",
"config": "配置管理",
"style": "样式管理"
"style": "样式管理",
"template": "模板消息管理"
}

View File

@ -6,4 +6,5 @@ export default function Render(props: WebComponentProps<EntityDict, 'application
config: EntityDict['application']['OpSchema']['config'];
name: string;
style: Style;
type: 'web' | 'wechatMp' | 'wechatPublic';
}>): import("react/jsx-runtime").JSX.Element | undefined;

View File

@ -3,12 +3,13 @@ import { Tabs } from 'antd';
import ApplicationDetail from '../detail';
import ConfigUpsert from '../../config/application';
import StyleUpsert from '../../config/style/platform';
import TemplateList from '../../messageTypeTemplateId/list';
import Styles from './web.pc.module.less';
export default function Render(props) {
const { id, config, oakFullpath, name, style } = props.data;
const { id, config, oakFullpath, name, style, type } = props.data;
const { t, update } = props.methods;
if (id && oakFullpath) {
return (_jsx("div", { className: Styles.container, children: _jsx(Tabs, { tabPosition: 'left', items: [
return (_jsx("div", { className: Styles.container, children: _jsx(Tabs, { tabPosition: 'left', items: type === 'wechatPublic' ? [
{
label: _jsx("div", { className: Styles.tabLabel, children: t('detail') }),
key: 'detail',
@ -22,7 +23,28 @@ export default function Render(props) {
{
label: _jsx("div", { className: Styles.tabLabel, children: t('style') }),
key: 'style',
children: (_jsx(StyleUpsert, { style: style, entity: 'platform', entityId: id, name: name })),
children: (_jsx(StyleUpsert, { style: style, entity: 'application', entityId: id, name: name })),
},
{
label: _jsx("div", { className: Styles.tabLabel, children: t('template') }),
key: 'template',
children: (_jsx(TemplateList, { oakAutoUnmount: true, oakPath: `templateUpsert-ApplicationId:${id}`, applicationId: id })),
},
] : [
{
label: _jsx("div", { className: Styles.tabLabel, children: t('detail') }),
key: 'detail',
children: (_jsx(ApplicationDetail, { oakId: id, oakPath: oakFullpath })),
},
{
label: _jsx("div", { className: Styles.tabLabel, children: t('config') }),
key: 'config',
children: (_jsx(ConfigUpsert, { entity: "application", entityId: id, config: config || {}, name: name, type: config?.type })),
},
{
label: _jsx("div", { className: Styles.tabLabel, children: t('style') }),
key: 'style',
children: (_jsx(StyleUpsert, { style: style, entity: 'application', entityId: id, name: name })),
},
] }) }));
}

View File

@ -58,4 +58,14 @@ export default OakComponent({
});
},
},
methods: {
async syncTemplate() {
const applicationId = this.props.applicationId;
await this.features.template.syncMessageTemplate(applicationId);
this.setMessage({
content: '操作成功',
type: 'success',
});
}
}
});

View File

@ -5,4 +5,6 @@ export default function Render(props: WebComponentProps<EntityDict, 'messageType
dirtyIds: string[];
messageTypes: string[];
applicationId: string;
}, {}>): import("react/jsx-runtime").JSX.Element;
}, {
syncTemplate: () => Promise<void>;
}>): import("react/jsx-runtime").JSX.Element;

View File

@ -3,13 +3,15 @@ import { Table, Button, Space, Typography, Input, Select } from 'antd';
import Styles from './web.module.less';
export default function Render(props) {
const { oakPagination, mtt = [], dirtyIds = [], oakLoading, messageTypes, applicationId, } = props.data;
const { setCurrentPage, setPageSize, t, addItem, removeItem, updateItem, recoverItem, resetItem, execute } = props.methods;
const { setCurrentPage, setPageSize, t, addItem, syncTemplate, removeItem, updateItem, recoverItem, resetItem, execute } = props.methods;
const { pageSize, total, currentPage } = oakPagination || {};
return (_jsxs("div", { className: Styles.container, children: [_jsxs(Space, { children: [_jsx(Button, { type: "default", onClick: () => {
/* addItem({
applicationId,
}); */
}, children: t('common::action.create') }), dirtyIds.length > 0 && (_jsx(Button, { type: "primary", onClick: () => {
}, children: t('common::action.create') }), _jsx(Button, { type: "default", onClick: async () => {
await syncTemplate();
}, children: '同步模板' }), dirtyIds.length > 0 && (_jsx(Button, { type: "primary", onClick: () => {
execute();
}, children: t('common::action.confirm') }))] }), _jsx(Table, { loading: oakLoading, dataSource: mtt, rowKey: "id", columns: [
{

View File

@ -133,7 +133,8 @@ const i18ns = [
data: {
"detail": "应用详情",
"config": "配置管理",
"style": "样式管理"
"style": "样式管理",
"template": "模板消息管理"
}
},
{

View File

@ -5,6 +5,7 @@ import { ExtraFile2 } from './extraFile2';
import { Application } from './application';
import { Config } from './config';
import { Style2 } from './style2';
import { Template } from './template';
import { WeiXinJsSdk } from './weiXinJsSdk';
import { WechatMenu } from './wechatMenu';
import { WechatPublicTag } from './wechatPublicTag';
@ -23,6 +24,7 @@ export type GeneralFeatures<ED extends EntityDict, Cxt extends BackendRuntimeCon
application: Application<ED, Cxt, FrontCxt, AD>;
config: Config<ED, Cxt, FrontCxt, AD>;
style2: Style2<ED, Cxt, FrontCxt, AD>;
template: Template<ED, Cxt, FrontCxt, AD>;
weiXinJsSdk: WeiXinJsSdk<ED, Cxt, FrontCxt, AD>;
theme: Theme<ED, Cxt, FrontCxt, AD>;
wechatMenu: WechatMenu<ED, Cxt, FrontCxt, AD>;

View File

@ -4,6 +4,7 @@ import { ExtraFile2 } from './extraFile2';
import { Application } from './application';
import { Config } from './config';
import { Style2 } from './style2';
import { Template } from './template';
import { WeiXinJsSdk } from './weiXinJsSdk';
import { WechatMenu } from './wechatMenu';
import { WechatPublicTag } from './wechatPublicTag';
@ -18,6 +19,7 @@ export function initialize(basicFeatures, type, domain) {
const extraFile2 = new ExtraFile2(basicFeatures.cache, application, basicFeatures.locales);
const config = new Config(basicFeatures.cache);
const style2 = new Style2(basicFeatures.cache);
const template = new Template(basicFeatures.cache);
const weiXinJsSdk = new WeiXinJsSdk(basicFeatures.cache, basicFeatures.localStorage, basicFeatures.environment);
const theme = new Theme(basicFeatures.cache, basicFeatures.localStorage);
return {
@ -27,6 +29,7 @@ export function initialize(basicFeatures, type, domain) {
application,
config,
style2,
template,
weiXinJsSdk,
theme,
wechatMenu,

12
es/features/template.d.ts vendored Normal file
View File

@ -0,0 +1,12 @@
import { Feature } from 'oak-frontend-base';
import AspectDict from '../aspects/AspectDict';
import { EntityDict } from '../oak-app-domain';
import { Cache } from 'oak-frontend-base/es/features/cache';
import { BackendRuntimeContext } from '../context/BackendRuntimeContext';
import { CommonAspectDict } from 'oak-common-aspect';
import { FrontendRuntimeContext } from '../context/FrontendRuntimeContext';
export declare class Template<ED extends EntityDict, Cxt extends BackendRuntimeContext<ED>, FrontCxt extends FrontendRuntimeContext<ED, Cxt, AD>, AD extends AspectDict<ED, Cxt> & CommonAspectDict<ED, Cxt>> extends Feature {
private cache;
constructor(cache: Cache<ED, Cxt, FrontCxt, AD & CommonAspectDict<ED, Cxt>>);
syncMessageTemplate(applicationId: string): Promise<void>;
}

15
es/features/template.js Normal file
View File

@ -0,0 +1,15 @@
import { Feature } from 'oak-frontend-base';
export class Template extends Feature {
cache;
constructor(cache) {
super();
this.cache = cache;
}
async syncMessageTemplate(applicationId) {
const result = await this.cache.exec('syncMessageTemplate', {
applicationId
});
console.log(result);
this.publish();
}
}

View File

@ -10,7 +10,7 @@ import { createComponent as createBaseComponent } from 'oak-frontend-base/es/pag
* @returns
*/
export async function subscribeMpMessage(messageTypes, haveToAccept, tip) {
const mttIds = this.features.cache.get('messageTypeTemplateId', {
const mttIds = this.features.cache.get('messageTypeTemplate', {
data: {
id: 1,
templateId: 1,

View File

@ -180,5 +180,8 @@ export type GeneralAspectDict<ED extends EntityDict, Cxt extends BackendRuntimeC
applicationId: string;
id: number;
}, context: Cxt) => Promise<any>;
syncMessageTemplate: (params: {
applicationId: string;
}, context: Cxt) => Promise<any>;
};
export default GeneralAspectDict;

View File

@ -3,6 +3,7 @@ import { getInfoByUrl } from './extraFile';
import { getApplication, signatureJsSDK, uploadWechatMedia } from './application';
import { updateConfig, updateApplicationConfig } from './config';
import { updateStyle } from './style2';
import { syncMessageTemplate } from './template';
import { mergeUser, getChangePasswordChannels, updateUserPassword } from './user';
import { createWechatLogin } from './wechatLogin';
import { unbindingWechat } from './wechatUser';
@ -52,5 +53,6 @@ declare const aspectDict: {
getTags: typeof getTags;
editTag: typeof editTag;
deleteTag: typeof deleteTag;
syncMessageTemplate: typeof syncMessageTemplate;
};
export default aspectDict;

View File

@ -5,6 +5,7 @@ const extraFile_1 = require("./extraFile");
const application_1 = require("./application");
const config_1 = require("./config");
const style2_1 = require("./style2");
const template_1 = require("./template");
const user_1 = require("./user");
const wechatLogin_1 = require("./wechatLogin");
const wechatUser_1 = require("./wechatUser");
@ -54,5 +55,6 @@ const aspectDict = {
getTags: wechatPublicTag_1.getTags,
editTag: wechatPublicTag_1.editTag,
deleteTag: wechatPublicTag_1.deleteTag,
syncMessageTemplate: template_1.syncMessageTemplate
};
exports.default = aspectDict;

12
lib/aspects/template.d.ts vendored Normal file
View File

@ -0,0 +1,12 @@
import { BackendRuntimeContext } from '../context/BackendRuntimeContext';
import { EntityDict } from '../oak-app-domain';
export declare function syncMessageTemplate<ED extends EntityDict, Cxt extends BackendRuntimeContext<ED>>(params: {
applicationId: string;
}, context: Cxt): Promise<{
content: string;
deputy_industry?: string | undefined;
example?: string | undefined;
primary_industry?: string | undefined;
template_id: string;
title: string;
}[]>;

95
lib/aspects/template.js Normal file
View File

@ -0,0 +1,95 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.syncMessageTemplate = void 0;
const assert_1 = require("oak-domain/lib/utils/assert");
const uuid_1 = require("oak-domain/lib/utils/uuid");
const oak_external_sdk_1 = require("oak-external-sdk");
function analyseContent(content) {
let content2 = content;
let result = {};
let pos1 = content2.indexOf('');
while (pos1 !== -1) {
let prefix = content2.slice(0, pos1);
let suffix = content2.slice(pos1 + 1);
let keyNameStart = prefix.lastIndexOf('}}');
let keyName = (prefix.slice(keyNameStart === -1 ? 0 : keyNameStart + 2)).trim();
let valueNameStart = suffix.indexOf('{{');
let valueNameEnd = suffix.indexOf('.');
let valueName = suffix.slice(valueNameStart + 2, valueNameEnd).trim();
Object.assign(result, {
[keyName]: valueName,
});
let nextStart = suffix.indexOf('}}');
content2 = suffix.slice(nextStart + 2);
pos1 = content2.indexOf('');
}
return result;
}
async function syncMessageTemplate(params, context) {
const applicationId = params?.applicationId;
const [application] = await context.select('application', {
data: {
id: 1,
systemId: 1,
type: 1,
config: 1,
},
filter: {
id: applicationId,
}
}, {});
const { type, config } = application;
(0, assert_1.assert)(type === 'wechatPublic', '当前只支持微信公众号的消息配置');
let appId, appSecret;
const config2 = config;
appId = config2.appId;
appSecret = config2.appSecret;
const wechatInstance = oak_external_sdk_1.WechatSDK.getInstance(appId, 'wechatPublic', appSecret);
const callbackData = await wechatInstance.getAllPrivateTemplate();
const { template_list } = callbackData;
const WechatPublicTemplateList = await context.select('wechatPublicTemplate', {
data: {
id: 1,
wechatId: 1,
},
filter: {
applicationId,
}
}, {});
const existsTemplateIds = WechatPublicTemplateList.map((ele) => ele.wechatId);
const newTemplateList = template_list.filter((ele) => !existsTemplateIds.includes(ele.template_id));
const newTemplateIds = template_list.map(ele => ele.template_id);
const removeTemplateList = WechatPublicTemplateList.filter(ele => !newTemplateIds.includes(ele.wechatId));
for (const template of newTemplateList) {
await context.operate('wechatPublicTemplate', {
id: await (0, uuid_1.generateNewIdAsync)(),
action: 'create',
data: {
id: await (0, uuid_1.generateNewIdAsync)(),
applicationId,
wechatId: template.template_id,
title: template.title,
primaryIndustry: template.primary_industry,
deputyIndustry: template.deputy_industry,
content: template.content,
example: template.example,
syncAt: Date.now(),
param: analyseContent(template.content)
}
}, {});
}
if (removeTemplateList.length > 0) {
await context.operate('wechatPublicTemplate', {
id: await (0, uuid_1.generateNewIdAsync)(),
action: 'remove',
data: {},
filter: {
id: {
$in: removeTemplateList.map((ele) => ele.id)
}
}
}, {});
}
return template_list;
}
exports.syncMessageTemplate = syncMessageTemplate;

View File

@ -1,5 +1,6 @@
{
"detail": "应用详情",
"config": "配置管理",
"style": "样式管理"
"style": "样式管理",
"template": "模板消息管理"
}

View File

@ -6,4 +6,5 @@ export default function Render(props: WebComponentProps<EntityDict, 'application
config: EntityDict['application']['OpSchema']['config'];
name: string;
style: Style;
type: 'web' | 'wechatMp' | 'wechatPublic';
}>): import("react/jsx-runtime").JSX.Element | undefined;

View File

@ -6,12 +6,13 @@ const antd_1 = require("antd");
const detail_1 = tslib_1.__importDefault(require("../detail"));
const application_1 = tslib_1.__importDefault(require("../../config/application"));
const platform_1 = tslib_1.__importDefault(require("../../config/style/platform"));
const list_1 = tslib_1.__importDefault(require("../../messageTypeTemplateId/list"));
const web_pc_module_less_1 = tslib_1.__importDefault(require("./web.pc.module.less"));
function Render(props) {
const { id, config, oakFullpath, name, style } = props.data;
const { id, config, oakFullpath, name, style, type } = props.data;
const { t, update } = props.methods;
if (id && oakFullpath) {
return ((0, jsx_runtime_1.jsx)("div", { className: web_pc_module_less_1.default.container, children: (0, jsx_runtime_1.jsx)(antd_1.Tabs, { tabPosition: 'left', items: [
return ((0, jsx_runtime_1.jsx)("div", { className: web_pc_module_less_1.default.container, children: (0, jsx_runtime_1.jsx)(antd_1.Tabs, { tabPosition: 'left', items: type === 'wechatPublic' ? [
{
label: (0, jsx_runtime_1.jsx)("div", { className: web_pc_module_less_1.default.tabLabel, children: t('detail') }),
key: 'detail',
@ -25,7 +26,28 @@ function Render(props) {
{
label: (0, jsx_runtime_1.jsx)("div", { className: web_pc_module_less_1.default.tabLabel, children: t('style') }),
key: 'style',
children: ((0, jsx_runtime_1.jsx)(platform_1.default, { style: style, entity: 'platform', entityId: id, name: name })),
children: ((0, jsx_runtime_1.jsx)(platform_1.default, { style: style, entity: 'application', entityId: id, name: name })),
},
{
label: (0, jsx_runtime_1.jsx)("div", { className: web_pc_module_less_1.default.tabLabel, children: t('template') }),
key: 'template',
children: ((0, jsx_runtime_1.jsx)(list_1.default, { oakAutoUnmount: true, oakPath: `templateUpsert-ApplicationId:${id}`, applicationId: id })),
},
] : [
{
label: (0, jsx_runtime_1.jsx)("div", { className: web_pc_module_less_1.default.tabLabel, children: t('detail') }),
key: 'detail',
children: ((0, jsx_runtime_1.jsx)(detail_1.default, { oakId: id, oakPath: oakFullpath })),
},
{
label: (0, jsx_runtime_1.jsx)("div", { className: web_pc_module_less_1.default.tabLabel, children: t('config') }),
key: 'config',
children: ((0, jsx_runtime_1.jsx)(application_1.default, { entity: "application", entityId: id, config: config || {}, name: name, type: config?.type })),
},
{
label: (0, jsx_runtime_1.jsx)("div", { className: web_pc_module_less_1.default.tabLabel, children: t('style') }),
key: 'style',
children: ((0, jsx_runtime_1.jsx)(platform_1.default, { style: style, entity: 'application', entityId: id, name: name })),
},
] }) }));
}

View File

@ -60,4 +60,14 @@ exports.default = OakComponent({
});
},
},
methods: {
async syncTemplate() {
const applicationId = this.props.applicationId;
await this.features.template.syncMessageTemplate(applicationId);
this.setMessage({
content: '操作成功',
type: 'success',
});
}
}
});

View File

@ -5,4 +5,6 @@ export default function Render(props: WebComponentProps<EntityDict, 'messageType
dirtyIds: string[];
messageTypes: string[];
applicationId: string;
}, {}>): import("react/jsx-runtime").JSX.Element;
}, {
syncTemplate: () => Promise<void>;
}>): import("react/jsx-runtime").JSX.Element;

View File

@ -6,13 +6,15 @@ const antd_1 = require("antd");
const web_module_less_1 = tslib_1.__importDefault(require("./web.module.less"));
function Render(props) {
const { oakPagination, mtt = [], dirtyIds = [], oakLoading, messageTypes, applicationId, } = props.data;
const { setCurrentPage, setPageSize, t, addItem, removeItem, updateItem, recoverItem, resetItem, execute } = props.methods;
const { setCurrentPage, setPageSize, t, addItem, syncTemplate, removeItem, updateItem, recoverItem, resetItem, execute } = props.methods;
const { pageSize, total, currentPage } = oakPagination || {};
return ((0, jsx_runtime_1.jsxs)("div", { className: web_module_less_1.default.container, children: [(0, jsx_runtime_1.jsxs)(antd_1.Space, { children: [(0, jsx_runtime_1.jsx)(antd_1.Button, { type: "default", onClick: () => {
/* addItem({
applicationId,
}); */
}, children: t('common::action.create') }), dirtyIds.length > 0 && ((0, jsx_runtime_1.jsx)(antd_1.Button, { type: "primary", onClick: () => {
}, children: t('common::action.create') }), (0, jsx_runtime_1.jsx)(antd_1.Button, { type: "default", onClick: async () => {
await syncTemplate();
}, children: '同步模板' }), dirtyIds.length > 0 && ((0, jsx_runtime_1.jsx)(antd_1.Button, { type: "primary", onClick: () => {
execute();
}, children: t('common::action.confirm') }))] }), (0, jsx_runtime_1.jsx)(antd_1.Table, { loading: oakLoading, dataSource: mtt, rowKey: "id", columns: [
{

View File

@ -135,7 +135,8 @@ const i18ns = [
data: {
"detail": "应用详情",
"config": "配置管理",
"style": "样式管理"
"style": "样式管理",
"template": "模板消息管理"
}
},
{

View File

@ -5,6 +5,7 @@ import { ExtraFile2 } from './extraFile2';
import { Application } from './application';
import { Config } from './config';
import { Style2 } from './style2';
import { Template } from './template';
import { WeiXinJsSdk } from './weiXinJsSdk';
import { WechatMenu } from './wechatMenu';
import { WechatPublicTag } from './wechatPublicTag';
@ -23,6 +24,7 @@ export type GeneralFeatures<ED extends EntityDict, Cxt extends BackendRuntimeCon
application: Application<ED, Cxt, FrontCxt, AD>;
config: Config<ED, Cxt, FrontCxt, AD>;
style2: Style2<ED, Cxt, FrontCxt, AD>;
template: Template<ED, Cxt, FrontCxt, AD>;
weiXinJsSdk: WeiXinJsSdk<ED, Cxt, FrontCxt, AD>;
theme: Theme<ED, Cxt, FrontCxt, AD>;
wechatMenu: WechatMenu<ED, Cxt, FrontCxt, AD>;

View File

@ -8,6 +8,7 @@ const extraFile2_1 = require("./extraFile2");
const application_1 = require("./application");
const config_1 = require("./config");
const style2_1 = require("./style2");
const template_1 = require("./template");
const weiXinJsSdk_1 = require("./weiXinJsSdk");
const wechatMenu_1 = require("./wechatMenu");
const wechatPublicTag_1 = require("./wechatPublicTag");
@ -22,6 +23,7 @@ function initialize(basicFeatures, type, domain) {
const extraFile2 = new extraFile2_1.ExtraFile2(basicFeatures.cache, application, basicFeatures.locales);
const config = new config_1.Config(basicFeatures.cache);
const style2 = new style2_1.Style2(basicFeatures.cache);
const template = new template_1.Template(basicFeatures.cache);
const weiXinJsSdk = new weiXinJsSdk_1.WeiXinJsSdk(basicFeatures.cache, basicFeatures.localStorage, basicFeatures.environment);
const theme = new theme_1.default(basicFeatures.cache, basicFeatures.localStorage);
return {
@ -31,6 +33,7 @@ function initialize(basicFeatures, type, domain) {
application,
config,
style2,
template,
weiXinJsSdk,
theme,
wechatMenu,

12
lib/features/template.d.ts vendored Normal file
View File

@ -0,0 +1,12 @@
import { Feature } from 'oak-frontend-base';
import AspectDict from '../aspects/AspectDict';
import { EntityDict } from '../oak-app-domain';
import { Cache } from 'oak-frontend-base/es/features/cache';
import { BackendRuntimeContext } from '../context/BackendRuntimeContext';
import { CommonAspectDict } from 'oak-common-aspect';
import { FrontendRuntimeContext } from '../context/FrontendRuntimeContext';
export declare class Template<ED extends EntityDict, Cxt extends BackendRuntimeContext<ED>, FrontCxt extends FrontendRuntimeContext<ED, Cxt, AD>, AD extends AspectDict<ED, Cxt> & CommonAspectDict<ED, Cxt>> extends Feature {
private cache;
constructor(cache: Cache<ED, Cxt, FrontCxt, AD & CommonAspectDict<ED, Cxt>>);
syncMessageTemplate(applicationId: string): Promise<void>;
}

19
lib/features/template.js Normal file
View File

@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Template = void 0;
const oak_frontend_base_1 = require("oak-frontend-base");
class Template extends oak_frontend_base_1.Feature {
cache;
constructor(cache) {
super();
this.cache = cache;
}
async syncMessageTemplate(applicationId) {
const result = await this.cache.exec('syncMessageTemplate', {
applicationId
});
console.log(result);
this.publish();
}
}
exports.Template = Template;

View File

@ -13,7 +13,7 @@ const page_mp_1 = require("oak-frontend-base/es/page.mp");
* @returns
*/
async function subscribeMpMessage(messageTypes, haveToAccept, tip) {
const mttIds = this.features.cache.get('messageTypeTemplateId', {
const mttIds = this.features.cache.get('messageTypeTemplate', {
data: {
id: 1,
templateId: 1,

View File

@ -290,6 +290,12 @@ export type GeneralAspectDict<
},
context: Cxt
) => Promise<any>;
syncMessageTemplate: (
params: {
applicationId: string;
},
context: Cxt
) => Promise<any>;
};
export default GeneralAspectDict;

View File

@ -19,6 +19,7 @@ import {
} from './application';
import { updateConfig, updateApplicationConfig } from './config';
import { updateStyle } from './style2';
import { syncMessageTemplate } from './template';
import { mergeUser, getChangePasswordChannels, updateUserPassword } from './user';
import { createWechatLogin } from './wechatLogin';
import { unbindingWechat } from './wechatUser';
@ -85,6 +86,7 @@ const aspectDict = {
getTags,
editTag,
deleteTag,
syncMessageTemplate
};
export default aspectDict;

133
src/aspects/template.ts Normal file
View File

@ -0,0 +1,133 @@
import { generateNewId } from 'oak-domain/lib/utils/uuid';
import { BackendRuntimeContext } from '../context/BackendRuntimeContext';
import { EntityDict } from '../oak-app-domain';
import { assert } from 'oak-domain/lib/utils/assert';
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
import {
WechatPublicInstance,
WechatSDK,
} from 'oak-external-sdk';
import {
WechatPublicConfig,
} from '../oak-app-domain/Application/Schema';
import { template } from 'oak-domain/lib/utils/string';
function analyseContent(content: string): Object {
let content2 = content;
let result = {};
let pos1 = content2.indexOf('');
while (pos1 !== -1) {
let prefix = content2.slice(0, pos1);
let suffix = content2.slice(pos1 + 1);
let keyNameStart = prefix.lastIndexOf('}}');
let keyName = (prefix.slice(keyNameStart === -1 ? 0 : keyNameStart + 2)).trim();
let valueNameStart = suffix.indexOf('{{');
let valueNameEnd = suffix.indexOf('.');
let valueName = suffix.slice(valueNameStart + 2, valueNameEnd).trim();
Object.assign(
result, {
[keyName]: valueName,
}
);
let nextStart = suffix.indexOf('}}');
content2 = suffix.slice(nextStart + 2);
pos1 = content2.indexOf('');
}
return result;
}
export async function syncMessageTemplate<ED extends EntityDict, Cxt extends BackendRuntimeContext<ED>>(params: {
applicationId: string,
}, context: Cxt) {
const applicationId = params?.applicationId;
const [application] = await context.select('application', {
data: {
id: 1,
systemId: 1,
type: 1,
config: 1,
},
filter: {
id: applicationId,
}
}, {});
const { type, config } = application!;
assert(type === 'wechatPublic', '当前只支持微信公众号的消息配置')
let appId: string, appSecret: string;
const config2 = config as WechatPublicConfig;
appId = config2.appId;
appSecret = config2.appSecret;
const wechatInstance = WechatSDK.getInstance(
appId,
'wechatPublic',
appSecret
) as WechatPublicInstance;
const callbackData = await wechatInstance.getAllPrivateTemplate();
const { template_list } = callbackData as {
template_list: {
content: string;
deputy_industry?: string;
example?: string;
primary_industry?: string;
template_id: string;
title: string;
}[]
};
const WechatPublicTemplateList = await context.select('wechatPublicTemplate',
{
data: {
id: 1,
wechatId: 1,
},
filter: {
applicationId,
}
},
{}
);
const existsTemplateIds = WechatPublicTemplateList.map((ele) => ele.wechatId);
const newTemplateList = template_list.filter((ele) => !existsTemplateIds.includes(ele.template_id))
const newTemplateIds = template_list.map(ele => ele.template_id);
const removeTemplateList = WechatPublicTemplateList.filter(ele => !newTemplateIds.includes(ele.wechatId!));
for (const template of newTemplateList) {
await context.operate('wechatPublicTemplate', {
id: await generateNewIdAsync(),
action: 'create',
data: {
id: await generateNewIdAsync(),
applicationId,
wechatId: template.template_id,
title: template.title,
primaryIndustry: template.primary_industry,
deputyIndustry: template.deputy_industry,
content: template.content,
example: template.example,
syncAt: Date.now(),
param: analyseContent(template.content)
}
}, {});
}
if (removeTemplateList.length > 0) {
await context.operate('wechatPublicTemplate', {
id: await generateNewIdAsync(),
action: 'remove',
data: {},
filter: {
id: {
$in: removeTemplateList.map((ele) => ele.id!)
}
}
}, {});
}
return template_list;
}

View File

@ -1,5 +1,6 @@
{
"detail": "应用详情",
"config": "配置管理",
"style": "样式管理"
"style": "样式管理",
"template": "模板消息管理"
}

View File

@ -4,6 +4,7 @@ import { WebComponentProps } from 'oak-frontend-base';
import ApplicationDetail from '../detail';
import ConfigUpsert from '../../config/application';
import StyleUpsert from '../../config/style/platform';
import TemplateList from '../../messageTypeTemplateId/list';
import { EntityDict } from '../../../oak-app-domain';
import { Style } from '../../../types/Style';
import Styles from './web.pc.module.less';
@ -13,8 +14,9 @@ export default function Render(props: WebComponentProps<EntityDict, 'application
config: EntityDict['application']['OpSchema']['config'];
name: string;
style: Style;
type: 'web' | 'wechatMp' | 'wechatPublic';
}>) {
const { id, config, oakFullpath, name, style } = props.data;
const { id, config, oakFullpath, name, style, type } = props.data;
const { t, update } = props.methods;
if (id && oakFullpath) {
@ -22,7 +24,7 @@ export default function Render(props: WebComponentProps<EntityDict, 'application
<div className={Styles.container}>
<Tabs
tabPosition='left'
items={[
items={type === 'wechatPublic' ? [
{
label: <div className={Styles.tabLabel}>{t('detail')}</div>,
key: 'detail',
@ -52,7 +54,54 @@ export default function Render(props: WebComponentProps<EntityDict, 'application
children: (
<StyleUpsert
style={style}
entity={'platform'}
entity={'application'}
entityId={id}
name={name}
/>
),
},
{
label: <div className={Styles.tabLabel}>{t('template')}</div>,
key: 'template',
children: (
<TemplateList
oakAutoUnmount={true}
oakPath={`templateUpsert-ApplicationId:${id}`}
applicationId={id}
/>
),
},
] : [
{
label: <div className={Styles.tabLabel}>{t('detail')}</div>,
key: 'detail',
children: (
<ApplicationDetail
oakId={id}
oakPath={oakFullpath}
/>
),
},
{
label: <div className={Styles.tabLabel}>{t('config')}</div>,
key: 'config',
children: (
<ConfigUpsert
entity="application"
entityId={id}
config={config || {}}
name={name}
type={config?.type}
/>
),
},
{
label: <div className={Styles.tabLabel}>{t('style')}</div>,
key: 'style',
children: (
<StyleUpsert
style={style}
entity={'application'}
entityId={id}
name={name}
/>

View File

@ -6,23 +6,31 @@ export default OakComponent({
projection: {
id: 1,
templateId: 1,
template: {
id: 1,
wechatId: 1,
title: 1,
},
type: 1,
},
properties: {
applicationId: '' as string,
},
data: {
wechatPublicTemplates: [] as Partial<EntityDict['wechatPublicTemplate']['Schema']>[],
},
formData({ data }) {
const operations = this.getOperations();
const dirtyIds = operations
? operations
.map(
(ele) =>
(
ele.operation
.data as EntityDict['messageTypeTemplate']['CreateSingle']['data']
)?.id || ele.operation.filter?.id
)
.filter((ele) => !!ele)
.map(
(ele) =>
(
ele.operation
.data as EntityDict['messageTypeTemplate']['CreateSingle']['data']
)?.id || ele.operation.filter?.id
)
.filter((ele) => !!ele)
: ([] as string[]);
const selectedTypes = data ? data.map((ele) => ele.type) : [];
@ -35,9 +43,8 @@ export default OakComponent({
})
.map((ele) => ele.type!)
.filter((ele: string) => !selectedTypes.includes(ele));
return {
mttIds: data,
mtt: data,
dirtyIds,
messageTypes,
};
@ -58,13 +65,52 @@ export default OakComponent({
},
],
lifetimes: {
ready() {
async ready() {
this.features.cache.refresh('messageType', {
data: {
id: 1,
type: 1,
},
});
const applicationId = this.props.applicationId;
const { data: wechatPublicTemplates } = await this.features.cache.refresh('wechatPublicTemplate', {
data: {
id: 1,
wechatId: 1,
title: 1,
},
filter: {
applicationId,
}
});
this.setState({
wechatPublicTemplates
})
},
},
methods: {
async syncTemplate() {
const applicationId = this.props.applicationId;
await this.features.template.syncMessageTemplate(applicationId!);
const { data: wechatPublicTemplates } = await this.features.cache.refresh('wechatPublicTemplate', {
data: {
id: 1,
wechatId: 1,
title: 1,
},
filter: {
applicationId,
}
});
this.setState({
wechatPublicTemplates
})
this.setMessage(
{
content: '操作成功',
type: 'success',
}
)
}
}
});

View File

@ -1,7 +1,7 @@
import { WebComponentProps } from "oak-frontend-base";
import { EntityDict } from '../../../oak-app-domain';
import * as React from 'react';
import { Table, Button, Space, Typography, Input, Select } from 'antd';
import React, { useState, useEffect, useRef } from 'react';
import { Table, Button, Space, Typography, Input, Select, Modal } from 'antd';
import Styles from './web.module.less';
export default function Render(props: WebComponentProps<
@ -9,13 +9,14 @@ export default function Render(props: WebComponentProps<
'messageTypeTemplate',
true,
{
mtt: EntityDict['messageTypeTemplate']['OpSchema'][];
mtt: EntityDict['messageTypeTemplate']['Schema'][];
wechatPublicTemplates: EntityDict['wechatPublicTemplate']['Schema'][];
dirtyIds: string[];
messageTypes: string[];
applicationId: string;
},
{
syncTemplate: () => Promise<void>;
}>) {
const {
@ -23,27 +24,42 @@ export default function Render(props: WebComponentProps<
mtt = [],
dirtyIds = [],
oakLoading,
messageTypes,
messageTypes = [],
applicationId,
wechatPublicTemplates = [],
} = props.data;
const { setCurrentPage, setPageSize, t, addItem,
const { setCurrentPage, setPageSize, t, addItem, syncTemplate,
removeItem, updateItem, recoverItem, resetItem, execute } = props.methods;
const [syncDisable, setSyncDisable] = useState(false);
const [open, setOpen] = useState(false);
const { pageSize, total, currentPage } = oakPagination || {};
console.log(messageTypes, wechatPublicTemplates);
return (
<div className={Styles.container}>
<Space>
<Button
type="default"
disabled={!(messageTypes.length > 0 && wechatPublicTemplates.length > 0)}
onClick={() => {
/* addItem({
applicationId,
}); */
addItem({
templateId: wechatPublicTemplates[0].id,
});
}}
>
{t('common::action.create')}
</Button>
<Button
type="default"
disabled={syncDisable}
onClick={async () => {
setSyncDisable(true);
await syncTemplate();
setSyncDisable(false);
}}
>
{'同步模板'}
</Button>
{
dirtyIds.length > 0 && (
<Button
@ -98,16 +114,25 @@ export default function Render(props: WebComponentProps<
},
{
dataIndex: 'templateId',
title: '模板消息Id',
title: '模板消息标题',
width: 300,
render: (value, record, index) => {
if (dirtyIds.includes(record.id) && !record.$$deleteAt$$) {
return (
<Input
<Select
style={{
width: '100%',
}}
value={value}
onChange={(e) => updateItem({
templateId: e.target.value,
type: e,
}, record.id)}
options={wechatPublicTemplates.map(
ele => ({
value: ele.id,
label: ele.title
})
)}
/>
);
}
@ -116,7 +141,7 @@ export default function Render(props: WebComponentProps<
type={!!record.$$deleteAt$$ ? 'danger' : undefined}
delete={!!record.$$deleteAt$$}
>
{value}
{record?.template?.title}
</Typography.Text>
);
},

View File

@ -135,7 +135,8 @@ const i18ns: I18n[] = [
data: {
"detail": "应用详情",
"config": "配置管理",
"style": "样式管理"
"style": "样式管理",
"template": "模板消息管理"
}
},
{

View File

@ -5,6 +5,7 @@ import { ExtraFile2 } from './extraFile2';
import { Application } from './application';
import { Config } from './config';
import { Style2 } from './style2';
import { Template } from './template';
import { WeiXinJsSdk } from './weiXinJsSdk';
import { WechatMenu } from './wechatMenu';
import { WechatPublicTag } from './wechatPublicTag'
@ -53,6 +54,7 @@ export function initialize<
const extraFile2 = new ExtraFile2<ED, Cxt, FrontCxt, AD>(basicFeatures.cache, application, basicFeatures.locales);
const config = new Config<ED, Cxt, FrontCxt, AD>(basicFeatures.cache);
const style2 = new Style2<ED, Cxt, FrontCxt, AD>(basicFeatures.cache);
const template = new Template<ED, Cxt, FrontCxt, AD>(basicFeatures.cache);
const weiXinJsSdk = new WeiXinJsSdk<ED, Cxt, FrontCxt, AD>(
basicFeatures.cache,
basicFeatures.localStorage,
@ -67,6 +69,7 @@ export function initialize<
application,
config,
style2,
template,
weiXinJsSdk,
theme,
wechatMenu,
@ -86,6 +89,7 @@ export type GeneralFeatures<
application: Application<ED, Cxt, FrontCxt, AD>;
config: Config<ED, Cxt, FrontCxt, AD>;
style2: Style2<ED, Cxt, FrontCxt, AD>;
template: Template<ED, Cxt, FrontCxt, AD>;
weiXinJsSdk: WeiXinJsSdk<ED, Cxt, FrontCxt, AD>;
theme: Theme<ED, Cxt, FrontCxt, AD>;
wechatMenu: WechatMenu<ED, Cxt, FrontCxt, AD>;

31
src/features/template.ts Normal file
View File

@ -0,0 +1,31 @@
import { Feature } from 'oak-frontend-base';
import AspectDict from '../aspects/AspectDict';
import { EntityDict } from '../oak-app-domain';
import { Cache } from 'oak-frontend-base/es/features/cache';
import { BackendRuntimeContext } from '../context/BackendRuntimeContext';
import { CommonAspectDict } from 'oak-common-aspect';
import { FrontendRuntimeContext } from '../context/FrontendRuntimeContext';
export class Template<
ED extends EntityDict,
Cxt extends BackendRuntimeContext<ED>,
FrontCxt extends FrontendRuntimeContext<ED, Cxt, AD>,
AD extends AspectDict<ED, Cxt> & CommonAspectDict<ED, Cxt>
> extends Feature {
private cache: Cache<ED, Cxt, FrontCxt, AD & CommonAspectDict<ED, Cxt>>;
constructor(
cache: Cache<ED, Cxt, FrontCxt, AD & CommonAspectDict<ED, Cxt>>
) {
super();
this.cache = cache;
}
async syncMessageTemplate(applicationId: string) {
const result = await this.cache.exec('syncMessageTemplate', {
applicationId
});
console.log(result);
this.publish();
}
}

View File

@ -42,7 +42,7 @@ export async function subscribeMpMessage<
TProperty,
TMethod
>, messageTypes: string[], haveToAccept?: boolean, tip?: string) {
const mttIds = this.features.cache.get('messageTypeTemplateId', {
const mttIds = this.features.cache.get('messageTypeTemplate', {
data: {
id: 1,
templateId: 1,
@ -50,6 +50,7 @@ export async function subscribeMpMessage<
},
filter: {
type: {
$in: messageTypes,
},
},
@ -171,7 +172,7 @@ export function createComponent<
},
applicationId,
},
});
});
}
}
ready && ready.call(this);