微信支付相关设置对象的管理

This commit is contained in:
Xu Chang 2024-06-03 23:52:59 +08:00
parent 771968ec6b
commit 23d7134152
333 changed files with 4798 additions and 418 deletions

View File

@ -1,5 +1,5 @@
import { BRC } from '@project/types/RuntimeCxt';
import { EntityDict } from '@project/oak-app-domain';
import { BRC } from '../types/RuntimeCxt';
import { EntityDict } from '../oak-app-domain';
export type AspectDict = {
getAccountPayRefunds: (params: {
accountId: string;

View File

@ -2,10 +2,12 @@ import aoCheckers from './accountOper';
import payCheckers from './pay';
import orderCheckers from './order';
import applicationCheckers from './application';
import offlineAccountCheckers from './offlineAccount';
const checkers = [
...aoCheckers,
...payCheckers,
...orderCheckers,
...applicationCheckers,
...offlineAccountCheckers,
];
export default checkers;

5
es/checkers/offlineAccount.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
import { Checker } from 'oak-domain/lib/types/Auth';
import { EntityDict } from '../oak-app-domain';
import { RuntimeCxt } from '../types/RuntimeCxt';
declare const checkers: Checker<EntityDict, 'offlineAccount', RuntimeCxt>[];
export default checkers;

View File

@ -0,0 +1,62 @@
import assert from 'assert';
import { OakAttrNotNullException, OakInputIllegalException } from 'oak-domain/lib/types';
import { pipeline } from 'oak-domain/lib/utils/executor';
function checkAttributes(data) {
const { type, channel, name, qrCode } = data;
switch (type) {
case 'bank': {
if (!channel || !name || !qrCode) {
throw new OakAttrNotNullException('offlineAccount', ['channel', 'name', 'qrCode'].filter(ele => !data[ele]));
}
break;
}
case 'shouqianba':
case 'wechat':
case 'alipay': {
if (!name && !qrCode) {
throw new OakInputIllegalException('offlineAccount', ['name', 'qrCode'], 'offlineAccount::error.nameQrCodeBothNull');
}
break;
}
case 'others': {
if (!name && !qrCode) {
throw new OakAttrNotNullException('offlineAccount', ['name', 'qrCode']);
}
if (!channel) {
throw new OakAttrNotNullException('offlineAccount', ['channel']);
}
}
}
}
const checkers = [
{
entity: 'offlineAccount',
action: 'create',
type: 'data',
checker(data) {
assert(!(data instanceof Array));
checkAttributes(data);
}
},
{
entity: 'offlineAccount',
action: 'update',
type: 'logicalData',
checker: (operation, context) => {
const { data, filter } = operation;
return pipeline(() => context.select('offlineAccount', {
data: {
id: 1,
type: 1,
channel: 1,
name: 1,
qrCode: 1,
},
filter
}, { dontCollect: true }), (accounts) => {
accounts.forEach((ele) => checkAttributes(Object.assign(ele, data)));
});
}
}
];
export default checkers;

View File

@ -1,5 +1,5 @@
import { Checker } from 'oak-domain/lib/types/Auth';
import { EntityDict } from '@project/oak-app-domain';
import { EntityDict } from '../oak-app-domain';
import { RuntimeCxt } from '../types/RuntimeCxt';
declare const checkers: Checker<EntityDict, 'pay', RuntimeCxt>[];
export default checkers;

View File

@ -1,5 +1,5 @@
import { Checker } from 'oak-domain/lib/types/Auth';
import { EntityDict } from '@project/oak-app-domain';
import { EntityDict } from '../oak-app-domain';
import { RuntimeCxt } from '../types/RuntimeCxt';
declare const checkers: Checker<EntityDict, 'refund', RuntimeCxt>[];
export default checkers;

View File

@ -2,7 +2,7 @@
* EntityDict的重新声明
* by Xc 20230807
*/
import { EntityDict } from '@project/oak-app-domain';
import { EntityDict } from '../oak-app-domain';
import { ReactComponentProps, ColumnProps, RowWithActions, OakExtraActionProps, OakAbsAttrDef, onActionFnDef, ListButtonProps, OakAbsAttrUpsertDef } from 'oak-frontend-base';
declare const FilterPanel: <T extends keyof EntityDict>(props: ReactComponentProps<EntityDict, T, false, {
entity: T;
@ -20,7 +20,7 @@ declare const List: <T extends keyof EntityDict>(props: ReactComponentProps<Enti
rowSelection?: any;
hideHeader?: boolean | undefined;
disableSerialNumber?: boolean | undefined;
size?: "small" | "large" | "middle" | undefined;
size?: "small" | "middle" | "large" | undefined;
scroll?: ({
x?: string | number | true | undefined;
y?: string | number | undefined;
@ -46,7 +46,7 @@ declare const ListPro: <T extends keyof EntityDict>(props: {
tablePagination?: any;
rowSelection?: any;
disableSerialNumber?: boolean | undefined;
size?: "small" | "large" | "middle" | undefined;
size?: "small" | "middle" | "large" | undefined;
scroll?: any;
locale?: any;
opWidth?: number | undefined;

View File

@ -1,7 +1,7 @@
import React from 'react';
import { WebComponentProps } from 'oak-frontend-base';
import { EntityDict } from '@project/oak-app-domain';
import { AccountPayConfig, PayConfig } from '@project/types/PayConfig';
import { EntityDict } from '../../../oak-app-domain';
import { AccountPayConfig, PayConfig } from '../../../types/PayConfig';
export default function Render(props: WebComponentProps<EntityDict, 'account', false, {
depositMax: number;
payConfig: PayConfig;

View File

@ -1,7 +1,7 @@
import React from 'react';
import { WebComponentProps } from 'oak-frontend-base';
import { EntityDict } from '@project/oak-app-domain';
import { AccountPayConfig, PayConfig } from '@project/types/PayConfig';
import { EntityDict } from '../../../oak-app-domain';
import { AccountPayConfig, PayConfig } from '../../../types/PayConfig';
export default function Render(props: WebComponentProps<EntityDict, 'account', false, {
depositMax: number;
payConfig: PayConfig;

View File

@ -1,6 +1,6 @@
import React from 'react';
import { RowWithActions, WebComponentProps } from 'oak-frontend-base';
import { EntityDict } from '@project/oak-app-domain';
import { EntityDict } from '../../../oak-app-domain';
export default function Render(props: WebComponentProps<EntityDict, 'account', false, {
account: RowWithActions<EntityDict, 'account'>;
depositMaxCent: number;

View File

@ -1,6 +1,6 @@
import React from 'react';
import { RowWithActions, WebComponentProps } from 'oak-frontend-base';
import { EntityDict } from '@project/oak-app-domain';
import { EntityDict } from '../../../oak-app-domain';
export default function Render(props: WebComponentProps<EntityDict, 'account', false, {
account: RowWithActions<EntityDict, 'account'>;
depositMaxCent: number;

View File

@ -1,5 +1,5 @@
import { RowWithActions, WebComponentProps } from 'oak-frontend-base';
import { EntityDict } from '@project/oak-app-domain';
import { EntityDict } from '../../../oak-app-domain';
export default function Render(props: WebComponentProps<EntityDict, 'accountOper', false, {
accountOpers: RowWithActions<EntityDict, 'accountOper'>[];
}>): string;

View File

@ -1,5 +1,5 @@
import { RowWithActions, WebComponentProps } from 'oak-frontend-base';
import { EntityDict } from '@project/oak-app-domain';
import { EntityDict } from '../../../oak-app-domain';
export default function Render(props: WebComponentProps<EntityDict, 'accountOper', false, {
accountOpers: RowWithActions<EntityDict, 'accountOper'>[];
}>): string;

View File

@ -1,5 +1,5 @@
/// <reference types="react" />
import { EntityDict } from "@project/oak-app-domain";
import { EntityDict } from "../../../oak-app-domain";
export default function Render(props: {
accountOpers: EntityDict['accountOper']['OpSchema'][];
t: (k: string) => string;

View File

@ -1,3 +1,4 @@
/// <reference types="wechat-miniprogram" />
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<import("../../../oak-app-domain").EntityDict, "offlineAccount", true, WechatMiniprogram.Component.DataOption>) => React.ReactElement;
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<import("../../../oak-app-domain").EntityDict, "offlineAccount", true, {
systemId: string;
}>) => React.ReactElement;
export default _default;

View File

@ -7,12 +7,27 @@ export default OakComponent({
channel: 1,
name: 1,
qrCode: 1,
desc: 1,
allowDeposit: 1,
allowPay: 1,
systemId: 1,
price: 1,
enabled: 1,
},
formData({ data }) {
properties: {
systemId: '',
},
formData({ data, legalActions }) {
return {
accounts: data,
accounts: data.map((ele) => {
const { type } = ele;
const color = this.features.style.getColor('offlineAccount', 'type', type);
return {
color,
...ele,
};
}),
canCreate: legalActions?.includes('create'),
};
}
},
actions: ['create', 'update', 'remove'],
});

View File

@ -0,0 +1,7 @@
{
"tips": "线下账户是需要system的相关工作人员手动同步收款和打款结果的账户",
"notnull": "属性\"%{value}\"不能为空",
"noData": "没有数据",
"confirmDelete": "确定删除",
"areYouSure": "确认删除本数据吗?"
}

View File

@ -2,5 +2,9 @@ import React from 'react';
import { EntityDict } from "../../../oak-app-domain";
import { RowWithActions, WebComponentProps } from "oak-frontend-base";
export default function render(props: WebComponentProps<EntityDict, 'offlineAccount', true, {
accounts: RowWithActions<EntityDict, 'offlineAccount'>[];
accounts?: (RowWithActions<EntityDict, 'offlineAccount'> & {
color: string;
})[];
systemId: string;
canCreate?: boolean;
}>): React.JSX.Element;

View File

@ -1,32 +1,110 @@
import React from 'react';
import { Button } from 'antd';
import React, { useState } from 'react';
import { Button, Modal, Alert, Descriptions, QRCode } from 'antd';
import { PlusCircleOutlined } from '@ant-design/icons';
import { Detail } from '../../../components/AbstractComponents';
import Styles from './web.pc.module.less';
import Upsert from '../upsert';
import { OakAttrNotNullException, OakException } from 'oak-domain/lib/types';
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
function OfflineAccount(props) {
const { account, t, onUpdate, onRemove, onQrCodeClick } = props;
const { type, channel, name, qrCode, allowDeposit, allowPay, color, enabled, price } = account;
const legalActions = account['#oakLegalActions'];
return (<Descriptions style={{ width: 280, height: 415 }} title={t(`offlineAccount:v.type.${type}`)} extra={<>
{legalActions.includes('update') && <Button style={{ marginRight: 4 }} size="small" onClick={() => onUpdate()}>
{t('common::action.update')}
</Button>}
{legalActions.includes('remove') && <Button danger size="small" onClick={() => onRemove()}>
{t('common::action.remove')}
</Button>}
</>} column={1} bordered size="small" labelStyle={{ width: 98 }}>
<Descriptions.Item label={t('offlineAccount:attr.type')}>{t(`offlineAccount:v.type.${type}`)}</Descriptions.Item>
{channel && <Descriptions.Item label={t(`offlineAccount::label.channel.${type}`)}>{channel}</Descriptions.Item>}
{name && <Descriptions.Item label={t(`offlineAccount::label.name.${type}`)}>{name}</Descriptions.Item>}
{qrCode && <Descriptions.Item label={t(`offlineAccount::label.qrCode.${type}`)}>
{type === 'bank' ? qrCode : <span className={Styles.qrCode} onClick={onQrCodeClick}><QRCode value={qrCode} size={name ? 80 : 128} color={color}/></span>}
</Descriptions.Item>}
<Descriptions.Item label={t('offlineAccount:attr.price')}>{price}</Descriptions.Item>
<Descriptions.Item label={t('offlineAccount:attr.allowDeposit')}>{t(`common::${allowDeposit}`)}</Descriptions.Item>
<Descriptions.Item label={t('offlineAccount:attr.allowPay')}>{t(`common::${allowPay}`)}</Descriptions.Item>
<Descriptions.Item label={t('offlineAccount:attr.enabled')}>{t(`common::${enabled}`)}</Descriptions.Item>
</Descriptions>);
}
export default function render(props) {
const { accounts } = props.data;
const { t } = props.methods;
if (accounts.length > 0) {
const { accounts, oakFullpath, oakExecutable, systemId, canCreate } = props.data;
const { t, addItem, execute, clean } = props.methods;
const [upsertId, setUpsertId] = useState('');
const getNotNullMessage = (attr) => {
if (['channel', 'name', 'qrCode'].includes(attr)) {
const upsertRow = accounts?.find(ele => ele.id === upsertId);
return t('notnull', { value: t(`offlineAccount::label.${attr}.${upsertRow.type}`) });
}
return t('notnull', { value: t(`offlineAccount:attr.${attr}`) });
};
const errMsg = oakExecutable instanceof OakException && (oakExecutable instanceof OakAttrNotNullException ? getNotNullMessage(oakExecutable.getAttributes()[0]) : t(oakExecutable.message));
const U = (<Modal destroyOnClose title={`${t('offlineAccount:name')}${t('common::action.add')}`} open={!!upsertId} onCancel={() => {
clean();
setUpsertId('');
}} closeIcon={null} onOk={async () => {
await execute();
setUpsertId('');
}} okButtonProps={{
disabled: oakExecutable !== true,
}} okText={t('common::confirm')} cancelText={(t('common::action.cancel'))}>
<div style={{ padding: 10 }}>
{errMsg && <Alert type="error" message={errMsg} style={{ marginBottom: 20 }}/>}
<Upsert oakPath={`${oakFullpath}.${upsertId}`} systemId={systemId}/>
</div>
</Modal>);
const [showQrCodeId, setShowQrCodeId] = useState('');
const showQrCodeRow = showQrCodeId ? accounts.find(ele => ele.id === showQrCodeId) : undefined;
if (accounts && accounts.length > 0) {
return (<div className={Styles.container}>
<Alert type='info' message={t('tips')}/>
{U}
<div className={Styles.list}>
{accounts.map((ele) => <div className={Styles.item}>
<Detail entity="offlineAccount" data={ele} attributes={[
'id',
'type',
'name',
'allowDeposit',
'allowPay'
]} column={1} bordered={true}/>
</div>)}
{accounts.filter(ele => ele.$$createAt$$ > 1).map((ele, idx) => <div className={Styles.item} key={idx}>
<OfflineAccount account={ele} t={t} onRemove={() => {
Modal.confirm({
title: t('confirmDelete'),
content: t('areYouSure'),
onOk: async () => execute(undefined, undefined, undefined, [
{
entity: 'offlineAccount',
operation: {
id: await generateNewIdAsync(),
action: 'remove',
data: {},
filter: {
id: ele.id,
},
}
}
]),
});
}} onUpdate={() => setUpsertId(ele.id)} onQrCodeClick={() => setShowQrCodeId(ele.id)}/>
</div>)}
</div>
<div className={Styles.btnBar}>
<Button>
{canCreate && <Button type="primary" onClick={() => {
const id = addItem({ systemId });
setUpsertId(id);
}}>
{t('common::action.add')}
</Button>
</Button>}
</div>
{showQrCodeRow && <Modal open={!!showQrCodeId} closeIcon={null} footer={null} onCancel={() => setShowQrCodeId('')}>
<QRCode value={showQrCodeRow.qrCode} size={480} color={showQrCodeRow.color}/>
</Modal>}
</div>);
}
return (<div className={Styles.container2}>
<Button icon={<PlusCircleOutlined style={{ fontSize: 50 }}/>}/>
<Alert type='info' message={t('tips')}/>
{U}
<div className={Styles.body}>
{canCreate ? <PlusCircleOutlined className={Styles.add} shape="circle" style={{ fontSize: 50 }} onClick={() => {
const id = addItem({ systemId });
setUpsertId(id);
}}/> : t('noData')}
</div>
</div>);
}

View File

@ -6,7 +6,8 @@ export default OakComponent({
channel: 1,
name: 1,
qrCode: 1,
desc: 1,
allowDeposit: 1,
allowPay: 1,
systemId: 1,
},
isList: false,
@ -15,4 +16,16 @@ export default OakComponent({
offlineAccount: data,
};
},
lifetimes: {
ready() {
if (this.isCreation()) {
this.update({
allowDeposit: false,
allowPay: false,
price: 0,
enabled: true,
});
}
}
}
});

View File

@ -1,13 +1,26 @@
{
"tips": "线下交易账户是需要人工同步收款和打款行为的账户",
"label": {
"channel": {
"bank": "银行及所属支行"
}
},
"placeholder": {
"channel": {
"bank": "银行-所属支行"
"bank": "银行/支行",
"others": "请输入途径名称"
},
"name": {
"bank": "输入户主姓名",
"alipay": "输入支付宝帐号",
"wechat": "输入微信号",
"shouqianba": "输入收钱吧商户号",
"others": "输入该收款途径的唯一账号"
},
"qrCode": {
"bank": "请输入银行账号",
"alipay": "请将二维码解析后的字符串填入",
"wechat": "请将二维码解析后的字符串填入",
"shouqianba": "请将二维码解析后的字符串填入",
"others": "请将二维码解析后的字符串填入"
}
},
"help": {
"allowDeposit": "是否允许用户在系统中主动向此账号发起充值",
"allowPay": "是否允许用户在系统中主动向此账号发起支付"
}
}

View File

@ -1,25 +1,61 @@
import React from 'react';
import { Alert, Form, Input, Select } from 'antd';
import { Form, Switch, Input, Select } from 'antd';
export default function render(props) {
const { offlineAccount } = props.data;
const { t, update } = props.methods;
if (offlineAccount) {
return (<Form labelCol={{ span: 6 }} wrapperCol={{ span: 12 }} layout="horizontal" style={{ minWidth: 600 }}>
<Alert type='info' message={t('tips')}/>
<Form.Item label={t('offlineAccount:attr.type')}>
<Select value={offlineAccount.type} options={['bank', 'alipay', 'wechat', 'shouqianba'].map(ele => ({
<Form.Item label={t('offlineAccount:attr.type')} required>
<Select value={offlineAccount.type} options={['bank', 'alipay', 'wechat', 'shouqianba', 'others'].map(ele => ({
label: t(`offlineAccount:v.type.${ele}`),
value: ele,
}))}/>
}))} onSelect={(value) => update({ type: value })}/>
</Form.Item>
{offlineAccount.type === 'bank' && <Form.Item label={t(`channle.${offlineAccount.type}`)}>
{['bank', 'others'].includes(offlineAccount.type) && <Form.Item label={t(`offlineAccount::label.channel.${offlineAccount.type}`)} required>
<Input value={offlineAccount.channel || ''} onChange={({ currentTarget }) => {
const { value } = currentTarget;
update({
channel: value,
});
}} placeholder={t('placeholder.channel.bank')}/>
}} placeholder={t(`placeholder.channel.${offlineAccount.type}`)}/>
</Form.Item>}
{!!offlineAccount.type && <Form.Item label={t(`offlineAccount::label.name.${offlineAccount.type}`)} required={['bank'].includes(offlineAccount.type)}>
<Input value={offlineAccount.name || ''} onChange={({ currentTarget }) => {
const { value } = currentTarget;
update({
name: value,
});
}} placeholder={t(`placeholder.name.${offlineAccount.type}`)}/>
</Form.Item>}
{!!offlineAccount.type && <Form.Item label={t(`offlineAccount::label.qrCode.${offlineAccount.type}`)} required={offlineAccount.type === 'bank'}>
{offlineAccount.type === 'bank' && <Input value={offlineAccount.qrCode || ''} onChange={({ currentTarget }) => {
const { value } = currentTarget;
update({
qrCode: value,
});
}} placeholder={t(`placeholder.qrCode.${offlineAccount.type}`)}/>}
{offlineAccount.type !== 'bank' && <Input.TextArea rows={8} value={offlineAccount.qrCode || ''} onChange={({ currentTarget }) => {
const { value } = currentTarget;
update({
qrCode: value,
});
}} placeholder={t(`placeholder.qrCode.${offlineAccount.type}`)}/>}
</Form.Item>}
{!!offlineAccount.type && <Form.Item label={t('offlineAccount:attr.allowDeposit')} required help={t('help.allowDeposit')}>
<Switch value={offlineAccount.allowDeposit} onChange={(allowDeposit) => {
update({ allowDeposit });
}}/>
</Form.Item>}
{!!offlineAccount.type && <Form.Item label={t('offlineAccount:attr.allowPay')} required help={t('help.allowPay')}>
<Switch value={offlineAccount.allowPay} onChange={(allowPay) => {
update({ allowPay });
}}/>
</Form.Item>}
<Form.Item label={t('offlineAccount:attr.enabled')} required>
<Switch value={offlineAccount.enabled} onChange={(enabled) => {
update({ enabled });
}}/>
</Form.Item>
</Form>);
}
}

View File

@ -1,4 +1,4 @@
import { EntityDict } from "@project/oak-app-domain";
import { EntityDict } from "../../../oak-app-domain";
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<EntityDict, "order", false, {
accountId: string;
accountAvailMax: number;

View File

@ -1,6 +1,6 @@
import React from 'react';
import { WebComponentProps } from 'oak-frontend-base';
import { EntityDict } from '@project/oak-app-domain';
import { EntityDict } from '../../../oak-app-domain';
import { OfflinePayConfig, PayConfig } from '../../../types/PayConfig';
export default function Render(props: WebComponentProps<EntityDict, keyof EntityDict, false, {
payConfig: PayConfig;

View File

@ -1,6 +1,6 @@
import React from 'react';
import { WebComponentProps } from 'oak-frontend-base';
import { EntityDict } from '@project/oak-app-domain';
import { EntityDict } from '../../../oak-app-domain';
import { OfflinePayConfig, PayConfig } from '../../../types/PayConfig';
export default function Render(props: WebComponentProps<EntityDict, keyof EntityDict, false, {
payConfig: PayConfig;

View File

@ -1,6 +1,6 @@
import React from 'react';
import { RowWithActions, WebComponentProps } from 'oak-frontend-base';
import { EntityDict } from '@project/oak-app-domain';
import { EntityDict } from '../../../oak-app-domain';
import { OfflinePayConfig, PayConfig } from '../../../types/PayConfig';
export declare function RenderOffline(props: {
pay: RowWithActions<EntityDict, 'pay'>;

View File

@ -1,6 +1,6 @@
import React from 'react';
import { RowWithActions, WebComponentProps } from 'oak-frontend-base';
import { EntityDict } from '@project/oak-app-domain';
import { EntityDict } from '../../../oak-app-domain';
import { OfflinePayConfig, PayConfig } from '../../../types/PayConfig';
export declare function RenderOffline(props: {
pay: RowWithActions<EntityDict, 'pay'>;

View File

@ -1,6 +1,6 @@
import React from 'react';
import { RowWithActions, WebComponentProps } from 'oak-frontend-base';
import { EntityDict } from '@project/oak-app-domain';
import { EntityDict } from '../../../oak-app-domain';
import { OfflinePayConfig } from '../../../types/PayConfig';
export default function Render(props: WebComponentProps<EntityDict, 'pay', false, {
pays: (RowWithActions<EntityDict, 'pay'> & {

View File

@ -1,10 +1,8 @@
import { composeServerUrl } from 'oak-general-business/es/utils/domain';
export default OakComponent({
entity: 'system',
isList: false,
projection: {
id: 1,
payConfig: 1,
wpAccount$system: {
$entity: 'wpAccount',
data: {
@ -17,25 +15,12 @@ export default OakComponent({
id: 1,
},
},
domain$system: {
$entity: 'domain',
data: {
id: 1,
protocol: 1,
url: 1,
apiPath: 1,
port: 1,
},
},
},
formData({ data, features }) {
const operation = this.state.oakFullpath && this.features.runningTree.getOperations(this.state.oakFullpath);
const domain = data && data.domain$system[0];
const serverUrl = domain && composeServerUrl(domain);
return {
operation: operation && operation[0].operation,
system: data,
serverUrl,
};
},
});

View File

@ -1,6 +1,11 @@
import React from 'react';
import { RowWithActions, WebComponentProps } from 'oak-frontend-base';
import { EntityDict } from '@project/oak-app-domain';
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity';
import { EntityDict } from '../../../oak-app-domain';
export declare function registerPayChannelConfigDict<ED extends EntityDict & BaseEntityDict, T extends keyof ED>(entity: T, component: (option: {
oakPath: string;
systemId: string;
}) => React.ReactElement): void;
export default function render(props: WebComponentProps<EntityDict, 'system', false, {
system: RowWithActions<EntityDict, 'system'>;
operation?: EntityDict['system']['Update'];

View File

@ -2,20 +2,37 @@ import React, { useState } from 'react';
import { Tabs } from 'antd';
import Styles from './web.pc.module.less';
import OfflineConfig from '../../offlineAccount/config';
import WpAccountConfig from '../../wpAccount/config';
const PayChannelConfigDict = {
'wpAccount': WpAccountConfig,
};
export function registerPayChannelConfigDict(entity, component) {
PayChannelConfigDict[entity] = component;
}
export default function render(props) {
const { system, oakFullpath, operation, oakDirty, serverUrl, oakExecutable } = props.data;
const { t, update, setMessage, execute } = props.methods;
const [key, setKey] = useState('');
if (system && oakFullpath) {
return (<div className={Styles.container}>
<Tabs tabPosition="left" items={[
<Tabs className={Styles.tabs} tabPosition="left" items={[
{
label: (<div className={Styles.systemLabel}>
{t('system')}
{t('offlineAccount:name')}
</div>),
key: 'offlineAccount',
children: (<OfflineConfig oakPath={`${oakFullpath}.offlineAccount$entity`}/>),
children: (<OfflineConfig oakPath={`${oakFullpath}.offlineAccount$system`} systemId={system.id}/>),
},
...Object.keys(PayChannelConfigDict).map((ele) => {
const C = PayChannelConfigDict[ele];
return {
label: (<div className={Styles.systemLabel}>
{t(`${ele}:name`)}
</div>),
key: 'ele',
children: <C oakPath={`${oakFullpath}.${ele}$system`} systemId={system.id}/>
};
})
]}/>
</div>);
}

View File

@ -1,4 +1,4 @@
import { AccountPayConfig } from '@project/types/PayConfig';
import { AccountPayConfig } from '../../../types/PayConfig';
import React from 'react';
export default function Account(props: {
config: AccountPayConfig;

View File

@ -1,5 +1,5 @@
import React from 'react';
import { PayConfig, ConfigBase } from '@project/types/PayConfig';
import { PayConfig, ConfigBase } from '../../../types/PayConfig';
type FC<T extends ConfigBase> = React.FC<{
config: T;
update: (config: Omit<T, 'channel'>) => void;

View File

@ -1,5 +1,5 @@
import React from 'react';
import { OfflinePayConfig } from '@project/types/PayConfig';
import { OfflinePayConfig } from '../../../types/PayConfig';
export default function Offline(props: {
config: OfflinePayConfig;
update: (config: Omit<OfflinePayConfig, 'channel'>) => void;

View File

@ -1,4 +1,4 @@
import { WechatPayConfig } from '@project/types/PayConfig';
import { WechatPayConfig } from '../../../types/PayConfig';
import React from 'react';
export default function WechatPay(props: {
config: WechatPayConfig;

View File

@ -0,0 +1,4 @@
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<import("../../../oak-app-domain").EntityDict, "wechatPay", false, {
systemId: string;
}>) => React.ReactElement;
export default _default;

View File

@ -0,0 +1,44 @@
import { composeServerUrl } from "oak-general-business/es/utils/domain";
export default OakComponent({
entity: 'wechatPay',
projection: {
id: 1,
refundNotifyUrl: 1,
payNotifyUrl: 1,
system: {
id: 1,
domain$system: {
$entity: 'domain',
data: {
id: 1,
protocol: 1,
url: 1,
apiPath: 1,
port: 1,
},
},
}
},
isList: false,
formData({ data }) {
const domain = data && data.system?.domain$system?.[0];
const serverUrl = domain && composeServerUrl(domain);
return {
wechatPay: data,
serverUrl,
};
},
properties: {
systemId: '',
},
lifetimes: {
ready() {
const { systemId } = this.props;
if (this.isCreation()) {
this.update({
systemId,
});
}
}
}
});

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1,7 @@
{
"placeholder": {
"lossRatio": "填百分比(0.6就代表千分之六)",
"payNotifyUrl": "endpoint",
"refundNotifyUrl": "endpoint"
}
}

View File

@ -0,0 +1,7 @@
import React from 'react';
import { EntityDict } from "../../../oak-app-domain";
import { RowWithActions, WebComponentProps } from "oak-frontend-base";
export default function render(props: WebComponentProps<EntityDict, 'wechatPay', false, {
wechatPay?: (RowWithActions<EntityDict, 'wechatPay'>);
serverUrl: string;
}>): React.JSX.Element | null;

View File

@ -0,0 +1,23 @@
import React from 'react';
import { Form, Input } from 'antd';
export default function render(props) {
const { wechatPay, serverUrl } = props.data;
const { t, update } = props.methods;
if (wechatPay) {
return (<>
<Form.Item label={t('wechatPay:attr.payNotifyUrl')}>
<Input prefix={`${serverUrl}/endpoint`} suffix='/${payId}' value={wechatPay.payNotifyUrl} placeholder={t('placeholder.payNotifyUrl')} onChange={({ currentTarget }) => {
const payNotifyUrl = currentTarget.value;
update({ payNotifyUrl });
}}/>
</Form.Item>
<Form.Item label={t('wechatPay:attr.refundNotifyUrl')}>
<Input prefix={`${serverUrl}/endpoint`} suffix='/${refundId}' value={wechatPay.refundNotifyUrl} placeholder={t('placeholder.refundNotifyUrl')} onChange={({ currentTarget }) => {
const refundNotifyUrl = currentTarget.value;
update({ refundNotifyUrl });
}}/>
</Form.Item>
</>);
}
return null;
}

View File

@ -1,5 +1,5 @@
/// <reference types="react" />
import { EntityDict } from '@project/oak-app-domain';
import { EntityDict } from '../../../oak-app-domain';
export default function Detail(props: {
createAt?: string;
withdrawMethod?: 'refund' | 'channel';

View File

@ -18,7 +18,7 @@ export default function Detail(props) {
<Steps current={step}>
<Steps.Step title={<span className={step >= 0 ? classNames(Styles.label, Styles.active) : Styles.label}>{t('steps.1.title')}</span>} description={<span className={step >= 0 ? classNames(Styles.label, Styles.active) : Styles.label}>{createAt}</span>} key="1"/>
<Steps.Step title={<span className={step >= 1 ? classNames(Styles.label, Styles.active) : Styles.label}>{t('steps.2.title')}</span>} description={<span className={step >= 1 ? classNames(Styles.label, Styles.active) : Styles.label}>{t(`method.v.${withdrawMethod}`)}</span>} key="2"/>
<Steps.Step title={<span className={step >= 2 ? classNames(Styles.label, iState === 'failed' ? Styles.failed : Styles.success) : Styles.label}>{iState === 'failed' ? t('steps.3.failed') : (iState === 'partiallySuccess' ? t('steps.3.partiallySuccess') : t('steps.3.success'))}</span>} key="3"/>
<Steps.Step title={<span className={step >= 2 ? classNames(Styles.label, iState === 'failed' ? Styles.failed : Styles.success) : Styles.label}>{iState === 'failed' ? t('steps.3.failed') : (iState === 'partiallySuccessful' ? t('steps.3.partiallySuccess') : t('steps.3.success'))}</span>} key="3"/>
</Steps>
</div>
{refundData.map((data, idx) => (<div className={Styles.refundItem} key={idx}>

View File

@ -1,5 +1,5 @@
/// <reference types="react" />
import { EntityDict } from '@project/oak-app-domain';
import { EntityDict } from '../../../oak-app-domain';
export default function Detail(props: {
createAt?: string;
withdrawMethod?: 'refund' | 'channel';

View File

@ -1,5 +1,5 @@
/// <reference types="react" />
import { EntityDict } from "@project/oak-app-domain";
import { EntityDict } from "../../../oak-app-domain";
import { RowWithActions, WebComponentProps } from "oak-frontend-base";
export default function render(props: WebComponentProps<EntityDict, 'withdrawChannel', true, {
channels: RowWithActions<EntityDict, 'withdrawChannel'>[];

View File

@ -1,5 +1,5 @@
/// <reference types="react" />
import { EntityDict } from "@project/oak-app-domain";
import { EntityDict } from "../../../oak-app-domain";
import { RowWithActions, WebComponentProps } from "oak-frontend-base";
export default function render(props: WebComponentProps<EntityDict, 'withdrawChannel', false, {
channel: RowWithActions<EntityDict, 'withdrawChannel'>;

View File

@ -0,0 +1,3 @@
/// <reference types="wechat-miniprogram" />
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<import("../../../oak-app-domain").EntityDict, "wpAccount", true, WechatMiniprogram.Component.DataOption>) => React.ReactElement;
export default _default;

View File

@ -0,0 +1,18 @@
export default OakComponent({
entity: 'wpAccount',
isList: true,
projection: {
id: 1,
price: 1,
mchId: 1,
refundGapDays: 1,
enabled: 1,
},
formData({ data, legalActions }) {
return {
accounts: data,
canCreate: legalActions?.includes('create'),
};
},
actions: ['create', 'update', 'remove'],
});

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1,7 @@
{
"tips": "微信支付账户是用来配置微信支付的账户相关信息",
"notnull": "属性\"%{value}\"不能为空",
"noData": "没有数据",
"confirmDelete": "确定删除",
"areYouSure": "确认删除本数据吗?"
}

View File

@ -0,0 +1,8 @@
import React from 'react';
import { EntityDict } from "../../../oak-app-domain";
import { RowWithActions, WebComponentProps } from "oak-frontend-base";
export default function render(props: WebComponentProps<EntityDict, 'wpAccount', true, {
accounts?: (RowWithActions<EntityDict, 'wpAccount'> & {})[];
systemId: string;
canCreate?: boolean;
}>): React.JSX.Element;

View File

@ -0,0 +1,110 @@
import React, { useState } from 'react';
import { Button, Modal, Alert, Descriptions, Tabs } from 'antd';
import { PlusCircleOutlined } from '@ant-design/icons';
import Styles from './web.pc.module.less';
import Upsert from '../upsert';
import WpProductConfig from '../../wpProduct/config';
import { OakAttrNotNullException, OakException } from 'oak-domain/lib/types';
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
function WpAccount(props) {
const { account, t, onUpdate, onRemove, oakFullpath, systemId } = props;
const { refundGapDays, mchId, enabled, price } = account;
const legalActions = account['#oakLegalActions'];
const [activeKey, setActiveKey] = useState("1");
return (<Tabs activeKey={activeKey} onTabClick={(activeKey) => {
setActiveKey(activeKey);
}} tabBarExtraContent={activeKey === "1" && <>
{legalActions.includes('update') && <Button style={{ marginRight: 4 }} size="small" onClick={() => onUpdate()}>
{t('common::action.update')}
</Button>}
{legalActions.includes('remove') && <Button danger size="small" onClick={() => onRemove()}>
{t('common::action.remove')}
</Button>}
</>} style={{ width: 380, height: 520 }} items={[
{
key: '1',
label: t('common::action.detail'),
children: (<Descriptions column={1} bordered size="small">
<Descriptions.Item label={t('wpAccount:attr.mchId')}>{mchId}</Descriptions.Item>
<Descriptions.Item label={t('wpAccount:attr.price')}>{price}</Descriptions.Item>
<Descriptions.Item label={t('wpAccount:attr.refundGapDays')}>{refundGapDays}</Descriptions.Item>
<Descriptions.Item label={t('wpAccount:attr.enabled')}>{t(`common::${enabled}`)}</Descriptions.Item>
</Descriptions>)
},
{
key: '2',
label: t('wpProduct:name'),
children: (<WpProductConfig oakPath={`$$wpProduct-${account.id}`} systemId={systemId} wpAccountId={account.id}/>)
}
]}/>);
}
export default function render(props) {
const { accounts, oakFullpath, oakExecutable, canCreate, systemId } = props.data;
const { t, addItem, execute, clean } = props.methods;
const getNotNullMessage = (entity, attr) => {
return t('notnull', { value: t(`${entity}:attr.${attr}`) });
};
const errMsg = oakExecutable instanceof OakException && (oakExecutable instanceof OakAttrNotNullException ? getNotNullMessage(oakExecutable.getEntity(), oakExecutable.getAttributes()[0]) : t(oakExecutable.message));
const [upsertId, setUpsertId] = useState('');
const U = (<Modal width={920} destroyOnClose title={`${t('wpAccount:name')}${t('common::action.add')}`} open={!!upsertId} onCancel={() => {
clean();
setUpsertId('');
}} closeIcon={null} onOk={async () => {
await execute();
setUpsertId('');
}} okButtonProps={{
disabled: oakExecutable !== true,
}} okText={t('common::confirm')} cancelText={(t('common::action.cancel'))}>
<div style={{ padding: 10 }}>
{errMsg && <Alert type="error" message={errMsg} style={{ marginBottom: 20 }}/>}
<Upsert oakPath={`${oakFullpath}.${upsertId}`} systemId={systemId}/>
</div>
</Modal>);
if (accounts && accounts?.length) {
return (<div className={Styles.container}>
<Alert type='info' message={t('tips')}/>
{U}
<div className={Styles.list}>
{accounts.filter(ele => ele.$$createAt$$ > 1).map((ele, idx) => <div className={Styles.item} key={idx}>
<WpAccount oakFullpath={oakFullpath} systemId={systemId} account={ele} t={t} onRemove={() => {
Modal.confirm({
title: t('confirmDelete'),
content: t('areYouSure'),
onOk: async () => execute(undefined, undefined, undefined, [
{
entity: 'wpAccount',
operation: {
id: await generateNewIdAsync(),
action: 'remove',
data: {},
filter: {
id: ele.id,
},
}
}
]),
});
}} onUpdate={() => setUpsertId(ele.id)}/>
</div>)}
</div>
<div className={Styles.btnBar}>
{canCreate && <Button type="primary" onClick={() => {
const id = addItem({ systemId });
setUpsertId(id);
}}>
{t('common::action.add')}
</Button>}
</div>
</div>);
}
return (<div className={Styles.container2}>
<Alert type='info' message={t('tips')}/>
{U}
<div className={Styles.body}>
{canCreate ? <PlusCircleOutlined className={Styles.add} shape="circle" style={{ fontSize: 50 }} onClick={() => {
const id = addItem({ systemId });
setUpsertId(id);
}}/> : t('noData')}
</div>
</div>);
}

View File

@ -0,0 +1,4 @@
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<import("../../../oak-app-domain").EntityDict, "wpAccount", false, {
systemId: string;
}>) => React.ReactElement;
export default _default;

View File

@ -0,0 +1,70 @@
export default OakComponent({
entity: 'wpAccount',
isList: false,
projection: {
id: 1,
price: 1,
mchId: 1,
refundGapDays: 1,
enabled: 1,
wechatPay: {
id: 1,
payNotifyUrl: 1,
refundNotifyUrl: 1,
},
taxlossRatio: 1,
publicKeyFilePath: 1,
privateKeyFilePath: 1,
refundLossFloor: 1,
refundLossRatio: 1,
apiV3Key: 1,
systemId: 1,
},
formData({ data, features }) {
const { systemId } = this.props;
const [wechatPay] = features.cache.get('wechatPay', {
data: {
id: 1,
refundNotifyUrl: 1,
payNotifyUrl: 1,
},
filter: {
systemId,
}
});
return {
wpAccount: data,
wechatPay,
};
},
properties: {
systemId: '',
},
lifetimes: {
async ready() {
if (this.isCreation()) {
this.update({
price: 0,
enabled: true,
taxlossRatio: 0.6,
});
const { systemId } = this.props;
const { data: [wechatPay] } = await this.features.cache.refresh('wechatPay', {
data: {
id: 1,
refundNotifyUrl: 1,
payNotifyUrl: 1,
},
filter: {
systemId,
}
});
if (wechatPay) {
this.update({
wechatPayId: wechatPay.id,
});
}
}
}
}
});

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1,14 @@
{
"placeholder": {
"privateKeyFilePath": "服务器上存放apiclient_key.pem的路径注意访问权限",
"publicKeyFilePath": "服务器上存放apiclient_cert.pem的路径注意访问权限",
"taxlossRatio": "微信支付收取的手续费一般为0.6(代表千分之六)",
"payNotifyUrl": "endpoint",
"refundNotifyUrl": "endpoint",
"apiV3Key": "需要登录商户后台获取",
"refundGapDays": "超过这个天数后无法退款",
"refundLossRatio": "退款时将按这个百分比扣除损耗0.6就代表千分之六)",
"refundLossFloor": "退款时按位向下取整(如按元取整,则忽略角位和分位)"
},
"wechatPayIsShared": "以上配置是全局的,请谨慎修改"
}

View File

@ -0,0 +1,8 @@
import React from 'react';
import { EntityDict } from "../../../oak-app-domain";
import { RowWithActions, WebComponentProps } from "oak-frontend-base";
export default function render(props: WebComponentProps<EntityDict, 'wpAccount', false, {
wpAccount?: (RowWithActions<EntityDict, 'wpAccount'> & {});
wechatPay?: EntityDict['wechatPay']['OpSchema'];
systemId: string;
}>): React.JSX.Element | null;

View File

@ -0,0 +1,78 @@
import React from 'react';
import { Form, Switch, InputNumber, Input, Radio, Divider } from 'antd';
import Styles from './web.pc.module.less';
import WechatPayUpsert from '../../wechatPay/upsert';
export default function render(props) {
const { wpAccount, wechatPay, oakFullpath, systemId } = props.data;
const { t, update } = props.methods;
if (wpAccount) {
return (<Form labelCol={{ span: 8 }} wrapperCol={{ span: 12 }} layout="horizontal" style={{ minWidth: 860 }}>
{(!wechatPay || wechatPay.$$createAt$$ === 1) && <WechatPayUpsert oakPath={`${oakFullpath}.wechatPay`} systemId={systemId} key="wpCreate"/>}
{wpAccount.wechatPayId && <WechatPayUpsert oakPath={`${oakFullpath}.wechatPay`} systemId={systemId} key="wpUpdate" oakId={wpAccount.wechatPayId}/>}
<Form.Item label="说明">
<div className={Styles.tips}>
{t('wechatPayIsShared')}
</div>
</Form.Item>
<Divider />
<Form.Item label={t('wpAccount:attr.mchId')}>
<Input maxLength={32} value={wpAccount.mchId} onChange={({ currentTarget }) => {
const mchId = currentTarget.value;
update({ mchId });
}}/>
</Form.Item>
<Form.Item label={t('wpAccount:attr.privateKeyFilePath')}>
<Input value={wpAccount.privateKeyFilePath} placeholder={t('placeholder.privateKeyFilePath')} onChange={({ currentTarget }) => {
const privateKeyFilePath = currentTarget.value;
update({ privateKeyFilePath });
}}/>
</Form.Item>
<Form.Item label={t('wpAccount:attr.publicKeyFilePath')}>
<Input value={wpAccount.publicKeyFilePath} placeholder={t('placeholder.publicKeyFilePath')} onChange={({ currentTarget }) => {
const publicKeyFilePath = currentTarget.value;
update({ publicKeyFilePath });
}}/>
</Form.Item>
<Form.Item label={t('wpAccount:attr.apiV3Key')}>
<Input value={wpAccount.apiV3Key} placeholder={t('placeholder.apiV3Key')} onChange={({ currentTarget }) => {
const apiV3Key = currentTarget.value;
update({ apiV3Key });
}}/>
</Form.Item>
<Form.Item label={t('wechatPay:attr.taxlossRatio')} help={t('placeholder.taxlossRatio')}>
<InputNumber value={wpAccount.taxlossRatio} max={5} min={0.01} addonAfter={"%"} step={0.01} precision={2} onChange={(value) => {
const taxlossRatio = value;
update({ taxlossRatio });
}}/>
</Form.Item>
<Form.Item label={t('wpAccount:attr.refundGapDays')} help={t('placeholder.refundGapDays')}>
<InputNumber value={wpAccount.refundGapDays} max={365} min={7} addonAfter={"天"} step={1} onChange={(value) => {
const refundGapDays = value;
update({ refundGapDays });
}}/>
</Form.Item>
<Form.Item label={t('wpAccount:attr.refundLossRatio')} help={t('placeholder.refundLossRatio')}>
<InputNumber value={wpAccount.refundLossRatio} max={5} min={0.01} addonAfter={"%"} step={0.01} precision={2} onChange={(value) => {
const refundLossRatio = value;
update({ refundLossRatio });
}}/>
</Form.Item>
<Form.Item label={t('wpAccount:attr.refundLossFloor')} help={t('placeholder.refundLossFloor')}>
<Radio.Group onChange={({ target }) => {
const { value } = target;
const refundLossFloor = value;
update({ refundLossFloor });
}} value={wpAccount.refundLossFloor}>
<Radio value={"jiao"}></Radio>
<Radio value={"yuan"}></Radio>
</Radio.Group>
</Form.Item>
<Form.Item label={t('wpAccount:attr.enabled')} required>
<Switch value={wpAccount.enabled} onChange={(enabled) => {
update({ enabled });
}}/>
</Form.Item>
</Form>);
}
return null;
}

View File

@ -0,0 +1,5 @@
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<import("../../../oak-app-domain").EntityDict, "wpProduct", true, {
systemId: string;
wpAccountId: string;
}>) => React.ReactElement;
export default _default;

View File

@ -0,0 +1,51 @@
export default OakComponent({
entity: 'wpProduct',
isList: true,
projection: {
id: 1,
type: 1,
wpAccountId: 1,
applicationId: 1,
application: {
id: 1,
type: 1,
name: 1,
},
},
filters: [
{
filter() {
const { wpAccountId } = this.props;
return {
wpAccountId,
};
}
}
],
properties: {
systemId: '',
wpAccountId: '',
},
formData({ data, legalActions }) {
return {
wpProducts: data,
canCreate: legalActions?.includes('create'),
};
},
actions: ['create', 'update', 'remove'],
lifetimes: {
ready() {
const { systemId } = this.props;
this.features.cache.refresh('application', {
data: {
id: 1,
name: 1,
type: 1,
},
filter: {
systemId,
},
});
}
}
});

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1,6 @@
{
"notnull": "属性\"%{value}\"不能为空",
"noData": "没有数据",
"confirmDelete": "确定删除",
"areYouSure": "确认删除本数据吗?"
}

View File

@ -0,0 +1,9 @@
import React from 'react';
import { EntityDict } from "../../../oak-app-domain";
import { RowWithActions, WebComponentProps } from "oak-frontend-base";
export default function render(props: WebComponentProps<EntityDict, 'wpProduct', true, {
wpProducts?: (RowWithActions<EntityDict, 'wpProduct'> & {})[];
systemId: string;
canCreate?: boolean;
wpAccountId: string;
}>): React.JSX.Element;

View File

@ -0,0 +1,79 @@
import React, { useState } from 'react';
import { Button, Modal, Alert } from 'antd';
import { PlusCircleOutlined } from '@ant-design/icons';
import Styles from './web.pc.module.less';
import Upsert from '../upsert';
import { OakAttrNotNullException, OakException } from 'oak-domain/lib/types';
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
function WpProduct(props) {
return null;
}
export default function render(props) {
const { wpAccountId, wpProducts, oakFullpath, oakExecutable, canCreate, systemId } = props.data;
const { t, addItem, execute, clean } = props.methods;
const getNotNullMessage = (entity, attr) => {
return t('notnull', { value: t(`${entity}:attr.${attr}`) });
};
const errMsg = oakExecutable instanceof OakException && (oakExecutable instanceof OakAttrNotNullException ? getNotNullMessage(oakExecutable.getEntity(), oakExecutable.getAttributes()[0]) : t(oakExecutable.message));
const [upsertId, setUpsertId] = useState('');
const U = (<Modal width={420} destroyOnClose title={`${t('wpProduct:name')}${t('common::action.update')}`} open={!!upsertId} onCancel={() => {
clean();
setUpsertId('');
}} closeIcon={null} onOk={async () => {
await execute();
setUpsertId('');
}} okButtonProps={{
disabled: oakExecutable !== true,
}} okText={t('common::confirm')} cancelText={(t('common::action.cancel'))}>
<div style={{ padding: 10 }}>
{errMsg && <Alert type="error" message={errMsg} style={{ marginBottom: 20 }}/>}
<Upsert oakPath={`${oakFullpath}.${upsertId}`} systemId={systemId} wpAccountId={wpAccountId}/>
</div>
</Modal>);
if (wpProducts && wpProducts.length) {
return (<div className={Styles.container}>
<Alert type='info' message={t('tips')}/>
{U}
<div className={Styles.list}>
{wpProducts.filter(ele => ele.$$createAt$$ > 1).map((ele, idx) => <div className={Styles.item} key={idx}>
<WpProduct wpProduct={ele} t={t} onRemove={() => {
Modal.confirm({
title: t('confirmDelete'),
content: t('areYouSure'),
onOk: async () => execute(undefined, undefined, undefined, [
{
entity: 'wpProduct',
operation: {
id: await generateNewIdAsync(),
action: 'remove',
data: {},
filter: {
id: ele.id,
},
}
}
]),
});
}} onUpdate={() => setUpsertId(ele.id)}/>
</div>)}
</div>
<div className={Styles.btnBar}>
{canCreate && <Button type="primary" onClick={() => {
const id = addItem({});
setUpsertId(id);
}}>
{t('common::action.add')}
</Button>}
</div>
</div>);
}
return (<div className={Styles.container2}>
{U}
<div className={Styles.body}>
{canCreate ? <PlusCircleOutlined className={Styles.add} shape="circle" style={{ fontSize: 50 }} onClick={() => {
const id = addItem({});
setUpsertId(id);
}}/> : t('noData')}
</div>
</div>);
}

View File

@ -0,0 +1,5 @@
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<import("../../../oak-app-domain").EntityDict, "wpProduct", false, {
systemId: string;
wpAccountId: string;
}>) => React.ReactElement;
export default _default;

View File

@ -0,0 +1,53 @@
import { getAppTypeFromProductType } from "../../../utils/wpProduct";
export default OakComponent({
entity: 'wpProduct',
isList: false,
projection: {
id: 1,
type: 1,
wpAccountId: 1,
applicationId: 1,
application: {
id: 1,
type: 1,
name: 1,
},
},
properties: {
systemId: '',
wpAccountId: '',
},
formData({ data, features }) {
const { systemId } = this.props;
const applications = data?.type && features.cache.get('application', {
data: {
id: 1,
name: 1,
type: 1,
},
filter: {
systemId,
wpProduct$application: {
"#sqp": 'not in',
},
type: {
$in: getAppTypeFromProductType(data.type),
}
}
});
return {
applications,
wpProduct: data,
};
},
lifetimes: {
ready() {
if (this.isCreation()) {
const { wpAccountId } = this.props;
this.update({
wpAccountId,
});
}
}
}
});

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1,8 @@
import React from 'react';
import { EntityDict } from "../../../oak-app-domain";
import { RowWithActions, WebComponentProps } from "oak-frontend-base";
export default function render(props: WebComponentProps<EntityDict, 'wpProduct', false, {
wpProduct?: (RowWithActions<EntityDict, 'wpProduct'> & {});
applications?: EntityDict['application']['OpSchema'][];
systemId: string;
}>): React.JSX.Element | null;

View File

@ -0,0 +1,23 @@
import React from 'react';
import { Form, Select } from 'antd';
export default function render(props) {
const { wpProduct, applications, oakFullpath, systemId } = props.data;
const { t, update } = props.methods;
if (wpProduct) {
return (<Form labelCol={{ span: 4 }} wrapperCol={{ span: 12 }} layout="horizontal" style={{ minWidth: 460 }}>
<Form.Item label={t('wpProduct:attr.type')}>
<Select value={wpProduct.type} options={['native', 'mp', 'jsapi', 'h5', 'app'].map(ele => ({
label: t(`wpProduct:v.type.${ele}`),
value: ele,
}))} onSelect={(value) => update({ type: value })}/>
</Form.Item>
{wpProduct.type && applications && (<Form.Item label={t('wpProduct:attr.application')}>
<Select value={wpProduct.applicationId} options={applications.map(ele => ({
label: ele.name,
value: ele.id,
}))} onSelect={(value) => update({ applicationId: value })}/>
</Form.Item>)}
</Form>);
}
return null;
}

View File

@ -1,4 +1,4 @@
import { AttrUpdateMatrix } from 'oak-domain/lib/types/EntityDesc';
import { EntityDict } from '@project/oak-app-domain';
import { EntityDict } from '../oak-app-domain';
declare const attrUpdateMatrix: AttrUpdateMatrix<EntityDict>;
export default attrUpdateMatrix;

View File

@ -61,6 +61,70 @@ const attrUpdateMatrix = {
meta: {
actions: ['succeed', 'fail', 'succeedPartially'],
},
}
},
offlineAccount: {
name: {
actions: ['update'],
filter: {
// 只有从来没用过的才能改
sysAccountOper$entity: {
"#sqp": 'not in',
}
}
},
channel: {
actions: ['update'],
filter: {
// 只有从来没用过的才能改
sysAccountOper$entity: {
"#sqp": 'not in',
}
}
},
qrCode: {
actions: ['update'],
},
enabled: {
actions: ['update'],
},
price: {
actions: ['pay', 'refund', 'deposit', 'withdraw', 'tax'],
}
},
wpAccount: {
wechatPay: {
actions: ['update'],
},
taxlossRatio: {
actions: ['update'],
},
depositLossRatio: {
actions: ['update'],
},
refundGapDays: {
actions: ['update'],
},
refundLossRatio: {
actions: ['update'],
},
refundLossFloor: {
actions: ['update'],
},
publicKeyFilePath: {
actions: ['update'],
},
privateKeyFilePath: {
actions: ['update'],
},
apiV3Key: {
actions: ['update'],
},
enabled: {
actions: ['update'],
},
price: {
actions: ['pay', 'refund', 'deposit', 'withdraw', 'tax'],
}
},
};
export default attrUpdateMatrix;

View File

@ -1,3 +1,3 @@
import { EntityDict } from '@project/oak-app-domain';
import { EntityDict } from '../oak-app-domain';
declare const cacheSavedEntities: (keyof EntityDict)[];
export default cacheSavedEntities;

View File

@ -1,4 +1,4 @@
import { CommonConfiguration } from 'oak-domain/lib/types/Configuration';
import { EntityDict } from '@project/oak-app-domain';
import { EntityDict } from '../oak-app-domain';
declare const _default: CommonConfiguration<EntityDict>;
export default _default;

View File

@ -2,7 +2,7 @@
* index中汇总了前端启动需要的引用配置
*/
import attrUpdateMatrix from './attrUpdateMatrix';
import { actionDefDict } from '@project/oak-app-domain/ActionDefDict';
import { actionDefDict } from '../oak-app-domain/ActionDefDict';
import { selectFreeEntities, authDeduceRelationMap, updateFreeDict } from './relation';
import cacheSavedEntities from './cache';
export default {

View File

@ -1,5 +1,5 @@
import { AuthDeduceRelationMap, UpdateFreeDict } from 'oak-domain/lib/types/Entity';
import { EntityDict } from '@project/oak-app-domain';
import { EntityDict } from '../oak-app-domain';
export declare const authDeduceRelationMap: AuthDeduceRelationMap<EntityDict>;
export declare const selectFreeEntities: string[];
export declare const updateFreeDict: UpdateFreeDict<EntityDict>;

View File

@ -1,7 +1,8 @@
// 此对象所标识的entity的权限由其外键指向的父对象判定
export const authDeduceRelationMap = {};
export const selectFreeEntities = [
'payChannel',
'offlineAccount',
'wpProduct',
];
export const updateFreeDict = {};
export default {

View File

@ -1,4 +1,4 @@
import { RenderConfiguration } from 'oak-domain/lib/types/Configuration';
import { EntityDict } from '@project/oak-app-domain';
import { EntityDict } from '../oak-app-domain';
declare const _default: RenderConfiguration<EntityDict>;
export default _default;

View File

@ -1,4 +1,4 @@
import { EntityDict } from '@project/oak-app-domain';
import { EntityDict } from '../oak-app-domain';
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity';
import { RuntimeContext } from './RuntimeContext';
import { BackendRuntimeContext as DependentBackendRuntimeContext } from './DependentContext';

View File

@ -1,5 +1,5 @@
import { BackendRuntimeContext as DependentBackendRuntimeContext } from './DependentContext';
import { mergedProjection } from '@project/utils/application';
import { mergedProjection } from '../utils/application';
export class BackendRuntimeContext extends DependentBackendRuntimeContext {
applicationProjection = mergedProjection;
async toString() {
@ -21,6 +21,4 @@ export class BackendRuntimeContext extends DependentBackendRuntimeContext {
}
}
;
console.log('aaaa');
export default BackendRuntimeContext;

View File

@ -1,4 +1,4 @@
import { EntityDict } from '@project/oak-app-domain';
import { EntityDict } from '../oak-app-domain';
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity';
import { FrontendRuntimeContext as DependentFrontendRuntimeContext, SerializedData } from './DependentContext';
import { RuntimeContext } from './RuntimeContext';

View File

@ -1,3 +1,3 @@
import { CreateOperationData as ActionAuth } from '@project/oak-app-domain/ActionAuth/Schema';
import { CreateOperationData as ActionAuth } from '../oak-app-domain/ActionAuth/Schema';
declare const actionAuths: ActionAuth[];
export default actionAuths;

View File

@ -53,6 +53,20 @@ const i18ns = [
position: "src/components/accountOper/list",
data: {}
},
{
id: "50388a328d027544c140578a7a614845",
namespace: "oak-pay-business-c-offlineAccount-config",
language: "zh-CN",
module: "oak-pay-business",
position: "src/components/offlineAccount/config",
data: {
"tips": "线下账户是需要system的相关工作人员手动同步收款和打款结果的账户",
"notnull": "属性\"%{value}\"不能为空",
"noData": "没有数据",
"confirmDelete": "确定删除",
"areYouSure": "确认删除本数据吗?"
}
},
{
id: "17cdc7adc4c7b439ae2298a7f9920855",
namespace: "oak-pay-business-c-offlineAccount-upsert",
@ -60,16 +74,29 @@ const i18ns = [
module: "oak-pay-business",
position: "src/components/offlineAccount/upsert",
data: {
"tips": "线下交易账户是需要人工同步收款和打款行为的账户",
"label": {
"channel": {
"bank": "银行及所属支行"
}
},
"placeholder": {
"channel": {
"bank": "银行-所属支行"
"bank": "银行/支行",
"others": "请输入途径名称"
},
"name": {
"bank": "输入户主姓名",
"alipay": "输入支付宝帐号",
"wechat": "输入微信号",
"shouqianba": "输入收钱吧商户号",
"others": "输入该收款途径的唯一账号"
},
"qrCode": {
"bank": "请输入银行账号",
"alipay": "请将二维码解析后的字符串填入",
"wechat": "请将二维码解析后的字符串填入",
"shouqianba": "请将二维码解析后的字符串填入",
"others": "请将二维码解析后的字符串填入"
}
},
"help": {
"allowDeposit": "是否允许用户在系统中主动向此账号发起充值",
"allowPay": "是否允许用户在系统中主动向此账号发起支付"
}
}
},
@ -264,6 +291,20 @@ const i18ns = [
}
}
},
{
id: "79255be8c093dfef9765b3f367cab553",
namespace: "oak-pay-business-c-wechatPay-upsert",
language: "zh-CN",
module: "oak-pay-business",
position: "src/components/wechatPay/upsert",
data: {
"placeholder": {
"lossRatio": "填百分比(0.6就代表千分之六)",
"payNotifyUrl": "endpoint",
"refundNotifyUrl": "endpoint"
}
}
},
{
id: "6a1df36d072d367121b49dfc665b4100",
namespace: "oak-pay-business-c-withdraw-create",
@ -399,6 +440,54 @@ const i18ns = [
}
}
},
{
id: "7273f5a9fdb45407416554325a1abb1e",
namespace: "oak-pay-business-c-wpAccount-config",
language: "zh-CN",
module: "oak-pay-business",
position: "src/components/wpAccount/config",
data: {
"tips": "微信支付账户是用来配置微信支付的账户相关信息",
"notnull": "属性\"%{value}\"不能为空",
"noData": "没有数据",
"confirmDelete": "确定删除",
"areYouSure": "确认删除本数据吗?"
}
},
{
id: "33691d391e052cccf005d0f0da84e20e",
namespace: "oak-pay-business-c-wpAccount-upsert",
language: "zh-CN",
module: "oak-pay-business",
position: "src/components/wpAccount/upsert",
data: {
"placeholder": {
"privateKeyFilePath": "服务器上存放apiclient_key.pem的路径注意访问权限",
"publicKeyFilePath": "服务器上存放apiclient_cert.pem的路径注意访问权限",
"taxlossRatio": "微信支付收取的手续费一般为0.6(代表千分之六)",
"payNotifyUrl": "endpoint",
"refundNotifyUrl": "endpoint",
"apiV3Key": "需要登录商户后台获取",
"refundGapDays": "超过这个天数后无法退款",
"refundLossRatio": "退款时将按这个百分比扣除损耗0.6就代表千分之六)",
"refundLossFloor": "退款时按位向下取整(如按元取整,则忽略角位和分位)"
},
"wechatPayIsShared": "以上配置是全局的,请谨慎修改"
}
},
{
id: "634284beff0e0bdf84fb23cdeea9107d",
namespace: "oak-pay-business-c-wpProduct-config",
language: "zh-CN",
module: "oak-pay-business",
position: "src/components/wpProduct/config",
data: {
"notnull": "属性\"%{value}\"不能为空",
"noData": "没有数据",
"confirmDelete": "确定删除",
"areYouSure": "确认删除本数据吗?"
}
},
{
id: "2127948cb52e116e3ceccd93db8f319c",
namespace: "oak-pay-business-l-common",
@ -470,6 +559,38 @@ const i18ns = [
}
}
},
{
id: "8ff2d28d93f47ceaa08aab6211449db8",
namespace: "oak-pay-business-l-offlineAccount",
language: "zh-CN",
module: "oak-pay-business",
position: "locales/offlineAccount",
data: {
"label": {
"channel": {
"bank": "银行及所属支行",
"others": "途径名"
},
"name": {
"bank": "户主姓名",
"alipay": "支付宝账号",
"wechat": "微信号",
"shouqianba": "收钱吧商户号",
"others": "相应帐号"
},
"qrCode": {
"bank": "银行账号",
"alipay": "收款二维码",
"wechat": "收款二维码",
"shouqianba": "收款二维码",
"others": "收款二维码"
}
},
"error": {
"nameQrCodeBothNull": "账号名和二维码不能同时为空"
}
}
},
{
id: "e8c0a965783373d9117f9447ebbbad8d",
namespace: "oak-pay-business-l-payChannel",

2
es/data/path.d.ts vendored
View File

@ -1,3 +1,3 @@
import { CreateOperationData as Path } from '@project/oak-app-domain/Path/Schema';
import { CreateOperationData as Path } from '../oak-app-domain/Path/Schema';
declare const paths: Path[];
export default paths;

View File

@ -1,8 +1,9 @@
import { String, Text, Boolean } from 'oak-domain/lib/types/DataType';
import { String, Text, Price, Boolean } from 'oak-domain/lib/types/DataType';
import { EntityShape } from 'oak-domain/lib/types/Entity';
import { EntityDesc } from 'oak-domain/lib/types';
import { Schema as System } from 'oak-general-business/lib/entities/System';
import { Schema as Pay } from './Pay';
import { Schema as SysAccountOper } from './SysAccountOper';
export interface Schema extends EntityShape {
type: 'bank' | 'alipay' | 'wechat' | 'shouqianba' | 'others';
channel?: String<32>;
@ -11,8 +12,12 @@ export interface Schema extends EntityShape {
allowDeposit: Boolean;
allowPay: Boolean;
system: System;
price: Price;
pays: Pay[];
opers: SysAccountOper[];
enabled: Boolean;
}
export declare const entityDesc: EntityDesc<Schema, '', '', {
export type Action = 'pay' | 'refund' | 'deposit' | 'withdraw' | 'tax';
export declare const entityDesc: EntityDesc<Schema, Action, '', {
type: Schema['type'];
}>;

View File

@ -2,7 +2,7 @@
export const entityDesc = {
locales: {
zh_CN: {
name: '离线账号管理',
name: '线下账户',
attr: {
type: '类型',
channel: '通道',
@ -11,7 +11,10 @@ export const entityDesc = {
allowDeposit: '允许主动充值',
allowPay: '允许主动支付',
system: '所属系统',
price: '余额',
pays: '支付',
opers: '操作记录',
enabled: '是否启用',
},
v: {
type: {
@ -22,17 +25,31 @@ export const entityDesc = {
others: '其它',
},
},
action: {
pay: '支付',
refund: '退款',
deposit: '充值',
withdraw: '提现',
tax: '渠道费',
}
},
},
style: {
color: {
type: {
bank: '#E74C3C',
alipay: '#3498DB',
wechat: '#27AE60',
shouqianba: '#F1C40F',
alipay: '#1678ff',
wechat: '#04BE02',
shouqianba: '#ffc106',
others: '#34495E',
},
},
icon: {
pay: '',
refund: '',
deposit: '',
withdraw: '',
tax: '',
}
},
};

12
es/entities/SysAccountOper.d.ts vendored Normal file
View File

@ -0,0 +1,12 @@
import { String, Price } from 'oak-domain/lib/types/DataType';
import { EntityShape } from 'oak-domain/lib/types/Entity';
import { EntityDesc } from 'oak-domain/lib/types';
export interface Schema extends EntityShape {
delta: Price;
type: 'pay' | 'refund' | 'deposit' | 'withdraw' | 'tax';
entity: String<32>;
entityId: String<64>;
}
export declare const entityDesc: EntityDesc<Schema, '', '', {
type: Schema['type'];
}>;

View File

@ -0,0 +1,37 @@
;
export const entityDesc = {
locales: {
zh_CN: {
name: '系统账户操作',
attr: {
type: '类型',
delta: '余额变化',
entity: '关联对象',
entityId: '关联对象Id',
},
v: {
type: {
deposit: '充值',
withdraw: '提现',
pay: '支付',
refund: '退款',
tax: '渠道手续费'
},
},
},
},
style: {
color: {
type: {
deposit: '#3498DB',
withdraw: '#F7DC6F',
pay: '#82E0AA',
tax: '#2E4053',
refund: '#2E4053',
}
}
},
configuration: {
actionType: 'appendOnly',
}
};

View File

@ -1,9 +1,10 @@
import { String, Decimal } from 'oak-domain/lib/types/DataType';
import { String } from 'oak-domain/lib/types/DataType';
import { EntityShape } from 'oak-domain/lib/types/Entity';
import { EntityDesc } from 'oak-domain/lib/types';
import { Schema as System } from 'oak-general-business/lib/entities/System';
export interface Schema extends EntityShape {
payNotifyUrl: String<128>;
refundNotifyUrl: String<128>;
lossRatio: Decimal<4, 2>;
system: System;
}
export declare const entityDesc: EntityDesc<Schema>;

View File

@ -6,7 +6,7 @@ export const entityDesc = {
attr: {
payNotifyUrl: '支付通知回调',
refundNotifyUrl: '退款通知回调',
lossRatio: '支付损耗比例'
system: '关联系统'
},
},
},

View File

@ -1,10 +1,12 @@
import { String, Text, Price, Int, Decimal } from 'oak-domain/lib/types/DataType';
import { String, Text, Price, Boolean, Int, Decimal } from 'oak-domain/lib/types/DataType';
import { EntityShape } from 'oak-domain/lib/types/Entity';
import { EntityDesc } from 'oak-domain/lib/types';
import { Schema as WechatPay } from './WechatPay';
import { Schema as System } from 'oak-general-business/lib/entities/System';
import { Schema as SysAccountOper } from './SysAccountOper';
export interface Schema extends EntityShape {
wechatPay: WechatPay;
taxlossRatio: Decimal<4, 2>;
depositLossRatio?: Decimal<4, 2>;
refundGapDays?: Int<4>;
refundLossRatio?: Decimal<4, 2>;
@ -15,7 +17,10 @@ export interface Schema extends EntityShape {
apiV3Key: String<32>;
price: Price;
system: System;
opers: SysAccountOper[];
enabled: Boolean;
}
export declare const entityDesc: EntityDesc<Schema, '', '', {
export type Action = 'pay' | 'refund' | 'deposit' | 'withdraw' | 'tax';
export declare const entityDesc: EntityDesc<Schema, Action, '', {
refundLossFloor: NonNullable<Schema['refundLossFloor']>;
}>;

View File

@ -5,6 +5,7 @@ export const entityDesc = {
name: '微信支付帐号',
attr: {
wechatPay: '微信支付',
taxlossRatio: '商户号手续费(百分比)',
depositLossRatio: '充值损耗百分比',
refundGapDays: '(支付后)允许退款的天数',
refundLossRatio: '退款损耗百分比',
@ -14,13 +15,22 @@ export const entityDesc = {
privateKeyFilePath: '私钥文件路径',
apiV3Key: 'apiV3Key',
price: '余额',
system: '关联系统'
system: '关联系统',
opers: '操作记录',
enabled: '是否启用',
},
v: {
refundLossFloor: {
yuan: '元',
jiao: '角'
},
},
action: {
pay: '支付',
refund: '退款',
deposit: '充值',
withdraw: '提现',
tax: '渠道费',
}
},
},
@ -30,6 +40,13 @@ export const entityDesc = {
yuan: '#FFFF00',
jiao: '#00FF00'
}
},
icon: {
pay: '',
refund: '',
deposit: '',
withdraw: '',
tax: '',
}
}
};

View File

@ -1,3 +1,4 @@
import { Boolean } from 'oak-domain/lib/types/DataType';
import { EntityShape } from 'oak-domain/lib/types/Entity';
import { EntityDesc } from 'oak-domain/lib/types';
import { Schema as WpAccount } from './WpAccount';
@ -7,6 +8,7 @@ export interface Schema extends EntityShape {
wpAccount: WpAccount;
type: 'native' | 'mp' | 'jsapi' | 'h5' | 'app';
application: Application;
enabled: Boolean;
pays: Pay[];
}
export declare const entityDesc: EntityDesc<Schema, '', '', {

View File

@ -7,6 +7,7 @@ export const entityDesc = {
wpAccount: '微信支付帐号',
type: '类型',
application: '关联应用',
enabled: '有效中',
pays: '支付',
},
v: {

View File

@ -1,6 +1,6 @@
import { Feature } from "oak-frontend-base";
import { FeatureDict as GeneralFeatures } from 'oak-general-business';
import { EntityDict } from '@project/oak-app-domain';
import { EntityDict } from '../oak-app-domain';
export default class Pay extends Feature {
private application;
constructor(application: GeneralFeatures<EntityDict>['application']);

View File

@ -1,5 +1,5 @@
import { Feature } from "oak-frontend-base";
// import { getDepositRatio } from "@project/utils/pay";
// import { getDepositRatio } from "../utils/pay";
export default class Pay extends Feature {
application;
constructor(application) {

View File

@ -1,4 +1,4 @@
import { EntityDict } from '@project/oak-app-domain';
import { EntityDict } from '../oak-app-domain';
import { AccessConfiguration } from 'oak-domain/lib/types/Configuration';
import { BasicFeatures } from 'oak-frontend-base';
import { FeatureDict as Ogb0FeatureDict } from "oak-general-business";

View File

@ -1,7 +1,7 @@
import { merge } from 'oak-domain/lib/utils/lodash';
import { initialize as initializeGeneral } from 'oak-general-business/es/features';
import Pay from './Pay';
import { applicationProjection } from '@project/utils/application';
import { applicationProjection } from '../utils/application';
export function create(features) {
const pay = new Pay(features.application);
return {

View File

@ -1,2 +1,2 @@
import { AFD } from '@project/types/RuntimeCxt';
import { AFD } from '../types/RuntimeCxt';
export default function useFeatures(): AFD;

View File

@ -0,0 +1,25 @@
{
"label": {
"channel": {
"bank": "银行及所属支行",
"others": "途径名"
},
"name": {
"bank": "户主姓名",
"alipay": "支付宝账号",
"wechat": "微信号",
"shouqianba": "收钱吧商户号",
"others": "相应帐号"
},
"qrCode": {
"bank": "银行账号",
"alipay": "收款二维码",
"wechat": "收款二维码",
"shouqianba": "收款二维码",
"others": "收款二维码"
}
},
"error": {
"nameQrCodeBothNull": "账号名和二维码不能同时为空"
}
}

View File

@ -42,6 +42,7 @@ export declare const actionDefDict: {
deposit: {
iState: import("oak-domain/lib/types").ActionDef<string, string>;
};
offlineAccount: {};
order: {
iState: import("oak-domain/lib/types").ActionDef<string, string>;
};
@ -58,4 +59,5 @@ export declare const actionDefDict: {
withdraw: {
iState: import("oak-domain/lib/types").ActionDef<string, string>;
};
wpAccount: {};
};

Some files were not shown because too many files have changed in this diff Show More