增加了sysAccount/survey的部分能力
This commit is contained in:
parent
3a0e6d3035
commit
4340d8bbe1
|
|
@ -143,6 +143,9 @@ export async function getWithdrawCreateData(params, context) {
|
|||
data: ele,
|
||||
})));
|
||||
}
|
||||
else {
|
||||
data.refund$entity = [];
|
||||
}
|
||||
if (totalPrice > price2) {
|
||||
// 如果还有要退的部分,就从withdrawAccount来进行转账
|
||||
const rest = totalPrice - price2;
|
||||
|
|
@ -196,6 +199,10 @@ export async function getWithdrawCreateData(params, context) {
|
|||
}
|
||||
];
|
||||
}
|
||||
else {
|
||||
// 保持结构完整,让上层可以和withdraw$detail同构渲染
|
||||
data.withdrawTransfer$withdraw = [];
|
||||
}
|
||||
data.loss = totalLoss;
|
||||
return data;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { EntityDict } from '../oak-app-domain';
|
||||
import { RuntimeCxt } from '../types/RuntimeCxt';
|
||||
import { Checker } from 'oak-domain/lib/types/Auth';
|
||||
export declare function registerAccountEntity<ED extends EntityDict>(entity: keyof ED): void;
|
||||
export declare const accountEntities: string[];
|
||||
declare const triggers: Checker<EntityDict, keyof EntityDict, RuntimeCxt>[];
|
||||
export default triggers;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import { generateNewId } from 'oak-domain/lib/utils/uuid';
|
||||
import { getPayClazzAccountEntities } from '../utils/payClazz';
|
||||
// 当注入一个新的pay entity时,将account的删除与之相关联
|
||||
const entities = getPayClazzAccountEntities();
|
||||
// 当注入一个新的account entity时,将withdrawChannel的删除与之相关联
|
||||
export function registerAccountEntity(entity) {
|
||||
accountEntities.push(entity);
|
||||
}
|
||||
export const accountEntities = ['wpAccount', 'offlineAccount'];
|
||||
const triggers = [
|
||||
...entities.filter(ele => !!ele).map((entity) => [
|
||||
...accountEntities.filter(ele => !!ele).map((entity) => [
|
||||
{
|
||||
entity,
|
||||
action: 'remove',
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ import applicationCheckers from './application';
|
|||
import offlineAccountCheckers from './offlineAccount';
|
||||
import wpProductCheckers from './wpProduct';
|
||||
import abstractCheckers from './abstractChecker';
|
||||
import withdrawAccounts from './withdrawAccount';
|
||||
const checkers = [
|
||||
...withdrawAccounts,
|
||||
...abstractCheckers,
|
||||
...aoCheckers,
|
||||
...payCheckers,
|
||||
|
|
|
|||
|
|
@ -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, 'withdrawAccount', RuntimeCxt>[];
|
||||
export default checkers;
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
import { generateNewId } from 'oak-domain/lib/utils/uuid';
|
||||
import { pipeline } from 'oak-domain/lib/utils/executor';
|
||||
import assert from 'assert';
|
||||
const checkers = [
|
||||
{
|
||||
entity: 'withdrawAccount',
|
||||
type: 'logical',
|
||||
action: 'create',
|
||||
checker(operation, context, option) {
|
||||
const { data } = operation;
|
||||
if (data) {
|
||||
const { id, entity, entityId, isDefault } = data;
|
||||
if (entity && entityId && isDefault) {
|
||||
return context.operate('withdrawAccount', {
|
||||
id: generateNewId(),
|
||||
action: 'update',
|
||||
data: {
|
||||
isDefault: false,
|
||||
},
|
||||
filter: {
|
||||
entity,
|
||||
entityId,
|
||||
isDefault: true,
|
||||
id: {
|
||||
$ne: id,
|
||||
},
|
||||
}
|
||||
}, option);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
entity: 'withdrawAccount',
|
||||
type: 'logical',
|
||||
action: 'update',
|
||||
checker(operation, context, option) {
|
||||
const { data, filter } = operation;
|
||||
if (data?.isDefault) {
|
||||
return pipeline(() => context.select('withdrawAccount', {
|
||||
data: {
|
||||
id: 1,
|
||||
entity: 1,
|
||||
entityId: 1,
|
||||
},
|
||||
filter,
|
||||
}, {}), (accounts) => {
|
||||
assert(accounts.length === 1);
|
||||
const [account] = accounts;
|
||||
const { entity, entityId, id } = account;
|
||||
return context.operate('withdrawAccount', {
|
||||
id: generateNewId(),
|
||||
action: 'update',
|
||||
data: {
|
||||
isDefault: false,
|
||||
},
|
||||
filter: {
|
||||
entity,
|
||||
entityId,
|
||||
isDefault: true,
|
||||
id: {
|
||||
$ne: id,
|
||||
},
|
||||
}
|
||||
}, option);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
export default checkers;
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
import { EntityDict } from "../../../oak-app-domain";
|
||||
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<EntityDict, keyof EntityDict, boolean, {
|
||||
entities: string[];
|
||||
systemId: string;
|
||||
}>) => React.ReactElement;
|
||||
export default _default;
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
import assert from 'assert';
|
||||
import { uniq } from "oak-domain/lib/utils/lodash";
|
||||
export default OakComponent({
|
||||
properties: {
|
||||
entities: [],
|
||||
systemId: '',
|
||||
},
|
||||
lifetimes: {
|
||||
ready() {
|
||||
this.refreshData();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
refreshData() {
|
||||
const { entities, systemId } = this.props;
|
||||
const schema = this.features.cache.getSchema();
|
||||
uniq(['wpAccount', 'offlineAccount'].concat(entities || [])).forEach((entity) => {
|
||||
const projection = {
|
||||
id: 1,
|
||||
$$createAt$$: 1,
|
||||
$$updateAt$$: 1,
|
||||
};
|
||||
Object.keys(schema[entity].attributes).forEach(ele => {
|
||||
if (!ele.startsWith('$$')) {
|
||||
projection[ele] = 1;
|
||||
}
|
||||
});
|
||||
this.features.cache.refresh(entity, {
|
||||
data: projection,
|
||||
filter: {
|
||||
systemId,
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
formData({ features }) {
|
||||
const { entities, systemId } = this.props;
|
||||
assert(systemId);
|
||||
const schema = features.cache.getSchema();
|
||||
let total = 0;
|
||||
const accounts = uniq(['wpAccount', 'offlineAccount'].concat(entities || [])).map((entity) => {
|
||||
const projection = {
|
||||
id: 1,
|
||||
$$createAt$$: 1,
|
||||
$$updateAt$$: 1,
|
||||
};
|
||||
Object.keys(schema[entity].attributes).forEach(ele => {
|
||||
if (!ele.startsWith('$$')) {
|
||||
projection[ele] = 1;
|
||||
}
|
||||
});
|
||||
const [data] = this.features.cache.get(entity, {
|
||||
data: projection,
|
||||
filter: {
|
||||
systemId,
|
||||
}
|
||||
}, true);
|
||||
const { price, id } = data;
|
||||
total += price;
|
||||
return {
|
||||
entity,
|
||||
id,
|
||||
price,
|
||||
data,
|
||||
};
|
||||
});
|
||||
return {
|
||||
total,
|
||||
accounts,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1 @@
|
|||
{}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import { EntityDict } from "../../../oak-app-domain";
|
||||
import { WebComponentProps } from "oak-frontend-base";
|
||||
import React from 'react';
|
||||
export default function render(props: WebComponentProps<EntityDict, 'offlineAccount', false, {
|
||||
total?: number;
|
||||
accounts?: Array<{
|
||||
id: string;
|
||||
entity: string;
|
||||
data: any;
|
||||
price: number;
|
||||
}>;
|
||||
}>): React.JSX.Element | null;
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import React from 'react';
|
||||
import Styles from './web.pc.module.less';
|
||||
export default function render(props) {
|
||||
const { accounts, total } = props.data;
|
||||
if (accounts) {
|
||||
return (<div className={Styles.container}>
|
||||
{total}
|
||||
</div>);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
.container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
|
@ -34,8 +34,8 @@ export default OakComponent({
|
|||
return {
|
||||
withdrawCreate: withdrawData && {
|
||||
...withdrawData,
|
||||
refund$entity: withdrawData.refund$entity.map((ele) => ele.data),
|
||||
withdrawTransfer$withdraw: withdrawData.withdrawTransfer$withdraw.map((ele) => ele.data),
|
||||
refund$entity: withdrawData.refund$entity?.map((ele) => ele.data),
|
||||
withdrawTransfer$withdraw: withdrawData.withdrawTransfer$withdraw?.map((ele) => ele.data),
|
||||
},
|
||||
withdrawable,
|
||||
withdrawLossText,
|
||||
|
|
@ -81,6 +81,9 @@ export default OakComponent({
|
|||
chooseWa: false,
|
||||
withdrawAccountId: '',
|
||||
waFilter: undefined,
|
||||
pickWithdrawChannelMp(id) {
|
||||
this.pickWithdrawChannel(id);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setValue(valueYuan) {
|
||||
|
|
@ -108,6 +111,7 @@ export default OakComponent({
|
|||
},
|
||||
async createWithdrawData() {
|
||||
const { value, refundAmount, avail, withdrawAccountId } = this.state;
|
||||
if (value) {
|
||||
if (value > avail) {
|
||||
this.setMessage({
|
||||
type: 'error',
|
||||
|
|
@ -122,6 +126,7 @@ export default OakComponent({
|
|||
chooseWa: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
async resetCreateData() {
|
||||
const { value, withdrawAccountId } = this.state;
|
||||
|
|
@ -208,6 +213,10 @@ export default OakComponent({
|
|||
},
|
||||
goBack() {
|
||||
this.navigateBack();
|
||||
},
|
||||
onGoToHistoryMp() {
|
||||
const { onGoToHistory } = this.props;
|
||||
onGoToHistory && onGoToHistory();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,17 @@
|
|||
@import '../../../config/styles/mp/mixins.less';
|
||||
@import '../../../config/styles/mp/index.less';
|
||||
|
||||
|
||||
.errorContainer {
|
||||
padding: 48rpx;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 40rpx;
|
||||
}
|
||||
|
||||
.container {
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
|
|
@ -93,6 +104,16 @@
|
|||
}
|
||||
}
|
||||
|
||||
.gotoHistory {
|
||||
font-size: 32rpx;
|
||||
color: @oak-color-primary;
|
||||
margin-top: 56rpx;
|
||||
text-decoration: underline;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,21 @@
|
|||
<block wx:if="{{withdrawCreate}}">
|
||||
<block wx:if="{{!withdrawable}}">
|
||||
<view class="errorContainer">
|
||||
<oak-icon name="delete_fill" size="48" color="#FF3141" />
|
||||
<view>{{t('notSetYet')}}</view>
|
||||
<l-button bind:lintap="goBack">{{t('common::back')}}</l-button>
|
||||
</view>
|
||||
</block>
|
||||
<block wx:elif="{{chooseWa && waFilter}}">
|
||||
<withdrawAccount-picker
|
||||
oakPath="$$opb-withdraw-create-wapicker"
|
||||
oakFilters="{{[waFilter]}}"
|
||||
onPicker="{{pickWithdrawChannelMp}}"
|
||||
onCancel="{{restartAll}}"
|
||||
entity="user"
|
||||
entityId="{{userId}}"
|
||||
/>
|
||||
</block>
|
||||
<block wx:elif="{{withdrawCreate}}">
|
||||
<view class="container">
|
||||
<withdraw-display
|
||||
withdraw="{{withdrawCreate}}"
|
||||
|
|
@ -10,7 +27,7 @@
|
|||
size="long"
|
||||
plain="true"
|
||||
shape="square"
|
||||
bind:lintap="clearWithdrawData"
|
||||
bind:lintap="restartAll"
|
||||
l-class="cancelBtn"
|
||||
>
|
||||
{{t('common::action.cancel')}}
|
||||
|
|
@ -25,12 +42,13 @@
|
|||
</view>
|
||||
</view>
|
||||
</block>
|
||||
<block wx:elif="{{account && avail > 0}}">
|
||||
<block wx:elif="{{account}}">
|
||||
<view class="main">
|
||||
<view class="label">{{t('label')}}</view>
|
||||
<view class="input">
|
||||
<span class="symbol">{{t('common::pay.symbol')}}</span>
|
||||
<l-input
|
||||
focus="{{true}}"
|
||||
hide-label="{{true}}"
|
||||
showRow="{{false}}"
|
||||
type="number"
|
||||
|
|
@ -38,16 +56,21 @@
|
|||
value="{{valueYuan ? valueYuan : undefind}}"
|
||||
l-input-class="my-input-class"
|
||||
bind:lininput="InputValueMp"
|
||||
bind:linconfirm="createWithdrawData"
|
||||
/>
|
||||
</view>
|
||||
<view class="tips">
|
||||
<view class="tipLine">
|
||||
<span>{{t('tips.1-1')}}</span>
|
||||
<span class="value">{{availYuan}}</span>
|
||||
<span>{{t('tips.1-2')}}</span>
|
||||
<block wx:if="{{avail>0}}">
|
||||
<span class="value" style="text-decoration: underline; margin-left: 16rpx;" bindtap="setValueMp" data-value="{{availYuan}}">{{t('tips.fill')}}</span>
|
||||
<span class="value" style="text-decoration: underline; margin-left: 16rpx;" bindtap="setValueMp" data-value="{{availYuan}}">
|
||||
<span class="value">{{availYuan}}</span>
|
||||
</span>
|
||||
</block>
|
||||
<block wx:else>
|
||||
<span class="value">{{availYuan}}</span>
|
||||
</block>
|
||||
<span>{{t('tips.1-2')}}</span>
|
||||
</view>
|
||||
<view wx:if="{{refundAmount > 0}}" class="tipLine">
|
||||
<span>{{t('tips.2-1')}}</span>
|
||||
|
|
@ -72,7 +95,11 @@
|
|||
<oak-icon wx:else name="unfold" size="18" color="skyblue"/>
|
||||
<span style="margin-left: 16rpx;color:{{showLossHelp ? 'blue' : 'skyblue'}};">{{t('helps.label.loss')}}</span>
|
||||
</view>
|
||||
<view wx:if="{{showLossHelp}}" class="content2">{{t('helps.content.loss')}}</view>
|
||||
<view wx:if="{{showLossHelp}}" class="content2">{{withdrawLossText}}</view>
|
||||
</view>
|
||||
<view class="gotoHistory" bindtap="onGoToHistoryMp">
|
||||
<oak-icon name="coupons" size="18" color="@oak-color-primary" style="margin-right:8rpx;"/>
|
||||
<span>{{t('gotoHistory')}}</span>
|
||||
</view>
|
||||
<view class="footer">
|
||||
<l-button
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export default function render(props) {
|
|||
</div>
|
||||
<div className={Styles.input}>
|
||||
<span className={Styles.symbol}>{t('common::pay.symbol')}</span>
|
||||
<Input autoFocus type="number" min={0} max={availYuan} placeholder={t('placeholder')} value={valueYuan ? `${valueYuan}` : undefined} onChange={(value) => setValue(value)} style={{ '--font-size': '28px' }}/>
|
||||
<Input autoFocus type="number" min={0} max={availYuan} placeholder={t('placeholder')} value={valueYuan ? `${valueYuan}` : undefined} onChange={(value) => setValue(value)} style={{ '--font-size': '28px' }} onEnterPress={() => createWithdrawData()}/>
|
||||
</div>
|
||||
<div className={Styles.tips}>
|
||||
<div className={Styles.tipLine}>
|
||||
|
|
|
|||
|
|
@ -9,10 +9,6 @@ export default OakComponent({
|
|||
dealLoss: 1,
|
||||
dealPrice: 1,
|
||||
iState: 1,
|
||||
withdrawAccount: {
|
||||
id: 1,
|
||||
name: 1,
|
||||
},
|
||||
reason: 1,
|
||||
meta: 1,
|
||||
refund$entity: {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ export default OakComponent({
|
|||
formData({ features }) {
|
||||
const { withdraw, create } = this.props;
|
||||
const { refund$entity: refundData, withdrawTransfer$withdraw: transferData } = withdraw;
|
||||
const refundData2 = (refundData).map((refund) => {
|
||||
const refundData2 = (refundData)?.map((refund) => {
|
||||
const { meta, price, loss, iState, $$updateAt$$, reason } = refund;
|
||||
const { lossExplanation, channel } = meta;
|
||||
return {
|
||||
|
|
@ -24,8 +24,8 @@ export default OakComponent({
|
|||
updateAt: !create && dayJs($$updateAt$$).format('YYYY-MM-DD HH:mm'),
|
||||
reason,
|
||||
};
|
||||
});
|
||||
const transferData2 = transferData.map((transfer) => {
|
||||
}) || [];
|
||||
const transferData2 = transferData?.map((transfer) => {
|
||||
const { price, loss, iState, meta, withdrawAccountId, withdrawAccount, $$updateAt$$, reason } = transfer;
|
||||
let withdrawChannel = withdrawAccount?.channel;
|
||||
if (!withdrawChannel && withdrawAccountId) {
|
||||
|
|
@ -65,7 +65,7 @@ export default OakComponent({
|
|||
updateAt: !create && dayJs($$updateAt$$).format('YYYY-MM-DD HH:mm'),
|
||||
reason,
|
||||
};
|
||||
});
|
||||
}) || [];
|
||||
const withdrawExactPrice = ['successful', 'partiallySuccessful', 'failed'].includes(withdraw.iState) ? withdraw.dealPrice - withdraw.dealLoss : withdraw.price - withdraw.loss;
|
||||
return {
|
||||
createAtStr: dayJs(withdraw.$$createAt$$).format('YYYY-MM-DD HH:mm'),
|
||||
|
|
|
|||
|
|
@ -24,7 +24,9 @@ export default function render(props) {
|
|||
</div>
|
||||
{itemData && itemData.map((data, idx) => (<div className={Styles.refundItem} key={idx}>
|
||||
<div className={Styles.header2}>
|
||||
<Tag color={data.typeColor}>{data.type}</Tag>
|
||||
<Tag fill="outline">
|
||||
{data.type}
|
||||
</Tag>
|
||||
</div>
|
||||
{data.iState && <div className={Styles.item}>
|
||||
<span className={Styles.label}>{t('refund:attr.iState')}</span>
|
||||
|
|
|
|||
|
|
@ -46,7 +46,9 @@ export default function render(props) {
|
|||
{itemData && <div className={Styles.refunds}>
|
||||
{itemData.map((data, idx) => (<div className={Styles.refundItem} key={idx}>
|
||||
<div className={Styles.header2}>
|
||||
<Tag color={data.typeColor}>{data.type}</Tag>
|
||||
<Tag>
|
||||
{data.type}
|
||||
</Tag>
|
||||
</div>
|
||||
{data.iState && <div className={Styles.item}>
|
||||
<span className={Styles.label}>{t('refund:attr.iState')}</span>
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export default OakComponent({
|
|||
},
|
||||
},
|
||||
},
|
||||
withdrawTransfer$withdraw: {
|
||||
withdrawTransfer$withdraw$$aggr: {
|
||||
$entity: 'withdrawTransfer',
|
||||
data: {
|
||||
'#count-1': {
|
||||
|
|
@ -60,5 +60,10 @@ export default OakComponent({
|
|||
};
|
||||
}),
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
goBack() {
|
||||
this.navigateBack();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"loss": "预计手续费%{value}%元",
|
||||
"dealLoss": "手续费%{value}%元",
|
||||
"count": "将分%{value}笔提现到您账户"
|
||||
"loss": "预计手续费%{value}元",
|
||||
"dealLoss": "手续费%{value}元",
|
||||
"count": "将分%{value}笔提现到您账户",
|
||||
"noData": "您尚无提现记录"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
import React from 'react';
|
||||
import { EntityDict } from "../../../oak-app-domain";
|
||||
import { WebComponentProps } from "oak-frontend-base";
|
||||
export default function render(props: WebComponentProps<EntityDict, 'withdraw', false, {
|
||||
gotoDetail: (id: string) => void;
|
||||
withdraws?: ({
|
||||
id: string;
|
||||
iState: string;
|
||||
iStateColor: string;
|
||||
price: string;
|
||||
lossDescription: string;
|
||||
countDescription: number;
|
||||
createAt: string;
|
||||
})[];
|
||||
}, {
|
||||
goBack: () => void;
|
||||
}>): React.JSX.Element;
|
||||
|
|
@ -1 +1,20 @@
|
|||
"use strict";
|
||||
import React from 'react';
|
||||
import { List, Tag, Result, Button } from 'antd-mobile';
|
||||
import { HandPayCircleOutline } from 'antd-mobile-icons';
|
||||
export default function render(props) {
|
||||
const { withdraws, gotoDetail } = props.data;
|
||||
const { t, goBack } = props.methods;
|
||||
if (withdraws?.length) {
|
||||
return (<List>
|
||||
{withdraws.map((ele) => (<List.Item prefix={<HandPayCircleOutline fontSize={38}/>} extra={<Tag color={ele.iStateColor}>{ele.iState}</Tag>} title={ele.lossDescription} description={ele.createAt} onClick={() => gotoDetail(ele.id)}>
|
||||
<>
|
||||
<span style={{ marginRight: 3 }}>{t('common::pay.symbol')}</span>
|
||||
{ele.price}
|
||||
</>
|
||||
</List.Item>))}
|
||||
</List>);
|
||||
}
|
||||
return (<Result status="info" title={t('noData')} description={<Button onClick={goBack}>
|
||||
{t('common::back')}
|
||||
</Button>}/>);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,4 +12,6 @@ export default function render(props: WebComponentProps<EntityDict, 'withdraw',
|
|||
countDescription: number;
|
||||
createAt: string;
|
||||
})[];
|
||||
}>): React.JSX.Element | null;
|
||||
}, {
|
||||
goBack: () => void;
|
||||
}>): React.JSX.Element;
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
import React from 'react';
|
||||
import { List, Avatar, Button } from 'antd';
|
||||
import { CalendarOutlined } from '@ant-design/icons';
|
||||
import { List, Avatar, Result, Button } from 'antd';
|
||||
import { CalendarOutlined, NodeExpandOutlined } from '@ant-design/icons';
|
||||
import Styles from './web.pc.module.less';
|
||||
export default function render(props) {
|
||||
const { withdraws, gotoDetail } = props.data;
|
||||
const { t } = props.methods;
|
||||
const { t, goBack } = props.methods;
|
||||
if (withdraws?.length) {
|
||||
return (<List itemLayout="horizontal" dataSource={withdraws} renderItem={(item, index) => (<List.Item key={index} actions={[
|
||||
<Button onClick={() => gotoDetail(item.id)}>
|
||||
{t('common::detail')}
|
||||
{t('common::action.detail')}
|
||||
</Button>
|
||||
]}>
|
||||
<List.Item.Meta avatar={<Avatar size={50} style={{
|
||||
|
|
@ -25,13 +25,18 @@ export default function render(props) {
|
|||
<span>{item.lossDescription}</span>
|
||||
</div>
|
||||
</div>} description={<div className={Styles.description}>
|
||||
<div className={Styles.count}>{item.countDescription}</div>
|
||||
<div className={Styles.count}>
|
||||
<NodeExpandOutlined />
|
||||
<span className={Styles.data}>{item.countDescription}</span>
|
||||
</div>
|
||||
<div className={Styles.createAt}>
|
||||
<CalendarOutlined />
|
||||
<span className={Styles.date}>{item.createAt}</span>
|
||||
<span className={Styles.data}>{item.createAt}</span>
|
||||
</div>
|
||||
</div>}/>
|
||||
</List.Item>)}/>);
|
||||
}
|
||||
return null;
|
||||
return (<Result title={t('noData')} extra={<Button onClick={goBack}>
|
||||
{t('common::back')}
|
||||
</Button>}/>);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,9 +22,7 @@
|
|||
.description {
|
||||
padding: auto;
|
||||
|
||||
.createAt {
|
||||
.date {
|
||||
.data {
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -63,6 +63,7 @@ export default OakComponent({
|
|||
return {
|
||||
entity,
|
||||
entityId,
|
||||
enabled: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -87,6 +88,8 @@ export default OakComponent({
|
|||
});
|
||||
},
|
||||
async removeAccount(id) {
|
||||
const row = this.state.withdrawAccounts.find(ele => ele.id === id);
|
||||
if (row['#oakLegalActions']?.includes('remove')) {
|
||||
return this.execute(undefined, undefined, undefined, [
|
||||
{
|
||||
entity: 'withdrawAccount',
|
||||
|
|
@ -100,6 +103,22 @@ export default OakComponent({
|
|||
}
|
||||
}
|
||||
]);
|
||||
}
|
||||
else {
|
||||
return this.execute(undefined, undefined, undefined, [
|
||||
{
|
||||
entity: 'withdrawAccount',
|
||||
operation: {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'disable',
|
||||
data: {},
|
||||
filter: {
|
||||
id,
|
||||
},
|
||||
}
|
||||
}
|
||||
]);
|
||||
}
|
||||
},
|
||||
resetAll() {
|
||||
this.clean();
|
||||
|
|
@ -111,6 +130,11 @@ export default OakComponent({
|
|||
await this.execute();
|
||||
this.resetAll();
|
||||
},
|
||||
switchDefault(id) {
|
||||
const row = this.state.withdrawAccounts.find(ele => ele.id === id);
|
||||
this.clean();
|
||||
this.updateItem({ isDefault: !row.isDefault }, row.id);
|
||||
},
|
||||
pickOne(id) {
|
||||
this.setState({
|
||||
selectedId: id,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"noData": "您还没有配置过提现账号",
|
||||
"confirmDelete": "确定删除",
|
||||
"areYouSure": "确认删除本账号吗?"
|
||||
"areYouSure": "确认删除本账号吗?",
|
||||
"default": "默认"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
import React from 'react';
|
||||
import { EntityDict } from "../../../oak-app-domain";
|
||||
import { RowWithActions, WebComponentProps } from "oak-frontend-base";
|
||||
export default function render(props: WebComponentProps<EntityDict, 'withdrawAccount', false, {
|
||||
withdrawAccounts?: (RowWithActions<EntityDict, 'withdrawAccount'> & {
|
||||
label: string;
|
||||
symbol: string;
|
||||
allowUpdate?: boolean;
|
||||
allowRemove?: boolean;
|
||||
})[];
|
||||
upsertId: string;
|
||||
entity: string;
|
||||
entityId: string;
|
||||
isCreate?: boolean;
|
||||
asPicker?: boolean;
|
||||
allowCreate?: boolean;
|
||||
selectedId?: string;
|
||||
onCancel?: () => void;
|
||||
}, {
|
||||
setUpsertId: (id: string) => void;
|
||||
doUpdate: () => Promise<void>;
|
||||
resetAll: () => void;
|
||||
updateAccount: (id: string) => void;
|
||||
newAccount: () => void;
|
||||
removeAccount: (id: string) => Promise<void>;
|
||||
switchDefault: (id: string) => void;
|
||||
pickOne: (id: string) => void;
|
||||
confirmPick: () => void;
|
||||
}>): React.JSX.Element;
|
||||
|
|
@ -1 +1,87 @@
|
|||
"use strict";
|
||||
import React from 'react';
|
||||
import { Popup, SwipeAction, List, Button, Result, Checkbox, Switch, Modal } from 'antd-mobile';
|
||||
import Styles from './web.module.less';
|
||||
import WdaUpsert from '../upsert';
|
||||
export default function render(props) {
|
||||
const { isCreate, withdrawAccounts, upsertId, oakFullpath, entity, entityId, asPicker, allowCreate, selectedId, onCancel, oakExecutable } = props.data;
|
||||
const { t, setUpsertId, doUpdate, resetAll, updateAccount, newAccount, pickOne, confirmPick, removeAccount, switchDefault } = props.methods;
|
||||
const U = upsertId && (<Popup visible={!!upsertId} destroyOnClose onMaskClick={() => resetAll()} onClose={() => resetAll()}>
|
||||
<WdaUpsert oakPath={`${oakFullpath}.${upsertId}`} oakId={upsertId} entity={entity} entityId={entityId}/>
|
||||
<div className={Styles.btns}>
|
||||
<Button block onClick={() => resetAll()}>
|
||||
{t('common::action.cancel')}
|
||||
</Button>
|
||||
<Button color="primary" block disabled={oakExecutable !== true} onClick={() => doUpdate()}>
|
||||
{t('common::confirm')}
|
||||
</Button>
|
||||
</div>
|
||||
</Popup>);
|
||||
if (!withdrawAccounts?.length) {
|
||||
return (<div className={Styles.container}>
|
||||
{U}
|
||||
<Result status="warning" title={t('noData')} description={<Button color="primary" onClick={() => newAccount()}>
|
||||
{t('common::action.create')}
|
||||
</Button>}/>
|
||||
</div>);
|
||||
}
|
||||
const getItem = (ele, index) => (<List.Item key={index} prefix={<div className={Styles.avatar}>
|
||||
{ele.symbol}
|
||||
</div>} title={ele.name} description={ele.code} extra={asPicker ? <Checkbox checked={ele.id === selectedId} onChange={(checked) => {
|
||||
if (checked) {
|
||||
pickOne(ele.id);
|
||||
}
|
||||
else {
|
||||
pickOne('');
|
||||
}
|
||||
}}/> : <Switch style={{ '--width': '72px' }} checked={ele.isDefault} checkedText={t('default')} onChange={() => switchDefault(ele.id)}/>}>
|
||||
{ele.label}
|
||||
</List.Item>);
|
||||
return (<div className={Styles.container2}>
|
||||
{U}
|
||||
<div className={Styles.list}>
|
||||
<List>
|
||||
{withdrawAccounts.map((ele, idx) => {
|
||||
const Item = getItem(ele, idx);
|
||||
if (asPicker) {
|
||||
return Item;
|
||||
}
|
||||
return (<SwipeAction key={idx} rightActions={[
|
||||
{
|
||||
key: 'delete',
|
||||
text: t('common::action.remove'),
|
||||
color: 'danger',
|
||||
onClick: () => Modal.confirm({
|
||||
title: t('confirmDelete'),
|
||||
content: t('areYouSure'),
|
||||
onConfirm: () => removeAccount(ele.id),
|
||||
})
|
||||
}
|
||||
]}>
|
||||
{Item}
|
||||
</SwipeAction>);
|
||||
})}
|
||||
</List>
|
||||
</div>
|
||||
<div className={Styles.btns}>
|
||||
{asPicker ? (<>
|
||||
<Button block onClick={() => onCancel()} style={{ marginRight: 6 }}>
|
||||
{t('common::action.cancel')}
|
||||
</Button>
|
||||
<Button block color="primary" disabled={!selectedId} onClick={() => confirmPick()}>
|
||||
{t('common::confirm')}
|
||||
</Button>
|
||||
</>) : (oakExecutable === true ?
|
||||
<>
|
||||
<Button block onClick={() => resetAll()}>
|
||||
{t('common::reset')}
|
||||
</Button>
|
||||
<Button block color="primary" onClick={() => doUpdate()}>
|
||||
{t('common::action.update')}
|
||||
</Button>
|
||||
</> :
|
||||
allowCreate && <Button block color="primary" onClick={() => newAccount()}>
|
||||
{t('common::action.create')}
|
||||
</Button>)}
|
||||
</div>
|
||||
</div>);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,3 +2,40 @@
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
.container2 {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
|
||||
.list {
|
||||
flex: 1;
|
||||
|
||||
|
||||
.avatar {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--oak-color-primary);
|
||||
background-color: silver;
|
||||
}
|
||||
|
||||
.switch {
|
||||
width: 72px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btns {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ export default function render(props: WebComponentProps<EntityDict, 'withdrawAcc
|
|||
updateAccount: (id: string) => void;
|
||||
newAccount: () => void;
|
||||
removeAccount: (id: string) => Promise<void>;
|
||||
switchDefault: (id: string) => void;
|
||||
pickOne: (id: string) => void;
|
||||
confirmPick: () => void;
|
||||
}>): React.JSX.Element;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import React from 'react';
|
||||
import { Modal, Avatar, List, Button, Result, Checkbox } from 'antd';
|
||||
import { Modal, Switch, Avatar, List, Button, Result, Checkbox } from 'antd';
|
||||
import Styles from './web.pc.module.less';
|
||||
import WdaUpsert from '../upsert';
|
||||
export default function render(props) {
|
||||
const { isCreate, withdrawAccounts, upsertId, oakFullpath, entity, entityId, asPicker, allowCreate, selectedId, onCancel } = props.data;
|
||||
const { t, setUpsertId, doUpdate, resetAll, updateAccount, newAccount, pickOne, confirmPick, removeAccount } = props.methods;
|
||||
const { isCreate, withdrawAccounts, upsertId, oakFullpath, entity, entityId, asPicker, allowCreate, selectedId, onCancel, oakExecutable } = props.data;
|
||||
const { t, setUpsertId, doUpdate, resetAll, updateAccount, newAccount, pickOne, confirmPick, removeAccount, switchDefault } = props.methods;
|
||||
const U = upsertId && (<Modal open={!!upsertId} destroyOnClose title={`${isCreate ? t('common::action.create') : t('common::action.update')}${t('withdrawAccount:name')}`} onCancel={() => resetAll()} closeIcon={null} onOk={() => doUpdate()} okText={t('common::confirm')} cancelText={t('common::action.cancel')}>
|
||||
<WdaUpsert oakPath={`${oakFullpath}.${upsertId}`} oakId={upsertId} entity={entity} entityId={entityId}/>
|
||||
</Modal>);
|
||||
|
|
@ -19,7 +19,7 @@ export default function render(props) {
|
|||
return (<div className={Styles.container2}>
|
||||
{U}
|
||||
<div className={Styles.list}>
|
||||
<List itemLayout="horizontal" dataSource={withdrawAccounts} renderItem={(item, index) => (<List.Item actions={asPicker ? [<Checkbox checked={item.id === selectedId} onChange={({ target }) => {
|
||||
<List itemLayout="horizontal" dataSource={withdrawAccounts} renderItem={(item, index) => (<List.Item key={index} actions={asPicker ? [<Checkbox checked={item.id === selectedId} onChange={({ target }) => {
|
||||
const { checked } = target;
|
||||
if (checked) {
|
||||
pickOne(item.id);
|
||||
|
|
@ -28,10 +28,9 @@ export default function render(props) {
|
|||
pickOne('');
|
||||
}
|
||||
}}/>] : [
|
||||
item.allowUpdate && <Button size="small" onClick={() => updateAccount(item.id)}>
|
||||
{t('common::action.update')}
|
||||
</Button>,
|
||||
item.allowRemove && <Button size="small" danger onClick={() => Modal.confirm({
|
||||
item.allowUpdate &&
|
||||
<Switch checkedChildren={t('default')} value={item.isDefault} onChange={(value) => switchDefault(item.id)}/>,
|
||||
<Button size="small" danger onClick={() => Modal.confirm({
|
||||
title: t('confirmDelete'),
|
||||
content: t('areYouSure'),
|
||||
okText: t('common::confirm'),
|
||||
|
|
@ -48,17 +47,25 @@ export default function render(props) {
|
|||
</List.Item>)}/>
|
||||
</div>
|
||||
<div className={Styles.btns}>
|
||||
{!asPicker && allowCreate && <Button type="primary" onClick={() => newAccount()}>
|
||||
{t('common::action.create')}
|
||||
</Button>}
|
||||
{asPicker && <>
|
||||
{asPicker ? (<>
|
||||
<Button onClick={() => onCancel()} style={{ marginRight: 6 }}>
|
||||
{t('common::action.cancel')}
|
||||
</Button>
|
||||
<Button type="primary" disabled={!selectedId} onClick={() => confirmPick()}>
|
||||
{t('common::confirm')}
|
||||
</Button>
|
||||
</>}
|
||||
</>) : (oakExecutable === true ?
|
||||
<>
|
||||
<Button onClick={() => resetAll()}>
|
||||
{t('common::reset')}
|
||||
</Button>
|
||||
<Button type="primary" onClick={() => doUpdate()}>
|
||||
{t('common::action.update')}
|
||||
</Button>
|
||||
</> :
|
||||
allowCreate && <Button type="primary" onClick={() => newAccount()}>
|
||||
{t('common::action.create')}
|
||||
</Button>)}
|
||||
</div>
|
||||
</div>);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
import React from 'react';
|
||||
import { EntityDict } from "../../../oak-app-domain";
|
||||
import { RowWithActions, WebComponentProps } from "oak-frontend-base";
|
||||
export default function render(props: WebComponentProps<EntityDict, 'withdrawAccount', false, {
|
||||
withdrawAccount?: RowWithActions<EntityDict, 'withdrawAccount'>;
|
||||
channels?: {
|
||||
label: string;
|
||||
value: string;
|
||||
}[];
|
||||
channel?: EntityDict['withdrawChannel']['Schema'];
|
||||
isBank?: boolean;
|
||||
isOffline?: boolean;
|
||||
}, {
|
||||
onSetChannelId: (id: string) => void;
|
||||
onUpdate: <T extends keyof EntityDict['withdrawAccount']['OpSchema']>(attr: T, value: EntityDict['withdrawAccount']['OpSchema'][T] | null) => void;
|
||||
}>): React.JSX.Element | undefined;
|
||||
|
|
@ -1 +1,34 @@
|
|||
"use strict";
|
||||
import React from 'react';
|
||||
import { Form, Switch, Input, Selector } from 'antd-mobile';
|
||||
export default function render(props) {
|
||||
const { withdrawAccount, channels, channel, isBank } = props.data;
|
||||
const { t, onSetChannelId, onUpdate } = props.methods;
|
||||
if (withdrawAccount) {
|
||||
return (<Form layout="horizontal">
|
||||
{channels && <Form.Item label={t('withdrawAccount:attr.channel')}>
|
||||
<Selector disabled={!channels} options={channels} value={channel ? [channel.id] : undefined} onChange={(ids) => {
|
||||
onSetChannelId(ids[0]);
|
||||
}}/>
|
||||
</Form.Item>}
|
||||
{isBank &&
|
||||
<Form.Item label={t('label.bank.org')}>
|
||||
<Input value={withdrawAccount.org} onChange={(value) => {
|
||||
onUpdate('org', value);
|
||||
}}/>
|
||||
</Form.Item>}
|
||||
<Form.Item label={isBank ? t('label.bank.name') : t('label.others.name')}>
|
||||
<Input value={withdrawAccount.name} onChange={(value) => {
|
||||
onUpdate('name', value);
|
||||
}}/>
|
||||
</Form.Item>
|
||||
<Form.Item label={t('withdrawAccount:attr.code')}>
|
||||
<Input value={withdrawAccount.code} onChange={(value) => {
|
||||
onUpdate('code', value);
|
||||
}}/>
|
||||
</Form.Item>
|
||||
<Form.Item label={t('withdrawAccount:attr.isDefault')}>
|
||||
<Switch checked={withdrawAccount.isDefault} onChange={(value) => onUpdate('isDefault', value)}/>
|
||||
</Form.Item>
|
||||
</Form>);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ export const authDeduceRelationMap = {};
|
|||
export const selectFreeEntities = [
|
||||
'offlineAccount',
|
||||
'wpProduct',
|
||||
'withdrawChannel',
|
||||
'wpAccount',
|
||||
];
|
||||
export const updateFreeDict = {};
|
||||
export default {
|
||||
|
|
|
|||
|
|
@ -38,6 +38,41 @@ const actionAuths = [
|
|||
id: 'user-account-withdraw',
|
||||
pathId: 'user-account-withdraw',
|
||||
deActions: ['select'],
|
||||
},
|
||||
//account
|
||||
{
|
||||
id: 'account-user',
|
||||
pathId: 'user-acc',
|
||||
deActions: ['select', 'deposit', 'withdraw', 'consume', 'loan'],
|
||||
},
|
||||
//deposit
|
||||
{
|
||||
id: 'deposit-creator',
|
||||
pathId: 'creator-deposit',
|
||||
deActions: ['create'],
|
||||
},
|
||||
{
|
||||
id: 'deposit-account-user',
|
||||
pathId: 'user-account-deposit',
|
||||
deActions: ['fail'],
|
||||
},
|
||||
// accountOper
|
||||
{
|
||||
id: 'user-acc-oper',
|
||||
pathId: 'user-acc-oper',
|
||||
deActions: ['select', 'create'],
|
||||
},
|
||||
// withdrawAccount
|
||||
{
|
||||
id: 'user-withdrawAccount',
|
||||
pathId: 'user-withdrawAccount',
|
||||
deActions: ['select', 'create', 'remove', 'disable', 'update'],
|
||||
},
|
||||
// withdrawTransfer
|
||||
{
|
||||
id: 'user-acc-wdtransfer',
|
||||
pathId: 'user-acc-wdtransfer',
|
||||
deActions: ['select', 'create'],
|
||||
}
|
||||
];
|
||||
export default actionAuths;
|
||||
|
|
|
|||
|
|
@ -457,9 +457,10 @@ const i18ns = [
|
|||
module: "oak-pay-business",
|
||||
position: "src/components/withdraw/list",
|
||||
data: {
|
||||
"loss": "预计手续费%{value}%元",
|
||||
"dealLoss": "手续费%{value}%元",
|
||||
"count": "将分%{value}笔提现到您账户"
|
||||
"loss": "预计手续费%{value}元",
|
||||
"dealLoss": "手续费%{value}元",
|
||||
"count": "将分%{value}笔提现到您账户",
|
||||
"noData": "您尚无提现记录"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -471,7 +472,8 @@ const i18ns = [
|
|||
data: {
|
||||
"noData": "您还没有配置过提现账号",
|
||||
"confirmDelete": "确定删除",
|
||||
"areYouSure": "确认删除本账号吗?"
|
||||
"areYouSure": "确认删除本账号吗?",
|
||||
"default": "默认"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -68,6 +68,48 @@ const paths = [
|
|||
destEntity: 'withdraw',
|
||||
value: 'account.user',
|
||||
recursive: false,
|
||||
},
|
||||
{
|
||||
id: 'user-acc',
|
||||
sourceEntity: 'user',
|
||||
destEntity: 'account',
|
||||
value: 'user',
|
||||
recursive: false,
|
||||
},
|
||||
{
|
||||
id: 'user-acc-oper',
|
||||
sourceEntity: 'user',
|
||||
destEntity: 'accountOper',
|
||||
value: 'account.user',
|
||||
recursive: false,
|
||||
},
|
||||
{
|
||||
id: 'creator-deposit',
|
||||
sourceEntity: 'user',
|
||||
destEntity: 'deposit',
|
||||
value: 'creator',
|
||||
recursive: false,
|
||||
},
|
||||
{
|
||||
id: 'user-account-deposit',
|
||||
sourceEntity: 'user',
|
||||
destEntity: 'deposit',
|
||||
value: 'account.user',
|
||||
recursive: false,
|
||||
},
|
||||
{
|
||||
id: 'user-withdrawAccount',
|
||||
sourceEntity: 'user',
|
||||
destEntity: 'withdrawAccount',
|
||||
value: 'user',
|
||||
recursive: false,
|
||||
},
|
||||
{
|
||||
id: 'user-acc-wdtransfer',
|
||||
sourceEntity: 'user',
|
||||
destEntity: 'withdrawTransfer',
|
||||
value: 'withdraw.account.user',
|
||||
recursive: false,
|
||||
}
|
||||
];
|
||||
export default paths;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
||||
import assert from 'assert';
|
||||
import { getPayClazzAccountEntities } from '../utils/payClazz';
|
||||
// 这里一定要注意两者的先后顺序,如果有注入更多的payEntity的话
|
||||
const entities = getPayClazzAccountEntities();
|
||||
import { accountEntities } from '../checkers/abstractChecker';
|
||||
const triggers = [
|
||||
...entities.filter(ele => !!ele).map((entity) => [
|
||||
...accountEntities.filter(ele => !!ele).map((entity) => [
|
||||
{
|
||||
name: `当${entity}的帐户生成时,则生成对应的withDrawAccount`,
|
||||
entity,
|
||||
|
|
|
|||
|
|
@ -150,6 +150,7 @@ const triggers = [
|
|||
entity: 'pay',
|
||||
action: 'create',
|
||||
when: 'after',
|
||||
asRoot: true,
|
||||
fn: async ({ operation }, context, option) => {
|
||||
const { data } = operation;
|
||||
assert(!(data instanceof Array));
|
||||
|
|
@ -171,6 +172,7 @@ const triggers = [
|
|||
action: ['startPaying', 'succeedPaying', 'continuePaying', 'startClosing', 'succeedClosing', 'startRefunding',
|
||||
'refundAll', 'refundPartially'],
|
||||
when: 'after',
|
||||
asRoot: true,
|
||||
fn: async ({ operation }, context, option) => {
|
||||
const { data, filter, action, id } = operation;
|
||||
assert(typeof filter.id === 'string');
|
||||
|
|
@ -286,6 +288,7 @@ const triggers = [
|
|||
action: 'close',
|
||||
when: 'before',
|
||||
priority: 99,
|
||||
asRoot: true,
|
||||
fn: async ({ operation }, context, option) => {
|
||||
const { data, filter } = operation;
|
||||
const pays = await context.select('pay', {
|
||||
|
|
@ -372,6 +375,7 @@ const triggers = [
|
|||
entity: 'pay',
|
||||
action: 'succeedPaying',
|
||||
when: 'after',
|
||||
asRoot: true,
|
||||
fn: async ({ operation }, context) => {
|
||||
const { data, filter } = operation;
|
||||
const projection = {
|
||||
|
|
@ -441,6 +445,7 @@ const triggers = [
|
|||
{
|
||||
name: '当account类型的pay的paid达到price,改为支付成功',
|
||||
entity: 'pay',
|
||||
asRoot: true,
|
||||
action: ['startPaying', 'continuePaying'],
|
||||
check(operation) {
|
||||
return (!!operation.data.paid) && operation.data.paid > 0;
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ const triggers = [
|
|||
entity: 'refund',
|
||||
action: 'succeedRefunding',
|
||||
when: 'after',
|
||||
asRoot: true,
|
||||
name: '退款成功时,更新对应的pay状态以及对应的withdraw状态',
|
||||
fn: async ({ operation }, context) => {
|
||||
const { filter } = operation;
|
||||
|
|
@ -210,6 +211,7 @@ const triggers = [
|
|||
entity: 'refund',
|
||||
action: 'failRefunding',
|
||||
when: 'after',
|
||||
asRoot: true,
|
||||
name: '退款失败时,更新对应的pay状态以及对应的withdraw状态',
|
||||
fn: async ({ operation }, context) => {
|
||||
const { filter } = operation;
|
||||
|
|
@ -252,6 +254,7 @@ const triggers = [
|
|||
entity: 'refund',
|
||||
name: '当发起退款时,将对应的pay置退款中状态',
|
||||
action: 'create',
|
||||
asRoot: true,
|
||||
when: 'before',
|
||||
fn: async ({ operation }, context) => {
|
||||
const { data } = operation;
|
||||
|
|
|
|||
|
|
@ -1,76 +1,2 @@
|
|||
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
||||
import assert from 'assert';
|
||||
const triggers = [
|
||||
{
|
||||
name: '当withdrawAccount帐户被设置为default时,将其它的default设为false',
|
||||
entity: 'withdrawAccount',
|
||||
action: 'update',
|
||||
check(operation) {
|
||||
return operation.data.isDefault === true;
|
||||
},
|
||||
when: 'after',
|
||||
fn: async ({ operation }, context, option) => {
|
||||
const { filter } = operation;
|
||||
const accounts = await context.select('withdrawAccount', {
|
||||
data: {
|
||||
id: 1,
|
||||
entity: 1,
|
||||
entityId: 1,
|
||||
},
|
||||
filter,
|
||||
}, {});
|
||||
assert(accounts.length === 1);
|
||||
for (const account of accounts) {
|
||||
const { entity, entityId, id } = account;
|
||||
await context.operate('withdrawAccount', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'update',
|
||||
data: {
|
||||
isDefault: false,
|
||||
},
|
||||
filter: {
|
||||
entity,
|
||||
entityId,
|
||||
isDefault: true,
|
||||
id: {
|
||||
$ne: id,
|
||||
},
|
||||
}
|
||||
}, {});
|
||||
}
|
||||
return 1;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '当withdrawAccount帐户创建为default时,将其它的default设为false',
|
||||
entity: 'withdrawAccount',
|
||||
action: 'create',
|
||||
when: 'after',
|
||||
fn: async ({ operation }, context, option) => {
|
||||
const { data } = operation;
|
||||
assert(!(data instanceof Array));
|
||||
const { id, entity, entityId, isDefault } = data;
|
||||
assert(entity && entityId);
|
||||
if (isDefault) {
|
||||
await context.operate('withdrawAccount', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'update',
|
||||
data: {
|
||||
isDefault: false,
|
||||
},
|
||||
filter: {
|
||||
entity,
|
||||
entityId,
|
||||
isDefault: true,
|
||||
id: {
|
||||
$ne: id,
|
||||
},
|
||||
}
|
||||
}, {});
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
];
|
||||
const triggers = [];
|
||||
export default triggers;
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@ import { EntityDict } from '../../oak-app-domain';
|
|||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity';
|
||||
import PayClazz from '../../types/PayClazz';
|
||||
import { BRC } from '../../types/RuntimeCxt';
|
||||
import { StorageSchema } from 'oak-domain/lib/types/Storage';
|
||||
type PayClazzConstructor = (applicationId: string, entityId: string, context: BRC) => Promise<PayClazz>;
|
||||
export declare function registerPayClazzEntity<ED extends EntityDict & BaseEntityDict>(entity: keyof ED, def: {
|
||||
export declare function registerPayClazzEntity<ED extends EntityDict & BaseEntityDict, T extends keyof ED>(entity: T, def: {
|
||||
clazzConstructor: PayClazzConstructor;
|
||||
accountEntity: keyof ED;
|
||||
}): void;
|
||||
export declare function getPayClazzAccountEntities<ED extends EntityDict & BaseEntityDict>(): (keyof ED)[];
|
||||
}, schema: StorageSchema<ED>): void;
|
||||
export declare function getPayClazz(applicationId: string, entity: EntityDict['pay']['OpSchema']['entity'], entityId: string, context: BRC): Promise<PayClazz>;
|
||||
export {};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import assert from 'assert';
|
|||
import Offline from './Offline';
|
||||
import Account from './Account';
|
||||
import WechatPay from './WechatPay';
|
||||
import { registerAccountEntity } from '../../checkers/abstractChecker';
|
||||
const PayChannelDict = {};
|
||||
const PayClazzEntityDict = {
|
||||
'account': {
|
||||
|
|
@ -106,19 +107,27 @@ const PayClazzEntityDict = {
|
|||
// 这里用一个flag来表达先后顺序,如果有registerPayClazzEntity,框架应保证register在get之前
|
||||
// 目前因为没有registerPayClazzEntity,所以没测,可能不对。by Xc 20240608
|
||||
let MODULE_USED = false;
|
||||
export function registerPayClazzEntity(entity, def) {
|
||||
export function registerPayClazzEntity(entity, def, schema) {
|
||||
if (!MODULE_USED) {
|
||||
assert(!MODULE_USED);
|
||||
}
|
||||
PayClazzEntityDict[entity] = {
|
||||
accountEntity: def.accountEntity,
|
||||
clazzConstructor: def.clazzConstructor,
|
||||
};
|
||||
}
|
||||
export function getPayClazzAccountEntities() {
|
||||
MODULE_USED = true;
|
||||
return Object.keys(PayClazzEntityDict).map(ele => PayClazzEntityDict[ele].accountEntity);
|
||||
// 检查此entity是否符合注册要求
|
||||
const { attributes } = schema[entity];
|
||||
const { attributes: payAttr } = schema.pay;
|
||||
const { attributes: accountAttr } = schema[def.accountEntity];
|
||||
assert(payAttr.entity.enumeration?.includes(entity));
|
||||
assert(accountAttr.price && accountAttr.price.type === 'decimal');
|
||||
assert(accountAttr.systemId && accountAttr.systemId.type === 'ref' && accountAttr.systemId.ref === 'system');
|
||||
registerAccountEntity(def.accountEntity);
|
||||
}
|
||||
export async function getPayClazz(applicationId, entity, entityId, context) {
|
||||
MODULE_USED = true;
|
||||
if (!MODULE_USED) {
|
||||
assert(!MODULE_USED);
|
||||
}
|
||||
const key = entity === 'account' ? entity : `${entity}.${entityId}`;
|
||||
if (PayChannelDict.hasOwnProperty(key)) {
|
||||
return PayChannelDict[key];
|
||||
|
|
|
|||
|
|
@ -147,6 +147,9 @@ async function getWithdrawCreateData(params, context) {
|
|||
data: ele,
|
||||
})));
|
||||
}
|
||||
else {
|
||||
data.refund$entity = [];
|
||||
}
|
||||
if (totalPrice > price2) {
|
||||
// 如果还有要退的部分,就从withdrawAccount来进行转账
|
||||
const rest = totalPrice - price2;
|
||||
|
|
@ -200,6 +203,10 @@ async function getWithdrawCreateData(params, context) {
|
|||
}
|
||||
];
|
||||
}
|
||||
else {
|
||||
// 保持结构完整,让上层可以和withdraw$detail同构渲染
|
||||
data.withdrawTransfer$withdraw = [];
|
||||
}
|
||||
data.loss = totalLoss;
|
||||
return data;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { EntityDict } from '../oak-app-domain';
|
||||
import { RuntimeCxt } from '../types/RuntimeCxt';
|
||||
import { Checker } from 'oak-domain/lib/types/Auth';
|
||||
export declare function registerAccountEntity<ED extends EntityDict>(entity: keyof ED): void;
|
||||
export declare const accountEntities: string[];
|
||||
declare const triggers: Checker<EntityDict, keyof EntityDict, RuntimeCxt>[];
|
||||
export default triggers;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.accountEntities = exports.registerAccountEntity = void 0;
|
||||
const uuid_1 = require("oak-domain/lib/utils/uuid");
|
||||
const payClazz_1 = require("../utils/payClazz");
|
||||
// 当注入一个新的pay entity时,将account的删除与之相关联
|
||||
const entities = (0, payClazz_1.getPayClazzAccountEntities)();
|
||||
// 当注入一个新的account entity时,将withdrawChannel的删除与之相关联
|
||||
function registerAccountEntity(entity) {
|
||||
exports.accountEntities.push(entity);
|
||||
}
|
||||
exports.registerAccountEntity = registerAccountEntity;
|
||||
exports.accountEntities = ['wpAccount', 'offlineAccount'];
|
||||
const triggers = [
|
||||
...entities.filter(ele => !!ele).map((entity) => [
|
||||
...exports.accountEntities.filter(ele => !!ele).map((entity) => [
|
||||
{
|
||||
entity,
|
||||
action: 'remove',
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@ const application_1 = tslib_1.__importDefault(require("./application"));
|
|||
const offlineAccount_1 = tslib_1.__importDefault(require("./offlineAccount"));
|
||||
const wpProduct_1 = tslib_1.__importDefault(require("./wpProduct"));
|
||||
const abstractChecker_1 = tslib_1.__importDefault(require("./abstractChecker"));
|
||||
const withdrawAccount_1 = tslib_1.__importDefault(require("./withdrawAccount"));
|
||||
const checkers = [
|
||||
...withdrawAccount_1.default,
|
||||
...abstractChecker_1.default,
|
||||
...accountOper_1.default,
|
||||
...pay_1.default,
|
||||
|
|
|
|||
|
|
@ -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, 'withdrawAccount', RuntimeCxt>[];
|
||||
export default checkers;
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const tslib_1 = require("tslib");
|
||||
const uuid_1 = require("oak-domain/lib/utils/uuid");
|
||||
const executor_1 = require("oak-domain/lib/utils/executor");
|
||||
const assert_1 = tslib_1.__importDefault(require("assert"));
|
||||
const checkers = [
|
||||
{
|
||||
entity: 'withdrawAccount',
|
||||
type: 'logical',
|
||||
action: 'create',
|
||||
checker(operation, context, option) {
|
||||
const { data } = operation;
|
||||
if (data) {
|
||||
const { id, entity, entityId, isDefault } = data;
|
||||
if (entity && entityId && isDefault) {
|
||||
return context.operate('withdrawAccount', {
|
||||
id: (0, uuid_1.generateNewId)(),
|
||||
action: 'update',
|
||||
data: {
|
||||
isDefault: false,
|
||||
},
|
||||
filter: {
|
||||
entity,
|
||||
entityId,
|
||||
isDefault: true,
|
||||
id: {
|
||||
$ne: id,
|
||||
},
|
||||
}
|
||||
}, option);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
entity: 'withdrawAccount',
|
||||
type: 'logical',
|
||||
action: 'update',
|
||||
checker(operation, context, option) {
|
||||
const { data, filter } = operation;
|
||||
if (data?.isDefault) {
|
||||
return (0, executor_1.pipeline)(() => context.select('withdrawAccount', {
|
||||
data: {
|
||||
id: 1,
|
||||
entity: 1,
|
||||
entityId: 1,
|
||||
},
|
||||
filter,
|
||||
}, {}), (accounts) => {
|
||||
(0, assert_1.default)(accounts.length === 1);
|
||||
const [account] = accounts;
|
||||
const { entity, entityId, id } = account;
|
||||
return context.operate('withdrawAccount', {
|
||||
id: (0, uuid_1.generateNewId)(),
|
||||
action: 'update',
|
||||
data: {
|
||||
isDefault: false,
|
||||
},
|
||||
filter: {
|
||||
entity,
|
||||
entityId,
|
||||
isDefault: true,
|
||||
id: {
|
||||
$ne: id,
|
||||
},
|
||||
}
|
||||
}, option);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
exports.default = checkers;
|
||||
|
|
@ -6,6 +6,8 @@ exports.authDeduceRelationMap = {};
|
|||
exports.selectFreeEntities = [
|
||||
'offlineAccount',
|
||||
'wpProduct',
|
||||
'withdrawChannel',
|
||||
'wpAccount',
|
||||
];
|
||||
exports.updateFreeDict = {};
|
||||
exports.default = {
|
||||
|
|
|
|||
|
|
@ -40,6 +40,41 @@ const actionAuths = [
|
|||
id: 'user-account-withdraw',
|
||||
pathId: 'user-account-withdraw',
|
||||
deActions: ['select'],
|
||||
},
|
||||
//account
|
||||
{
|
||||
id: 'account-user',
|
||||
pathId: 'user-acc',
|
||||
deActions: ['select', 'deposit', 'withdraw', 'consume', 'loan'],
|
||||
},
|
||||
//deposit
|
||||
{
|
||||
id: 'deposit-creator',
|
||||
pathId: 'creator-deposit',
|
||||
deActions: ['create'],
|
||||
},
|
||||
{
|
||||
id: 'deposit-account-user',
|
||||
pathId: 'user-account-deposit',
|
||||
deActions: ['fail'],
|
||||
},
|
||||
// accountOper
|
||||
{
|
||||
id: 'user-acc-oper',
|
||||
pathId: 'user-acc-oper',
|
||||
deActions: ['select', 'create'],
|
||||
},
|
||||
// withdrawAccount
|
||||
{
|
||||
id: 'user-withdrawAccount',
|
||||
pathId: 'user-withdrawAccount',
|
||||
deActions: ['select', 'create', 'remove', 'disable', 'update'],
|
||||
},
|
||||
// withdrawTransfer
|
||||
{
|
||||
id: 'user-acc-wdtransfer',
|
||||
pathId: 'user-acc-wdtransfer',
|
||||
deActions: ['select', 'create'],
|
||||
}
|
||||
];
|
||||
exports.default = actionAuths;
|
||||
|
|
|
|||
|
|
@ -459,9 +459,10 @@ const i18ns = [
|
|||
module: "oak-pay-business",
|
||||
position: "src/components/withdraw/list",
|
||||
data: {
|
||||
"loss": "预计手续费%{value}%元",
|
||||
"dealLoss": "手续费%{value}%元",
|
||||
"count": "将分%{value}笔提现到您账户"
|
||||
"loss": "预计手续费%{value}元",
|
||||
"dealLoss": "手续费%{value}元",
|
||||
"count": "将分%{value}笔提现到您账户",
|
||||
"noData": "您尚无提现记录"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -473,7 +474,8 @@ const i18ns = [
|
|||
data: {
|
||||
"noData": "您还没有配置过提现账号",
|
||||
"confirmDelete": "确定删除",
|
||||
"areYouSure": "确认删除本账号吗?"
|
||||
"areYouSure": "确认删除本账号吗?",
|
||||
"default": "默认"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -70,6 +70,48 @@ const paths = [
|
|||
destEntity: 'withdraw',
|
||||
value: 'account.user',
|
||||
recursive: false,
|
||||
},
|
||||
{
|
||||
id: 'user-acc',
|
||||
sourceEntity: 'user',
|
||||
destEntity: 'account',
|
||||
value: 'user',
|
||||
recursive: false,
|
||||
},
|
||||
{
|
||||
id: 'user-acc-oper',
|
||||
sourceEntity: 'user',
|
||||
destEntity: 'accountOper',
|
||||
value: 'account.user',
|
||||
recursive: false,
|
||||
},
|
||||
{
|
||||
id: 'creator-deposit',
|
||||
sourceEntity: 'user',
|
||||
destEntity: 'deposit',
|
||||
value: 'creator',
|
||||
recursive: false,
|
||||
},
|
||||
{
|
||||
id: 'user-account-deposit',
|
||||
sourceEntity: 'user',
|
||||
destEntity: 'deposit',
|
||||
value: 'account.user',
|
||||
recursive: false,
|
||||
},
|
||||
{
|
||||
id: 'user-withdrawAccount',
|
||||
sourceEntity: 'user',
|
||||
destEntity: 'withdrawAccount',
|
||||
value: 'user',
|
||||
recursive: false,
|
||||
},
|
||||
{
|
||||
id: 'user-acc-wdtransfer',
|
||||
sourceEntity: 'user',
|
||||
destEntity: 'withdrawTransfer',
|
||||
value: 'withdraw.account.user',
|
||||
recursive: false,
|
||||
}
|
||||
];
|
||||
exports.default = paths;
|
||||
|
|
|
|||
|
|
@ -3,11 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|||
const tslib_1 = require("tslib");
|
||||
const uuid_1 = require("oak-domain/lib/utils/uuid");
|
||||
const assert_1 = tslib_1.__importDefault(require("assert"));
|
||||
const payClazz_1 = require("../utils/payClazz");
|
||||
// 这里一定要注意两者的先后顺序,如果有注入更多的payEntity的话
|
||||
const entities = (0, payClazz_1.getPayClazzAccountEntities)();
|
||||
const abstractChecker_1 = require("../checkers/abstractChecker");
|
||||
const triggers = [
|
||||
...entities.filter(ele => !!ele).map((entity) => [
|
||||
...abstractChecker_1.accountEntities.filter(ele => !!ele).map((entity) => [
|
||||
{
|
||||
name: `当${entity}的帐户生成时,则生成对应的withDrawAccount`,
|
||||
entity,
|
||||
|
|
|
|||
|
|
@ -153,6 +153,7 @@ const triggers = [
|
|||
entity: 'pay',
|
||||
action: 'create',
|
||||
when: 'after',
|
||||
asRoot: true,
|
||||
fn: async ({ operation }, context, option) => {
|
||||
const { data } = operation;
|
||||
(0, assert_1.default)(!(data instanceof Array));
|
||||
|
|
@ -174,6 +175,7 @@ const triggers = [
|
|||
action: ['startPaying', 'succeedPaying', 'continuePaying', 'startClosing', 'succeedClosing', 'startRefunding',
|
||||
'refundAll', 'refundPartially'],
|
||||
when: 'after',
|
||||
asRoot: true,
|
||||
fn: async ({ operation }, context, option) => {
|
||||
const { data, filter, action, id } = operation;
|
||||
(0, assert_1.default)(typeof filter.id === 'string');
|
||||
|
|
@ -289,6 +291,7 @@ const triggers = [
|
|||
action: 'close',
|
||||
when: 'before',
|
||||
priority: 99,
|
||||
asRoot: true,
|
||||
fn: async ({ operation }, context, option) => {
|
||||
const { data, filter } = operation;
|
||||
const pays = await context.select('pay', {
|
||||
|
|
@ -375,6 +378,7 @@ const triggers = [
|
|||
entity: 'pay',
|
||||
action: 'succeedPaying',
|
||||
when: 'after',
|
||||
asRoot: true,
|
||||
fn: async ({ operation }, context) => {
|
||||
const { data, filter } = operation;
|
||||
const projection = {
|
||||
|
|
@ -444,6 +448,7 @@ const triggers = [
|
|||
{
|
||||
name: '当account类型的pay的paid达到price,改为支付成功',
|
||||
entity: 'pay',
|
||||
asRoot: true,
|
||||
action: ['startPaying', 'continuePaying'],
|
||||
check(operation) {
|
||||
return (!!operation.data.paid) && operation.data.paid > 0;
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ const triggers = [
|
|||
entity: 'refund',
|
||||
action: 'succeedRefunding',
|
||||
when: 'after',
|
||||
asRoot: true,
|
||||
name: '退款成功时,更新对应的pay状态以及对应的withdraw状态',
|
||||
fn: async ({ operation }, context) => {
|
||||
const { filter } = operation;
|
||||
|
|
@ -213,6 +214,7 @@ const triggers = [
|
|||
entity: 'refund',
|
||||
action: 'failRefunding',
|
||||
when: 'after',
|
||||
asRoot: true,
|
||||
name: '退款失败时,更新对应的pay状态以及对应的withdraw状态',
|
||||
fn: async ({ operation }, context) => {
|
||||
const { filter } = operation;
|
||||
|
|
@ -255,6 +257,7 @@ const triggers = [
|
|||
entity: 'refund',
|
||||
name: '当发起退款时,将对应的pay置退款中状态',
|
||||
action: 'create',
|
||||
asRoot: true,
|
||||
when: 'before',
|
||||
fn: async ({ operation }, context) => {
|
||||
const { data } = operation;
|
||||
|
|
|
|||
|
|
@ -1,79 +1,4 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const tslib_1 = require("tslib");
|
||||
const uuid_1 = require("oak-domain/lib/utils/uuid");
|
||||
const assert_1 = tslib_1.__importDefault(require("assert"));
|
||||
const triggers = [
|
||||
{
|
||||
name: '当withdrawAccount帐户被设置为default时,将其它的default设为false',
|
||||
entity: 'withdrawAccount',
|
||||
action: 'update',
|
||||
check(operation) {
|
||||
return operation.data.isDefault === true;
|
||||
},
|
||||
when: 'after',
|
||||
fn: async ({ operation }, context, option) => {
|
||||
const { filter } = operation;
|
||||
const accounts = await context.select('withdrawAccount', {
|
||||
data: {
|
||||
id: 1,
|
||||
entity: 1,
|
||||
entityId: 1,
|
||||
},
|
||||
filter,
|
||||
}, {});
|
||||
(0, assert_1.default)(accounts.length === 1);
|
||||
for (const account of accounts) {
|
||||
const { entity, entityId, id } = account;
|
||||
await context.operate('withdrawAccount', {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'update',
|
||||
data: {
|
||||
isDefault: false,
|
||||
},
|
||||
filter: {
|
||||
entity,
|
||||
entityId,
|
||||
isDefault: true,
|
||||
id: {
|
||||
$ne: id,
|
||||
},
|
||||
}
|
||||
}, {});
|
||||
}
|
||||
return 1;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '当withdrawAccount帐户创建为default时,将其它的default设为false',
|
||||
entity: 'withdrawAccount',
|
||||
action: 'create',
|
||||
when: 'after',
|
||||
fn: async ({ operation }, context, option) => {
|
||||
const { data } = operation;
|
||||
(0, assert_1.default)(!(data instanceof Array));
|
||||
const { id, entity, entityId, isDefault } = data;
|
||||
(0, assert_1.default)(entity && entityId);
|
||||
if (isDefault) {
|
||||
await context.operate('withdrawAccount', {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'update',
|
||||
data: {
|
||||
isDefault: false,
|
||||
},
|
||||
filter: {
|
||||
entity,
|
||||
entityId,
|
||||
isDefault: true,
|
||||
id: {
|
||||
$ne: id,
|
||||
},
|
||||
}
|
||||
}, {});
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
];
|
||||
const triggers = [];
|
||||
exports.default = triggers;
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@ import { EntityDict } from '../../oak-app-domain';
|
|||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity';
|
||||
import PayClazz from '../../types/PayClazz';
|
||||
import { BRC } from '../../types/RuntimeCxt';
|
||||
import { StorageSchema } from 'oak-domain/lib/types/Storage';
|
||||
type PayClazzConstructor = (applicationId: string, entityId: string, context: BRC) => Promise<PayClazz>;
|
||||
export declare function registerPayClazzEntity<ED extends EntityDict & BaseEntityDict>(entity: keyof ED, def: {
|
||||
export declare function registerPayClazzEntity<ED extends EntityDict & BaseEntityDict, T extends keyof ED>(entity: T, def: {
|
||||
clazzConstructor: PayClazzConstructor;
|
||||
accountEntity: keyof ED;
|
||||
}): void;
|
||||
export declare function getPayClazzAccountEntities<ED extends EntityDict & BaseEntityDict>(): (keyof ED)[];
|
||||
}, schema: StorageSchema<ED>): void;
|
||||
export declare function getPayClazz(applicationId: string, entity: EntityDict['pay']['OpSchema']['entity'], entityId: string, context: BRC): Promise<PayClazz>;
|
||||
export {};
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getPayClazz = exports.getPayClazzAccountEntities = exports.registerPayClazzEntity = void 0;
|
||||
exports.getPayClazz = exports.registerPayClazzEntity = void 0;
|
||||
const tslib_1 = require("tslib");
|
||||
const assert_1 = tslib_1.__importDefault(require("assert"));
|
||||
const Offline_1 = tslib_1.__importDefault(require("./Offline"));
|
||||
const Account_1 = tslib_1.__importDefault(require("./Account"));
|
||||
const WechatPay_1 = tslib_1.__importDefault(require("./WechatPay"));
|
||||
const abstractChecker_1 = require("../../checkers/abstractChecker");
|
||||
const PayChannelDict = {};
|
||||
const PayClazzEntityDict = {
|
||||
'account': {
|
||||
|
|
@ -110,21 +111,28 @@ const PayClazzEntityDict = {
|
|||
// 这里用一个flag来表达先后顺序,如果有registerPayClazzEntity,框架应保证register在get之前
|
||||
// 目前因为没有registerPayClazzEntity,所以没测,可能不对。by Xc 20240608
|
||||
let MODULE_USED = false;
|
||||
function registerPayClazzEntity(entity, def) {
|
||||
function registerPayClazzEntity(entity, def, schema) {
|
||||
if (!MODULE_USED) {
|
||||
(0, assert_1.default)(!MODULE_USED);
|
||||
}
|
||||
PayClazzEntityDict[entity] = {
|
||||
accountEntity: def.accountEntity,
|
||||
clazzConstructor: def.clazzConstructor,
|
||||
};
|
||||
// 检查此entity是否符合注册要求
|
||||
const { attributes } = schema[entity];
|
||||
const { attributes: payAttr } = schema.pay;
|
||||
const { attributes: accountAttr } = schema[def.accountEntity];
|
||||
(0, assert_1.default)(payAttr.entity.enumeration?.includes(entity));
|
||||
(0, assert_1.default)(accountAttr.price && accountAttr.price.type === 'decimal');
|
||||
(0, assert_1.default)(accountAttr.systemId && accountAttr.systemId.type === 'ref' && accountAttr.systemId.ref === 'system');
|
||||
(0, abstractChecker_1.registerAccountEntity)(def.accountEntity);
|
||||
}
|
||||
exports.registerPayClazzEntity = registerPayClazzEntity;
|
||||
function getPayClazzAccountEntities() {
|
||||
MODULE_USED = true;
|
||||
return Object.keys(PayClazzEntityDict).map(ele => PayClazzEntityDict[ele].accountEntity);
|
||||
}
|
||||
exports.getPayClazzAccountEntities = getPayClazzAccountEntities;
|
||||
async function getPayClazz(applicationId, entity, entityId, context) {
|
||||
MODULE_USED = true;
|
||||
if (!MODULE_USED) {
|
||||
(0, assert_1.default)(!MODULE_USED);
|
||||
}
|
||||
const key = entity === 'account' ? entity : `${entity}.${entityId}`;
|
||||
if (PayChannelDict.hasOwnProperty(key)) {
|
||||
return PayChannelDict[key];
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
import { generateNewId } from 'oak-domain/lib/utils/uuid';
|
||||
import { EntityDict } from '../oak-app-domain';
|
||||
import { BRC, RuntimeCxt } from '../types/RuntimeCxt';
|
||||
import assert from 'assert';
|
||||
import { getPayClazzAccountEntities } from '../utils/payClazz';
|
||||
import { Checker, LogicalChecker } from 'oak-domain/lib/types/Auth';
|
||||
|
||||
// 当注入一个新的pay entity时,将account的删除与之相关联
|
||||
const entities = getPayClazzAccountEntities<EntityDict>();
|
||||
// 当注入一个新的account entity时,将withdrawChannel的删除与之相关联
|
||||
export function registerAccountEntity<ED extends EntityDict>(entity: keyof ED) {
|
||||
accountEntities.push(entity as string);
|
||||
}
|
||||
|
||||
export const accountEntities = ['wpAccount', 'offlineAccount'];
|
||||
|
||||
const triggers: Checker<EntityDict, keyof EntityDict, RuntimeCxt>[] = [
|
||||
...entities.filter(ele => !!ele).map(
|
||||
...accountEntities.filter(ele => !!ele).map(
|
||||
(entity) => [
|
||||
{
|
||||
entity,
|
||||
|
|
|
|||
|
|
@ -12,10 +12,25 @@ export default OakComponent({
|
|||
systemId: 1,
|
||||
price: 1,
|
||||
enabled: 1,
|
||||
taxLossRatio: 1,
|
||||
refundCompensateRatio: 1,
|
||||
refundGapDays: 1,
|
||||
allowWithdrawTransfer: 1,
|
||||
withdrawTransferLossRatio: 1,
|
||||
},
|
||||
properties: {
|
||||
systemId: '',
|
||||
},
|
||||
filters: [
|
||||
{
|
||||
filter() {
|
||||
const { systemId } = this.props;
|
||||
return {
|
||||
systemId,
|
||||
};
|
||||
}
|
||||
}
|
||||
],
|
||||
formData({ data, legalActions }) {
|
||||
return {
|
||||
accounts: data.map(
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
align-items: stretch;
|
||||
|
||||
.item {
|
||||
border: 0.1px solid silver;
|
||||
|
|
|
|||
|
|
@ -8,31 +8,33 @@ 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';
|
||||
import { ToYuan } from 'oak-domain/lib/utils/money';
|
||||
|
||||
function OfflineAccount(props: {
|
||||
account: RowWithActions<EntityDict, 'offlineAccount'> & { color: string };
|
||||
export function OfflineAccount(props: {
|
||||
data: RowWithActions<EntityDict, 'offlineAccount'> & { color: string };
|
||||
t: (k: string, param?: any) => string;
|
||||
onUpdate: () => void;
|
||||
onRemove: () => void;
|
||||
onQrCodeClick: () => void;
|
||||
onUpdate?: () => void;
|
||||
onRemove?: () => void;
|
||||
onQrCodeClick?: () => void;
|
||||
}) {
|
||||
const { account, t, onUpdate, onRemove, onQrCodeClick } = props;
|
||||
const { type, channel, name, qrCode, allowDeposit, allowPay, color, enabled, price } = account;
|
||||
const legalActions = account['#oakLegalActions'];
|
||||
const { data: account, t, onUpdate, onRemove, onQrCodeClick } = props;
|
||||
const { type, channel, name, qrCode, allowDeposit, allowPay, color, enabled, price,
|
||||
taxLossRatio, refundCompensateRatio, refundGapDays, allowWithdrawTransfer, withdrawTransferLossRatio
|
||||
} = account;
|
||||
|
||||
return (
|
||||
<Descriptions
|
||||
style={{ width: 340, height: 435 }}
|
||||
style={{ width: 380 }}
|
||||
title={t(`offlineAccount:v.type.${type}`)}
|
||||
extra={<>
|
||||
{legalActions.includes('update') && <Button
|
||||
{onUpdate && <Button
|
||||
style={{ marginRight: 4 }}
|
||||
size="small"
|
||||
onClick={() => onUpdate()}
|
||||
>
|
||||
{t('common::action.update')}
|
||||
</Button>}
|
||||
{legalActions.includes('remove') && <Button
|
||||
{onRemove && <Button
|
||||
danger
|
||||
size="small"
|
||||
onClick={() => onRemove()}
|
||||
|
|
@ -43,7 +45,7 @@ function OfflineAccount(props: {
|
|||
column={1}
|
||||
bordered
|
||||
size="small"
|
||||
labelStyle={{ width: 98 }}
|
||||
labelStyle={{ width: 198 }}
|
||||
>
|
||||
<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>}
|
||||
|
|
@ -51,10 +53,15 @@ function OfflineAccount(props: {
|
|||
{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.price')}>{ToYuan(price!)}{t('common::pay.scale')}</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.Item label={t('offlineAccount:attr.taxLossRatio')}>{taxLossRatio}%</Descriptions.Item>
|
||||
<Descriptions.Item label={t('offlineAccount:attr.refundCompensateRatio')}>{refundCompensateRatio}%</Descriptions.Item>
|
||||
<Descriptions.Item label={t('offlineAccount:attr.refundGapDays')}>{refundGapDays}</Descriptions.Item>
|
||||
<Descriptions.Item label={t('offlineAccount:attr.taxLossRatio')}>{taxLossRatio}%</Descriptions.Item>
|
||||
<Descriptions.Item label={t('offlineAccount:attr.allowWithdrawTransfer')}>{t(`common::${allowWithdrawTransfer}`)}</Descriptions.Item>
|
||||
<Descriptions.Item label={t('offlineAccount:attr.withdrawTransferLossRatio')}>{withdrawTransferLossRatio}%</Descriptions.Item>
|
||||
</Descriptions>
|
||||
);
|
||||
}
|
||||
|
|
@ -129,9 +136,9 @@ export default function render(props: WebComponentProps<EntityDict, 'offlineAcco
|
|||
accounts.filter(ele => ele.$$createAt$$ as number > 1).map(
|
||||
(ele, idx) => <div className={Styles.item} key={idx}>
|
||||
<OfflineAccount
|
||||
account={ele}
|
||||
data={ele}
|
||||
t={t}
|
||||
onRemove={() => {
|
||||
onRemove={ele['#oakLegalActions']?.includes('remove') ? () => {
|
||||
Modal.confirm({
|
||||
title: t('confirmDelete'),
|
||||
content: t('areYouSure'),
|
||||
|
|
@ -149,8 +156,8 @@ export default function render(props: WebComponentProps<EntityDict, 'offlineAcco
|
|||
}
|
||||
]),
|
||||
})
|
||||
}}
|
||||
onUpdate={() => setUpsertId(ele.id!)}
|
||||
} : undefined}
|
||||
onUpdate={ele['#oakLegalActions']?.includes('update') ? () => setUpsertId(ele.id!) : undefined}
|
||||
onQrCodeClick={() => setShowQrCodeId(ele.id!)}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ export default OakComponent({
|
|||
return {
|
||||
operation: operation && operation[0].operation,
|
||||
system: data,
|
||||
canUpdate: !!data?.['#oakLegalActions']?.includes('update'),
|
||||
};
|
||||
},
|
||||
actions: ['update'],
|
||||
});
|
||||
|
|
@ -30,7 +30,7 @@ export function registerPayChannelComponent<ED extends EntityDict & BaseEntityDi
|
|||
|
||||
function PayConfig(props: {
|
||||
payConfig: EntityDict['system']['OpSchema']['payConfig'];
|
||||
update: (config: EntityDict['system']['OpSchema']['payConfig']) => void;
|
||||
update?: (config: EntityDict['system']['OpSchema']['payConfig']) => void;
|
||||
t: (key: string) => string;
|
||||
}) {
|
||||
const { payConfig, update, t } = props;
|
||||
|
|
@ -38,7 +38,7 @@ function PayConfig(props: {
|
|||
const depositLoss = payConfig?.depositLoss;
|
||||
|
||||
const updateDepositLoss = (data: Partial<NonNullable<EntityDict['system']['OpSchema']['payConfig']>['depositLoss']>) => {
|
||||
update({
|
||||
update && update({
|
||||
depositLoss: {
|
||||
...depositLoss,
|
||||
...data,
|
||||
|
|
@ -49,7 +49,7 @@ function PayConfig(props: {
|
|||
});
|
||||
};
|
||||
const updateWithdrawLoss = (data: Partial<NonNullable<EntityDict['system']['OpSchema']['payConfig']>['withdrawLoss']>) => {
|
||||
update({
|
||||
update && update({
|
||||
depositLoss: depositLoss || {},
|
||||
withdrawLoss: {
|
||||
conservative: !!(withdrawLoss?.conservative),
|
||||
|
|
@ -84,6 +84,7 @@ function PayConfig(props: {
|
|||
addonAfter={"%"}
|
||||
step={0.01}
|
||||
precision={2}
|
||||
disabled={!update}
|
||||
onChange={(value) => {
|
||||
const ratio = value;
|
||||
updateDepositLoss({
|
||||
|
|
@ -99,6 +100,7 @@ function PayConfig(props: {
|
|||
value={depositLoss?.highest}
|
||||
min={0}
|
||||
step={1}
|
||||
disabled={!update}
|
||||
onChange={(value) => {
|
||||
const highest = value;
|
||||
updateDepositLoss({
|
||||
|
|
@ -115,6 +117,7 @@ function PayConfig(props: {
|
|||
value={depositLoss?.lowest}
|
||||
min={0}
|
||||
step={1}
|
||||
disabled={!update}
|
||||
onChange={(value) => {
|
||||
const lowest = value;
|
||||
updateDepositLoss({
|
||||
|
|
@ -144,6 +147,7 @@ function PayConfig(props: {
|
|||
label={t('payConfig.label.conservative')}
|
||||
>
|
||||
<Switch
|
||||
disabled={!update}
|
||||
value={withdrawLoss?.conservative}
|
||||
onChange={(conservative) => {
|
||||
updateWithdrawLoss({ conservative });
|
||||
|
|
@ -154,7 +158,7 @@ function PayConfig(props: {
|
|||
label={t('payConfig.label.ratio')}
|
||||
>
|
||||
<InputNumber
|
||||
disabled={!!withdrawLoss?.conservative}
|
||||
disabled={!!withdrawLoss?.conservative || !update}
|
||||
value={withdrawLoss?.ratio}
|
||||
max={20}
|
||||
min={0.01}
|
||||
|
|
@ -173,7 +177,7 @@ function PayConfig(props: {
|
|||
label={t('payConfig.label.highest')}
|
||||
>
|
||||
<InputNumber
|
||||
disabled={!!withdrawLoss?.conservative}
|
||||
disabled={!!withdrawLoss?.conservative || !update}
|
||||
value={withdrawLoss?.highest}
|
||||
min={0}
|
||||
step={1}
|
||||
|
|
@ -190,7 +194,7 @@ function PayConfig(props: {
|
|||
label={t('payConfig.label.lowest')}
|
||||
>
|
||||
<InputNumber
|
||||
disabled={!!withdrawLoss?.conservative}
|
||||
disabled={!!withdrawLoss?.conservative || !update}
|
||||
value={withdrawLoss?.lowest}
|
||||
min={0}
|
||||
step={1}
|
||||
|
|
@ -207,7 +211,7 @@ function PayConfig(props: {
|
|||
label={t('payConfig.label.trim')}
|
||||
>
|
||||
<Radio.Group
|
||||
disabled={!!withdrawLoss?.conservative}
|
||||
disabled={!!withdrawLoss?.conservative || !update}
|
||||
options={[
|
||||
{
|
||||
label: t('payConfig.label.jiao'),
|
||||
|
|
@ -238,8 +242,9 @@ export default function render(props: WebComponentProps<EntityDict, 'system', fa
|
|||
system: RowWithActions<EntityDict, 'system'>;
|
||||
operation?: EntityDict['system']['Update'];
|
||||
serverUrl?: string;
|
||||
canUpdate?: boolean;
|
||||
}>) {
|
||||
const { system, oakFullpath, operation, oakDirty, serverUrl, oakExecutable } = props.data;
|
||||
const { system, oakFullpath, operation, oakDirty, serverUrl, oakExecutable, canUpdate } = props.data;
|
||||
const { t, update, clean, execute } = props.methods;
|
||||
|
||||
if (system && oakFullpath) {
|
||||
|
|
@ -262,7 +267,7 @@ export default function render(props: WebComponentProps<EntityDict, 'system', fa
|
|||
>
|
||||
<PayConfig
|
||||
payConfig={system.payConfig}
|
||||
update={(payConfig) => update({ payConfig })}
|
||||
update={canUpdate ? (payConfig) => update({ payConfig }) : undefined}
|
||||
t={t}
|
||||
/>
|
||||
<Flex
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
{}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
import assert from 'assert';
|
||||
import { EntityDict } from "../../../oak-app-domain";
|
||||
import { uniq } from "oak-domain/lib/utils/lodash";
|
||||
|
||||
export default OakComponent({
|
||||
properties: {
|
||||
entities: [] as string[],
|
||||
systemId: '',
|
||||
},
|
||||
lifetimes: {
|
||||
ready() {
|
||||
this.refreshData();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
refreshData() {
|
||||
const { entities, systemId } = this.props;
|
||||
const schema = this.features.cache.getSchema();
|
||||
|
||||
uniq(['wpAccount', 'offlineAccount'].concat(entities || [])).forEach(
|
||||
(entity) => {
|
||||
const projection: EntityDict['offlineAccount']['Selection']['data'] = {
|
||||
id: 1,
|
||||
$$createAt$$: 1,
|
||||
$$updateAt$$: 1,
|
||||
};
|
||||
Object.keys(schema[entity as keyof EntityDict].attributes!).forEach(
|
||||
ele => {
|
||||
if (!ele.startsWith('$$')) {
|
||||
projection[ele] = 1;
|
||||
}
|
||||
}
|
||||
);
|
||||
this.features.cache.refresh(entity as 'offlineAccount', {
|
||||
data: projection,
|
||||
filter: {
|
||||
systemId,
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
},
|
||||
},
|
||||
formData({ features }) {
|
||||
const { entities, systemId } = this.props;
|
||||
assert(systemId);
|
||||
const schema = features.cache.getSchema();
|
||||
let total = 0;
|
||||
const accounts = uniq(['wpAccount', 'offlineAccount'].concat(entities || [])).map(
|
||||
(entity) => {
|
||||
const projection: EntityDict['offlineAccount']['Selection']['data'] = {
|
||||
id: 1,
|
||||
$$createAt$$: 1,
|
||||
$$updateAt$$: 1,
|
||||
};
|
||||
Object.keys(schema[entity as keyof EntityDict].attributes!).forEach(
|
||||
ele => {
|
||||
if (!ele.startsWith('$$')) {
|
||||
projection[ele] = 1;
|
||||
}
|
||||
}
|
||||
);
|
||||
const data = this.features.cache.get(entity as 'offlineAccount', {
|
||||
data: projection,
|
||||
filter: {
|
||||
systemId,
|
||||
}
|
||||
});
|
||||
|
||||
data.forEach(
|
||||
ele => total += ele.price || 0,
|
||||
);
|
||||
return data.map(
|
||||
(ele) => {
|
||||
return {
|
||||
entity,
|
||||
data: {
|
||||
...ele,
|
||||
color: entity === 'offlineAccount' && features.style.getColor('offlineAccount', 'type', ele.type!),
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
).flat();
|
||||
|
||||
return {
|
||||
total,
|
||||
accounts,
|
||||
systemId,
|
||||
};
|
||||
},
|
||||
features: ['cache'],
|
||||
})
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"sysAccount": "系统账户统计",
|
||||
"total": "总余额",
|
||||
"count": "账户个数",
|
||||
"qrCode": "二维码收款",
|
||||
"noDetailRender": "没有注入相应的详情渲染组件"
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
.container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
|
||||
.total {
|
||||
font-weight: bold;
|
||||
color: var(--oak-color-primary);
|
||||
|
||||
.symbol {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.sysAccounts {
|
||||
display: flex;
|
||||
padding: 8px;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 12px;
|
||||
|
||||
.sysAccount {
|
||||
width: 244px;
|
||||
height: 284px;
|
||||
border: solid 0.1px silver;
|
||||
border-radius: 5px;
|
||||
margin-right: 14px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
|
||||
.top {
|
||||
height: 90px;
|
||||
|
||||
.title {
|
||||
font-size: larger;
|
||||
font-weight: bold;
|
||||
font-family: 黑体;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: var(--oak-text-color-disabled);
|
||||
font-size: smaller;
|
||||
}
|
||||
}
|
||||
|
||||
.middle {
|
||||
flex: 1;
|
||||
font-weight: bold;
|
||||
color: var(--oak-color-primary);
|
||||
font-size: large;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.bottom {
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,232 @@
|
|||
import { EntityDict } from "../../../oak-app-domain";
|
||||
import { RowWithActions, WebComponentProps } from "oak-frontend-base";
|
||||
import React from 'react';
|
||||
import { Descriptions, Tag, Form, Switch, Modal, Input, Select, Flex, Button } from 'antd';
|
||||
import { AlipayOutlined, WechatOutlined } from '@ant-design/icons';
|
||||
import Styles from './web.pc.module.less';
|
||||
import { ThousandCont, ToYuan } from "oak-domain/lib/utils/money";
|
||||
import { OfflineAccount as OADetail } from '../../offlineAccount/config/web.pc';
|
||||
import { WpAccount as WADetail } from '../../wpAccount/config/web.pc';
|
||||
|
||||
function OfflineAccount(props: {
|
||||
data: EntityDict['offlineAccount']['OpSchema'] & { color?: string };
|
||||
t: (key: string) => string;
|
||||
}) {
|
||||
const { data, t } = props;
|
||||
const { type, channel, color, name } = data;
|
||||
switch (type) {
|
||||
case 'bank': {
|
||||
return (
|
||||
<Flex
|
||||
style={{ height: '100%' }}
|
||||
vertical
|
||||
gap="small"
|
||||
align="center"
|
||||
justify="center"
|
||||
>
|
||||
<Tag>{t('offlineAccount:name')}</Tag>
|
||||
<div className={Styles.title}>{t(`offlineAccount:v.type.${type}`)}</div>
|
||||
<div className={Styles.subtitle}>{channel}</div>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
case 'alipay': {
|
||||
return (
|
||||
<Flex
|
||||
style={{ height: '100%' }}
|
||||
vertical
|
||||
gap="small"
|
||||
align="center"
|
||||
justify="center"
|
||||
>
|
||||
<Tag>{t('offlineAccount:name')}</Tag>
|
||||
<AlipayOutlined style={{ color, fontSize: 18 }} />
|
||||
<div className={Styles.subtitle}>{name || t('qrCode')}</div>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
case 'wechat': {
|
||||
return (
|
||||
<Flex
|
||||
style={{ height: '100%' }}
|
||||
vertical
|
||||
gap="small"
|
||||
align="center"
|
||||
justify="center"
|
||||
>
|
||||
<Tag>{t('offlineAccount:name')}</Tag>
|
||||
<WechatOutlined style={{ color, fontSize: 18 }} />
|
||||
<div className={Styles.subtitle}>{name || t('qrCode')}</div>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
case 'shouqianba':
|
||||
case 'others': {
|
||||
return (
|
||||
<Flex
|
||||
style={{ height: '100%' }}
|
||||
vertical
|
||||
gap="small"
|
||||
align="center"
|
||||
justify="center"
|
||||
>
|
||||
<Tag>{t('offlineAccount:name')}</Tag>
|
||||
<div className={Styles.title}>{t(`offlineAccount:v.type.${type}`)}</div>
|
||||
<div className={Styles.subtitle}>{name || t('qrCode')}</div>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function WpAccount(props: {
|
||||
data: EntityDict['offlineAccount']['OpSchema'] & { color?: string };
|
||||
t: (key: string) => string;
|
||||
}) {
|
||||
const { data, t } = props;
|
||||
return (
|
||||
<Flex
|
||||
style={{ height: '100%' }}
|
||||
vertical
|
||||
gap="middle"
|
||||
align="center"
|
||||
justify="center"
|
||||
>
|
||||
<Tag>{t('wpAccount:name')}</Tag>
|
||||
<WechatOutlined style={{ color: 'green', fontSize: 34 }} />
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
function GeneralAccount(props: {
|
||||
entity: string;
|
||||
t: (key: string) => string;
|
||||
}) {
|
||||
const { entity, t } = props;
|
||||
return (
|
||||
<Flex
|
||||
style={{ height: '100%' }}
|
||||
vertical
|
||||
gap="small"
|
||||
align="center"
|
||||
justify="center"
|
||||
>
|
||||
<Tag>{t(`${entity}:name`)}</Tag>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
const RenderSysAccountCardTopDict: Record<string, (props: {
|
||||
data: any,
|
||||
t: (k: string) => string
|
||||
}) => JSX.Element> = {
|
||||
'offlineAccount': OfflineAccount,
|
||||
'wpAccount': WpAccount,
|
||||
}
|
||||
|
||||
const RenderSysAccountDetailDict: Record<string, (props: {
|
||||
data: any,
|
||||
t: (k: string) => string
|
||||
systemId: string,
|
||||
}) => JSX.Element> = {
|
||||
'offlineAccount': OADetail,
|
||||
'wpAccount': WADetail,
|
||||
}
|
||||
|
||||
export default function render(props: WebComponentProps<EntityDict, 'offlineAccount', false, {
|
||||
total?: number;
|
||||
accounts?: Array<{
|
||||
entity: string;
|
||||
data: any;
|
||||
price: number
|
||||
}>;
|
||||
systemId?: string;
|
||||
}>) {
|
||||
const { accounts, total, systemId } = props.data;
|
||||
const { t, setMessage } = props.methods;
|
||||
|
||||
if (accounts && systemId) {
|
||||
return (
|
||||
<div className={Styles.container}>
|
||||
<Descriptions
|
||||
title={t('sysAccount')}
|
||||
bordered
|
||||
items={[
|
||||
{
|
||||
label: t('total'),
|
||||
children: <div className={Styles.total}>
|
||||
<span className={Styles.symbol}>{t('common::pay.symbol')}</span>
|
||||
<span className={Styles.value}>{ThousandCont(ToYuan(total || 0), 2)}</span>
|
||||
</div>
|
||||
},
|
||||
{
|
||||
label: t('count'),
|
||||
children: accounts.length,
|
||||
}
|
||||
]}
|
||||
column={2}
|
||||
/>
|
||||
<div className={Styles.sysAccounts}>
|
||||
{
|
||||
accounts.map(
|
||||
({ entity, data }) => {
|
||||
const TopRender = RenderSysAccountCardTopDict[entity];
|
||||
|
||||
return (
|
||||
<div className={Styles.sysAccount}>
|
||||
<div className={Styles.top}>
|
||||
{
|
||||
TopRender ? <TopRender
|
||||
data={data}
|
||||
t={t}
|
||||
/> : <GeneralAccount
|
||||
entity={entity}
|
||||
t={t}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
<div className={Styles.middle}>
|
||||
<span className={Styles.symbol}>{t('common::pay.symbol')}</span>
|
||||
<span className={Styles.value}>{ThousandCont(ToYuan(data.price || 0), 2)}</span>
|
||||
</div>
|
||||
<div className={Styles.bottom}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
const DetailRender = RenderSysAccountDetailDict[entity];
|
||||
if (DetailRender) {
|
||||
Modal.info({
|
||||
width: 560,
|
||||
title: `${t(`${entity}:name`)}${t('common::action.detail')}`,
|
||||
content: (
|
||||
<DetailRender
|
||||
data={data}
|
||||
systemId={systemId}
|
||||
t={t}
|
||||
/>
|
||||
),
|
||||
onOk() { },
|
||||
});
|
||||
}
|
||||
else {
|
||||
setMessage({
|
||||
title: t('noDetailRender'),
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('common::action.detail')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
@ -5,14 +5,30 @@ export default OakComponent({
|
|||
id: 1,
|
||||
price: 1,
|
||||
mchId: 1,
|
||||
refundGapDays: 1,
|
||||
taxLossRatio: 1,
|
||||
enabled: 1,
|
||||
taxLossRatio: 1,
|
||||
refundCompensateRatio: 1,
|
||||
refundGapDays: 1,
|
||||
allowWithdrawTransfer: 1,
|
||||
withdrawTransferLossRatio: 1,
|
||||
},
|
||||
properties: {
|
||||
systemId: '',
|
||||
},
|
||||
filters: [
|
||||
{
|
||||
filter() {
|
||||
const { systemId } = this.props;
|
||||
return {
|
||||
systemId,
|
||||
};
|
||||
}
|
||||
}
|
||||
],
|
||||
formData({ data, legalActions }) {
|
||||
return {
|
||||
accounts: data,
|
||||
canCreate: legalActions?.includes('create'),
|
||||
canCreate: legalActions?.includes('create') && !data?.find(ele => ele.enabled),
|
||||
};
|
||||
},
|
||||
actions: ['create', 'update', 'remove'],
|
||||
|
|
|
|||
|
|
@ -9,20 +9,40 @@ 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';
|
||||
import { ToYuan } from 'oak-domain/lib/utils/money';
|
||||
|
||||
function WpAccount(props: {
|
||||
account: RowWithActions<EntityDict, 'wpAccount'>,
|
||||
oakFullpath: string;
|
||||
export function WpAccount(props: {
|
||||
data: RowWithActions<EntityDict, 'wpAccount'>,
|
||||
systemId: string;
|
||||
t: (k: string, param?: any) => string;
|
||||
onUpdate: () => void;
|
||||
onRemove: () => void;
|
||||
onUpdate?: () => void;
|
||||
onRemove?: () => void;
|
||||
}) {
|
||||
const { account, t, onUpdate, onRemove, oakFullpath, systemId } = props;
|
||||
const { refundGapDays, mchId, enabled, price, taxLossRatio } = account;
|
||||
const legalActions = account['#oakLegalActions'];
|
||||
const { data: account, t, onUpdate, onRemove, systemId } = props;
|
||||
const { refundGapDays, mchId, enabled, price, taxLossRatio,
|
||||
refundCompensateRatio, allowWithdrawTransfer, withdrawTransferLossRatio
|
||||
} = account;
|
||||
|
||||
const [activeKey, setActiveKey] = useState("1");
|
||||
const D = (
|
||||
<Descriptions
|
||||
column={1}
|
||||
bordered
|
||||
title={t('wpAccount:name')}
|
||||
size="small"
|
||||
>
|
||||
<Descriptions.Item label={t('wpAccount:attr.mchId')}>{mchId}</Descriptions.Item>
|
||||
<Descriptions.Item label={t('wpAccount:attr.price')}>{ToYuan(price!)}{t('common::pay.scale')}</Descriptions.Item>
|
||||
<Descriptions.Item label={t('wpAccount:attr.taxLossRatio')}>{taxLossRatio}%</Descriptions.Item>
|
||||
<Descriptions.Item label={t('wpAccount:attr.refundCompensateRatio')}>{refundCompensateRatio}%</Descriptions.Item>
|
||||
<Descriptions.Item label={t('wpAccount:attr.refundGapDays')}>{refundGapDays}</Descriptions.Item>
|
||||
<Descriptions.Item label={t('wpAccount:attr.taxLossRatio')}>{taxLossRatio}%</Descriptions.Item>
|
||||
<Descriptions.Item label={t('wpAccount:attr.allowWithdrawTransfer')}>{t(`common::${allowWithdrawTransfer}`)}</Descriptions.Item>
|
||||
<Descriptions.Item label={t('wpAccount:attr.withdrawTransferLossRatio')}>{withdrawTransferLossRatio}%</Descriptions.Item>
|
||||
<Descriptions.Item label={t('wpAccount:attr.enabled')}>{t(`common::${enabled}`)}</Descriptions.Item>
|
||||
</Descriptions>
|
||||
);
|
||||
if (onUpdate) {
|
||||
return (
|
||||
<Tabs
|
||||
activeKey={activeKey}
|
||||
|
|
@ -31,14 +51,14 @@ function WpAccount(props: {
|
|||
}}
|
||||
tabBarExtraContent={
|
||||
activeKey === "1" && <>
|
||||
{legalActions.includes('update') && <Button
|
||||
{onUpdate && <Button
|
||||
style={{ marginRight: 4 }}
|
||||
size="small"
|
||||
onClick={() => onUpdate()}
|
||||
>
|
||||
{t('common::action.update')}
|
||||
</Button>}
|
||||
{legalActions.includes('remove') && <Button
|
||||
{onRemove && <Button
|
||||
danger
|
||||
size="small"
|
||||
onClick={() => onRemove()}
|
||||
|
|
@ -52,19 +72,7 @@ function WpAccount(props: {
|
|||
{
|
||||
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.taxLossRatio')}>{taxLossRatio}%</Descriptions.Item>
|
||||
<Descriptions.Item label={t('wpAccount:attr.enabled')}>{t(`common::${enabled}`)}</Descriptions.Item>
|
||||
</Descriptions>
|
||||
)
|
||||
children: D,
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
|
|
@ -82,6 +90,9 @@ function WpAccount(props: {
|
|||
);
|
||||
}
|
||||
|
||||
return D;
|
||||
}
|
||||
|
||||
export default function render(props: WebComponentProps<EntityDict, 'wpAccount', true, {
|
||||
accounts?: (RowWithActions<EntityDict, 'wpAccount'> & {})[];
|
||||
systemId: string;
|
||||
|
|
@ -143,11 +154,10 @@ export default function render(props: WebComponentProps<EntityDict, 'wpAccount',
|
|||
accounts.filter(ele => ele.$$createAt$$ as number > 1).map(
|
||||
(ele, idx) => <div className={Styles.item} key={idx}>
|
||||
<WpAccount
|
||||
oakFullpath={oakFullpath}
|
||||
systemId={systemId}
|
||||
account={ele}
|
||||
data={ele}
|
||||
t={t}
|
||||
onRemove={() => {
|
||||
onRemove={ele['#oakLegalActions']?.includes('remove') ? () => {
|
||||
Modal.confirm({
|
||||
title: t('confirmDelete'),
|
||||
content: t('areYouSure'),
|
||||
|
|
@ -165,8 +175,8 @@ export default function render(props: WebComponentProps<EntityDict, 'wpAccount',
|
|||
}
|
||||
]),
|
||||
})
|
||||
}}
|
||||
onUpdate={() => setUpsertId(ele.id!)}
|
||||
} : undefined}
|
||||
onUpdate={ele['#oakLegalActions']?.includes('update') ? () => setUpsertId(ele.id!) : undefined}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@ export default OakComponent({
|
|||
price: 0,
|
||||
enabled: true,
|
||||
taxLossRatio: 0.6,
|
||||
refundCompensateRatio: 100,
|
||||
allowWithdrawTransfer: false,
|
||||
});
|
||||
const { systemId } = this.props;
|
||||
const { data: [ wechatPay ]} = await this.features.cache.refresh('wechatPay', {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
"refundNotifyUrl": "endpoint",
|
||||
"apiV3Key": "需要登录商户后台获取",
|
||||
"refundGapDays": "超过这个天数后无法退款",
|
||||
"allowWithdrawTransfer": "开启转账允许后,提现时用户即可选择从此商户号提现到微信零钱,帐户需要开启此项能力",
|
||||
"allowWithdrawTransfer": "开启转账允许后,提现时用户即可选择从此商户号提现到微信零钱,微信商户必须要开启此项能力(当前尚未实现)",
|
||||
"withdrawTransferLossRatio": "渠道转账(提现)时收取的手续费百分比,0.6代表千分之六"
|
||||
},
|
||||
"wechatPayIsShared": "以上配置是全局的,请谨慎修改"
|
||||
|
|
|
|||
|
|
@ -331,6 +331,19 @@ const i18ns: I18n[] = [
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "c014581e9b3508d49c8dc75b2c7f5730",
|
||||
namespace: "oak-pay-business-c-sysAccount-survey",
|
||||
language: "zh-CN",
|
||||
module: "oak-pay-business",
|
||||
position: "src/components/sysAccount/survey",
|
||||
data: {
|
||||
"sysAccount": "系统账户统计",
|
||||
"total": "总余额",
|
||||
"count": "账户个数",
|
||||
"qrCode": "二维码收款"
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "79255be8c093dfef9765b3f367cab553",
|
||||
namespace: "oak-pay-business-c-wechatPay-upsert",
|
||||
|
|
@ -527,7 +540,7 @@ const i18ns: I18n[] = [
|
|||
"refundNotifyUrl": "endpoint",
|
||||
"apiV3Key": "需要登录商户后台获取",
|
||||
"refundGapDays": "超过这个天数后无法退款",
|
||||
"allowWithdrawTransfer": "开启转账允许后,提现时用户即可选择从此商户号提现到微信零钱,帐户需要开启此项能力",
|
||||
"allowWithdrawTransfer": "开启转账允许后,提现时用户即可选择从此商户号提现到微信零钱,微信商户必须要开启此项能力(当前尚未实现)",
|
||||
"withdrawTransferLossRatio": "渠道转账(提现)时收取的手续费百分比,0.6代表千分之六"
|
||||
},
|
||||
"wechatPayIsShared": "以上配置是全局的,请谨慎修改"
|
||||
|
|
|
|||
|
|
@ -2,15 +2,12 @@ import { CreateTriggerInTxn, RemoveTriggerInTxn, Trigger, UpdateTriggerInTxn } f
|
|||
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
||||
import { EntityDict } from '../oak-app-domain';
|
||||
import { BRC } from '../types/RuntimeCxt';
|
||||
import { DATA_SUBSCRIBER_KEYS } from '../config/constants';
|
||||
import assert from 'assert';
|
||||
import { getPayClazzAccountEntities } from '../utils/payClazz';
|
||||
|
||||
// 这里一定要注意两者的先后顺序,如果有注入更多的payEntity的话
|
||||
const entities = getPayClazzAccountEntities<EntityDict>();
|
||||
import { accountEntities } from '../checkers/abstractChecker';
|
||||
|
||||
const triggers: Trigger<EntityDict, keyof EntityDict, BRC>[] = [
|
||||
...entities.filter(ele => !!ele).map(
|
||||
...accountEntities.filter(ele => !!ele).map(
|
||||
(entity) => [
|
||||
{
|
||||
name: `当${entity}的帐户生成时,则生成对应的withDrawAccount`,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import Account from './Account';
|
|||
import { WebConfig, WechatMpConfig, WechatPublicConfig } from 'oak-general-business/lib/entities/Application';
|
||||
import WechatPay from './WechatPay';
|
||||
import { StorageSchema } from 'oak-domain/lib/types/Storage';
|
||||
import { registerAccountEntity } from '../../checkers/abstractChecker';
|
||||
|
||||
|
||||
type PayClazzConstructor = (applicationId: string, entityId: string, context: BRC) => Promise<PayClazz>;
|
||||
|
|
@ -141,13 +142,9 @@ export function registerPayClazzEntity<ED extends EntityDict & BaseEntityDict, T
|
|||
|
||||
assert(payAttr.entity.enumeration?.includes(entity as string));
|
||||
assert(accountAttr.price && accountAttr.price.type === 'decimal');
|
||||
}
|
||||
assert(accountAttr.systemId && accountAttr.systemId.type === 'ref' && accountAttr.systemId.ref === 'system');
|
||||
|
||||
export function getPayClazzAccountEntities<ED extends EntityDict & BaseEntityDict>() {
|
||||
MODULE_USED = true;
|
||||
return Object.keys(PayClazzEntityDict).map(
|
||||
ele => PayClazzEntityDict[ele].accountEntity as keyof ED
|
||||
);
|
||||
registerAccountEntity(def.accountEntity);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue