修改了Account类型的支付逻辑,支持多次支付
This commit is contained in:
parent
507c43d87f
commit
962298ea98
|
|
@ -1,4 +1,7 @@
|
|||
import { OakInputIllegalException } from 'oak-domain/lib/types';
|
||||
import { CHECKER_MAX_PRIORITY } from 'oak-domain/lib/types/Trigger';
|
||||
import { pipeline } from 'oak-domain/lib/utils/executor';
|
||||
import assert from 'assert';
|
||||
import { PAY_CHANNEL_ACCOUNT_NAME, PAY_CHANNEL_OFFLINE_NAME } from '../types/PayConfig';
|
||||
const checkers = [
|
||||
{
|
||||
|
|
@ -103,6 +106,33 @@ const checkers = [
|
|||
channel: PAY_CHANNEL_OFFLINE_NAME,
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
// 如果在开始支付或者继续支付过程中,paid达到了price,pay的状态可以改为paid
|
||||
entity: 'pay',
|
||||
type: 'logical',
|
||||
action: ['continuePaying', 'startPaying'],
|
||||
priority: CHECKER_MAX_PRIORITY - 1, // 要超过action矩阵定义的赋state值
|
||||
checker: (operation, context) => {
|
||||
const { data, filter } = operation;
|
||||
assert(filter && typeof filter.id === 'string');
|
||||
const { paid } = data;
|
||||
if (paid) {
|
||||
return pipeline(() => context.select('pay', {
|
||||
data: {
|
||||
id: 1,
|
||||
paid: 1,
|
||||
price: 1,
|
||||
},
|
||||
}, {}), (pays) => {
|
||||
const [pay] = pays;
|
||||
const { paid: payPaid, price } = pay;
|
||||
if (payPaid + paid === price) {
|
||||
data.iState === 'paid';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
export default checkers;
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ const attrUpdateMatrix = {
|
|||
}
|
||||
},
|
||||
paid: {
|
||||
actions: ['succeedPaying'],
|
||||
actions: ['succeedPaying', 'continuePaying'],
|
||||
},
|
||||
refunded: {
|
||||
actions: ['refundPartially', 'refundAll'],
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export interface Schema extends EntityShape {
|
|||
type IAction = 'startPaying' | 'succeedPaying' | 'close' | 'startRefunding' | 'refundAll' | 'refundPartially' | 'stopRefunding';
|
||||
type IState = 'unpaid' | 'paying' | 'paid' | 'closed' | 'refunding' | 'partiallyRefunded' | 'refunded';
|
||||
export declare const IActionDef: ActionDef<IAction, IState>;
|
||||
type Action = IAction | 'closeRefund';
|
||||
type Action = IAction | 'closeRefund' | 'continuePaying';
|
||||
export declare const entityDesc: EntityDesc<Schema, Action, '', {
|
||||
iState: IState;
|
||||
}>;
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ export const entityDesc = {
|
|||
},
|
||||
action: {
|
||||
startPaying: '开始支付',
|
||||
continuePaying: '继续支付',
|
||||
succeedPaying: '支付成功',
|
||||
close: '关闭',
|
||||
startRefunding: '开始退款',
|
||||
|
|
@ -105,6 +106,7 @@ export const entityDesc = {
|
|||
refundPartially: '',
|
||||
closeRefund: '',
|
||||
stopRefunding: '',
|
||||
continuePaying: '',
|
||||
},
|
||||
color: {
|
||||
iState: {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
||||
import { DATA_SUBSCRIBER_KEYS } from '../config/constants';
|
||||
import assert from 'assert';
|
||||
const triggers = [
|
||||
|
|
@ -16,5 +17,76 @@ const triggers = [
|
|||
return 1;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '当account帐户的avail增加时,如果有等待中的pay,则继续完成支付',
|
||||
entity: 'account',
|
||||
action: ['deposit', 'withdrawBack', 'repay'],
|
||||
when: 'after',
|
||||
fn: async ({ operation }, context, option) => {
|
||||
const { id, filter } = operation;
|
||||
assert(typeof filter?.id === 'string');
|
||||
const [account] = await context.select('account', {
|
||||
data: {
|
||||
id: 1,
|
||||
avail: 1,
|
||||
pay$account: {
|
||||
$entity: 'pay',
|
||||
data: {
|
||||
id: 1,
|
||||
paid: 1,
|
||||
price: 1,
|
||||
},
|
||||
filter: {
|
||||
iState: 'paying',
|
||||
orderId: {
|
||||
$exists: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
id: filter.id,
|
||||
}
|
||||
}, {});
|
||||
const { avail, pay$account: pays } = account;
|
||||
let rest = avail;
|
||||
let count = 0;
|
||||
if (pays && pays.length > 0) {
|
||||
for (const pay of pays) {
|
||||
if (rest === 0) {
|
||||
break;
|
||||
}
|
||||
const { price, paid } = pay;
|
||||
const paid2 = Math.min(price - paid, rest);
|
||||
await context.operate('pay', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'continuePaying',
|
||||
data: {
|
||||
paid: paid + paid2,
|
||||
accountOper$entity: [
|
||||
{
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'create',
|
||||
data: {
|
||||
id: await generateNewIdAsync(),
|
||||
type: 'consume',
|
||||
availPlus: -paid2,
|
||||
totalPlus: -paid2,
|
||||
accountId: filter.id,
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
filter: {
|
||||
id: pay.id,
|
||||
}
|
||||
}, {});
|
||||
rest = rest - paid2;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
},
|
||||
},
|
||||
];
|
||||
export default triggers;
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ const triggers = [
|
|||
},
|
||||
},
|
||||
{
|
||||
name: '当account depoist时,根据系统配置,扣除掉损耗',
|
||||
name: '当account depoist时,根据系统配置,扣除掉损耗,如果有没支付完整的account类型的pay则继续支付',
|
||||
entity: 'accountOper',
|
||||
action: 'create',
|
||||
when: 'after',
|
||||
|
|
@ -75,27 +75,27 @@ const triggers = [
|
|||
},
|
||||
}, {});
|
||||
const { price, application, channel } = pay;
|
||||
if (PAY_ORG_CHANNELS.includes(channel)) {
|
||||
const ratio = getDepositRatio(channel, application);
|
||||
if (ratio > 0) {
|
||||
const loss = Math.ceil(price * ratio / 100);
|
||||
await context.operate('accountOper', {
|
||||
assert(PAY_ORG_CHANNELS.includes(channel));
|
||||
const ratio = getDepositRatio(channel, application);
|
||||
let count = 0;
|
||||
if (ratio > 0) {
|
||||
const loss = Math.ceil(price * ratio / 100);
|
||||
await context.operate('accountOper', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'create',
|
||||
data: {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'create',
|
||||
data: {
|
||||
id: await generateNewIdAsync(),
|
||||
type: 'loss',
|
||||
totalPlus: -loss,
|
||||
availPlus: -loss,
|
||||
entity,
|
||||
entityId,
|
||||
accountId: data.accountId,
|
||||
}
|
||||
}, {});
|
||||
return 1;
|
||||
}
|
||||
type: 'loss',
|
||||
totalPlus: -loss,
|
||||
availPlus: -loss,
|
||||
entity,
|
||||
entityId,
|
||||
accountId: data.accountId,
|
||||
}
|
||||
}, {});
|
||||
count++;
|
||||
}
|
||||
return 0;
|
||||
return count;
|
||||
}
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { OpSchema as Refund } from "../../oak-app-domain/Refund/Schema";
|
||||
import { Schema, OpSchema as Pay, UpdateOperationData as PayUpdateData } from "../../oak-app-domain/Pay/Schema";
|
||||
import PayClazz from "../../types/PayClazz";
|
||||
import { BRC } from "../../types/RuntimeCxt";
|
||||
export default class Account implements PayClazz {
|
||||
refund(refund: Refund): Promise<undefined>;
|
||||
closeRefund(refund: Refund): Promise<void>;
|
||||
|
|
@ -11,7 +12,7 @@ export default class Account implements PayClazz {
|
|||
extra?: PayUpdateData | undefined;
|
||||
}>;
|
||||
channel: string;
|
||||
prepay(pay: Schema, data: PayUpdateData): Promise<void>;
|
||||
prepay(pay: Schema, data: PayUpdateData, context: BRC): Promise<void>;
|
||||
getState(pay: Pay): Promise<[string, PayUpdateData]>;
|
||||
close(pay: Pay): Promise<void>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,28 +15,40 @@ export default class Account {
|
|||
throw new Error("account类型的pay不需调用此接口");
|
||||
}
|
||||
channel = PAY_CHANNEL_ACCOUNT_NAME;
|
||||
async prepay(pay, data) {
|
||||
async prepay(pay, data, context) {
|
||||
const { accountId, price } = pay;
|
||||
assert(accountId);
|
||||
/**
|
||||
* account类型的支付就是直接从account中扣除款项
|
||||
* 但是注意最多只能把avail扣空。
|
||||
* 如果一个pay没有完全支付,等account中充值了会自动继续进行支付
|
||||
*/
|
||||
data.iState = 'paid',
|
||||
data.accountOper$entity = [
|
||||
{
|
||||
const [account] = await context.select('account', {
|
||||
data: {
|
||||
id: 1,
|
||||
avail: 1,
|
||||
},
|
||||
filter: {
|
||||
id: accountId,
|
||||
}
|
||||
}, { forUpdate: true });
|
||||
const { avail } = account;
|
||||
const paid = Math.min(price, avail);
|
||||
data.accountOper$entity = [
|
||||
{
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'create',
|
||||
data: {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'create',
|
||||
data: {
|
||||
id: await generateNewIdAsync(),
|
||||
totalPlus: -price,
|
||||
availPlus: -price,
|
||||
type: 'consume',
|
||||
accountId,
|
||||
},
|
||||
}
|
||||
];
|
||||
totalPlus: -paid,
|
||||
availPlus: -paid,
|
||||
type: 'consume',
|
||||
accountId,
|
||||
},
|
||||
}
|
||||
];
|
||||
data.meta = {};
|
||||
data.paid = pay.price;
|
||||
data.paid = paid;
|
||||
}
|
||||
getState(pay) {
|
||||
throw new Error("account类型的pay不应该需要查询此状态");
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import WechatPay from './WechatPay';
|
||||
import WechatPayDebug from './WechatPay.debug';
|
||||
declare const _default: typeof WechatPayDebug | typeof WechatPay;
|
||||
declare const _default: typeof WechatPay | typeof WechatPayDebug;
|
||||
export default _default;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const tslib_1 = require("tslib");
|
||||
const types_1 = require("oak-domain/lib/types");
|
||||
const Trigger_1 = require("oak-domain/lib/types/Trigger");
|
||||
const executor_1 = require("oak-domain/lib/utils/executor");
|
||||
const assert_1 = tslib_1.__importDefault(require("assert"));
|
||||
const PayConfig_1 = require("../types/PayConfig");
|
||||
const checkers = [
|
||||
{
|
||||
|
|
@ -105,6 +109,33 @@ const checkers = [
|
|||
channel: PayConfig_1.PAY_CHANNEL_OFFLINE_NAME,
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
// 如果在开始支付或者继续支付过程中,paid达到了price,pay的状态可以改为paid
|
||||
entity: 'pay',
|
||||
type: 'logical',
|
||||
action: ['continuePaying', 'startPaying'],
|
||||
priority: Trigger_1.CHECKER_MAX_PRIORITY - 1, // 要超过action矩阵定义的赋state值
|
||||
checker: (operation, context) => {
|
||||
const { data, filter } = operation;
|
||||
(0, assert_1.default)(filter && typeof filter.id === 'string');
|
||||
const { paid } = data;
|
||||
if (paid) {
|
||||
return (0, executor_1.pipeline)(() => context.select('pay', {
|
||||
data: {
|
||||
id: 1,
|
||||
paid: 1,
|
||||
price: 1,
|
||||
},
|
||||
}, {}), (pays) => {
|
||||
const [pay] = pays;
|
||||
const { paid: payPaid, price } = pay;
|
||||
if (payPaid + paid === price) {
|
||||
data.iState === 'paid';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
exports.default = checkers;
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ const attrUpdateMatrix = {
|
|||
}
|
||||
},
|
||||
paid: {
|
||||
actions: ['succeedPaying'],
|
||||
actions: ['succeedPaying', 'continuePaying'],
|
||||
},
|
||||
refunded: {
|
||||
actions: ['refundPartially', 'refundAll'],
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export interface Schema extends EntityShape {
|
|||
type IAction = 'startPaying' | 'succeedPaying' | 'close' | 'startRefunding' | 'refundAll' | 'refundPartially' | 'stopRefunding';
|
||||
type IState = 'unpaid' | 'paying' | 'paid' | 'closed' | 'refunding' | 'partiallyRefunded' | 'refunded';
|
||||
export declare const IActionDef: ActionDef<IAction, IState>;
|
||||
type Action = IAction | 'closeRefund';
|
||||
type Action = IAction | 'closeRefund' | 'continuePaying';
|
||||
export declare const entityDesc: EntityDesc<Schema, Action, '', {
|
||||
iState: IState;
|
||||
}>;
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ exports.entityDesc = {
|
|||
},
|
||||
action: {
|
||||
startPaying: '开始支付',
|
||||
continuePaying: '继续支付',
|
||||
succeedPaying: '支付成功',
|
||||
close: '关闭',
|
||||
startRefunding: '开始退款',
|
||||
|
|
@ -108,6 +109,7 @@ exports.entityDesc = {
|
|||
refundPartially: '',
|
||||
closeRefund: '',
|
||||
stopRefunding: '',
|
||||
continuePaying: '',
|
||||
},
|
||||
color: {
|
||||
iState: {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const tslib_1 = require("tslib");
|
||||
const uuid_1 = require("oak-domain/lib/utils/uuid");
|
||||
const constants_1 = require("../config/constants");
|
||||
const assert_1 = tslib_1.__importDefault(require("assert"));
|
||||
const triggers = [
|
||||
|
|
@ -19,5 +20,76 @@ const triggers = [
|
|||
return 1;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '当account帐户的avail增加时,如果有等待中的pay,则继续完成支付',
|
||||
entity: 'account',
|
||||
action: ['deposit', 'withdrawBack', 'repay'],
|
||||
when: 'after',
|
||||
fn: async ({ operation }, context, option) => {
|
||||
const { id, filter } = operation;
|
||||
(0, assert_1.default)(typeof filter?.id === 'string');
|
||||
const [account] = await context.select('account', {
|
||||
data: {
|
||||
id: 1,
|
||||
avail: 1,
|
||||
pay$account: {
|
||||
$entity: 'pay',
|
||||
data: {
|
||||
id: 1,
|
||||
paid: 1,
|
||||
price: 1,
|
||||
},
|
||||
filter: {
|
||||
iState: 'paying',
|
||||
orderId: {
|
||||
$exists: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
id: filter.id,
|
||||
}
|
||||
}, {});
|
||||
const { avail, pay$account: pays } = account;
|
||||
let rest = avail;
|
||||
let count = 0;
|
||||
if (pays && pays.length > 0) {
|
||||
for (const pay of pays) {
|
||||
if (rest === 0) {
|
||||
break;
|
||||
}
|
||||
const { price, paid } = pay;
|
||||
const paid2 = Math.min(price - paid, rest);
|
||||
await context.operate('pay', {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'continuePaying',
|
||||
data: {
|
||||
paid: paid + paid2,
|
||||
accountOper$entity: [
|
||||
{
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'create',
|
||||
data: {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
type: 'consume',
|
||||
availPlus: -paid2,
|
||||
totalPlus: -paid2,
|
||||
accountId: filter.id,
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
filter: {
|
||||
id: pay.id,
|
||||
}
|
||||
}, {});
|
||||
rest = rest - paid2;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
},
|
||||
},
|
||||
];
|
||||
exports.default = triggers;
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ const triggers = [
|
|||
},
|
||||
},
|
||||
{
|
||||
name: '当account depoist时,根据系统配置,扣除掉损耗',
|
||||
name: '当account depoist时,根据系统配置,扣除掉损耗,如果有没支付完整的account类型的pay则继续支付',
|
||||
entity: 'accountOper',
|
||||
action: 'create',
|
||||
when: 'after',
|
||||
|
|
@ -78,27 +78,27 @@ const triggers = [
|
|||
},
|
||||
}, {});
|
||||
const { price, application, channel } = pay;
|
||||
if (PayConfig_1.PAY_ORG_CHANNELS.includes(channel)) {
|
||||
const ratio = (0, pay_1.getDepositRatio)(channel, application);
|
||||
if (ratio > 0) {
|
||||
const loss = Math.ceil(price * ratio / 100);
|
||||
await context.operate('accountOper', {
|
||||
(0, assert_1.default)(PayConfig_1.PAY_ORG_CHANNELS.includes(channel));
|
||||
const ratio = (0, pay_1.getDepositRatio)(channel, application);
|
||||
let count = 0;
|
||||
if (ratio > 0) {
|
||||
const loss = Math.ceil(price * ratio / 100);
|
||||
await context.operate('accountOper', {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'create',
|
||||
data: {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'create',
|
||||
data: {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
type: 'loss',
|
||||
totalPlus: -loss,
|
||||
availPlus: -loss,
|
||||
entity,
|
||||
entityId,
|
||||
accountId: data.accountId,
|
||||
}
|
||||
}, {});
|
||||
return 1;
|
||||
}
|
||||
type: 'loss',
|
||||
totalPlus: -loss,
|
||||
availPlus: -loss,
|
||||
entity,
|
||||
entityId,
|
||||
accountId: data.accountId,
|
||||
}
|
||||
}, {});
|
||||
count++;
|
||||
}
|
||||
return 0;
|
||||
return count;
|
||||
}
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { OpSchema as Refund } from "../../oak-app-domain/Refund/Schema";
|
||||
import { Schema, OpSchema as Pay, UpdateOperationData as PayUpdateData } from "../../oak-app-domain/Pay/Schema";
|
||||
import PayClazz from "../../types/PayClazz";
|
||||
import { BRC } from "../../types/RuntimeCxt";
|
||||
export default class Account implements PayClazz {
|
||||
refund(refund: Refund): Promise<undefined>;
|
||||
closeRefund(refund: Refund): Promise<void>;
|
||||
|
|
@ -11,7 +12,7 @@ export default class Account implements PayClazz {
|
|||
extra?: PayUpdateData | undefined;
|
||||
}>;
|
||||
channel: string;
|
||||
prepay(pay: Schema, data: PayUpdateData): Promise<void>;
|
||||
prepay(pay: Schema, data: PayUpdateData, context: BRC): Promise<void>;
|
||||
getState(pay: Pay): Promise<[string, PayUpdateData]>;
|
||||
close(pay: Pay): Promise<void>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,28 +18,40 @@ class Account {
|
|||
throw new Error("account类型的pay不需调用此接口");
|
||||
}
|
||||
channel = PayConfig_1.PAY_CHANNEL_ACCOUNT_NAME;
|
||||
async prepay(pay, data) {
|
||||
async prepay(pay, data, context) {
|
||||
const { accountId, price } = pay;
|
||||
(0, assert_1.default)(accountId);
|
||||
/**
|
||||
* account类型的支付就是直接从account中扣除款项
|
||||
* 但是注意最多只能把avail扣空。
|
||||
* 如果一个pay没有完全支付,等account中充值了会自动继续进行支付
|
||||
*/
|
||||
data.iState = 'paid',
|
||||
data.accountOper$entity = [
|
||||
{
|
||||
const [account] = await context.select('account', {
|
||||
data: {
|
||||
id: 1,
|
||||
avail: 1,
|
||||
},
|
||||
filter: {
|
||||
id: accountId,
|
||||
}
|
||||
}, { forUpdate: true });
|
||||
const { avail } = account;
|
||||
const paid = Math.min(price, avail);
|
||||
data.accountOper$entity = [
|
||||
{
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'create',
|
||||
data: {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'create',
|
||||
data: {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
totalPlus: -price,
|
||||
availPlus: -price,
|
||||
type: 'consume',
|
||||
accountId,
|
||||
},
|
||||
}
|
||||
];
|
||||
totalPlus: -paid,
|
||||
availPlus: -paid,
|
||||
type: 'consume',
|
||||
accountId,
|
||||
},
|
||||
}
|
||||
];
|
||||
data.meta = {};
|
||||
data.paid = pay.price;
|
||||
data.paid = paid;
|
||||
}
|
||||
getState(pay) {
|
||||
throw new Error("account类型的pay不应该需要查询此状态");
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import WechatPay from './WechatPay';
|
||||
import WechatPayDebug from './WechatPay.debug';
|
||||
declare const _default: typeof WechatPayDebug | typeof WechatPay;
|
||||
declare const _default: typeof WechatPay | typeof WechatPayDebug;
|
||||
export default _default;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ import { EntityDict } from '@project/oak-app-domain';
|
|||
import { RuntimeCxt } from '../types/RuntimeCxt';
|
||||
import { OakInputIllegalException } from 'oak-domain/lib/types';
|
||||
import { generateNewId, generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
||||
import { CHECKER_MAX_PRIORITY } from 'oak-domain/lib/types/Trigger';
|
||||
import { pipeline } from 'oak-domain/lib/utils/executor';
|
||||
import assert from 'assert';
|
||||
import { PAY_CHANNEL_ACCOUNT_NAME, PAY_CHANNEL_OFFLINE_NAME } from '@project/types/PayConfig';
|
||||
|
||||
|
|
@ -115,6 +117,36 @@ const checkers: Checker<EntityDict, 'pay', RuntimeCxt>[] = [
|
|||
channel: PAY_CHANNEL_OFFLINE_NAME,
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
// 如果在开始支付或者继续支付过程中,paid达到了price,pay的状态可以改为paid
|
||||
entity: 'pay',
|
||||
type: 'logical',
|
||||
action: ['continuePaying', 'startPaying'],
|
||||
priority: CHECKER_MAX_PRIORITY - 1, // 要超过action矩阵定义的赋state值
|
||||
checker: (operation, context) => {
|
||||
const { data, filter } = operation as EntityDict['pay']['Update'];
|
||||
assert(filter && typeof filter.id === 'string');
|
||||
const { paid } = data;
|
||||
if (paid) {
|
||||
return pipeline(
|
||||
() => context.select('pay', {
|
||||
data: {
|
||||
id: 1,
|
||||
paid: 1,
|
||||
price: 1,
|
||||
},
|
||||
}, {}),
|
||||
(pays: EntityDict['pay']['OpSchema'][]) => {
|
||||
const [ pay ] = pays;
|
||||
const { paid: payPaid, price } = pay;
|
||||
if (payPaid + paid === price) {
|
||||
data.iState === 'paid';
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ const attrUpdateMatrix: AttrUpdateMatrix<EntityDict> = {
|
|||
}
|
||||
},
|
||||
paid: {
|
||||
actions: ['succeedPaying'],
|
||||
actions: ['succeedPaying', 'continuePaying'],
|
||||
},
|
||||
refunded: {
|
||||
actions: ['refundPartially', 'refundAll'],
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ export const IActionDef: ActionDef<IAction, IState> = {
|
|||
},
|
||||
is: 'unpaid',
|
||||
};
|
||||
type Action = IAction | 'closeRefund';
|
||||
type Action = IAction | 'closeRefund' | 'continuePaying';
|
||||
|
||||
export const entityDesc: EntityDesc<Schema, Action, '', {
|
||||
iState: IState,
|
||||
|
|
@ -122,6 +122,7 @@ export const entityDesc: EntityDesc<Schema, Action, '', {
|
|||
},
|
||||
action: {
|
||||
startPaying: '开始支付',
|
||||
continuePaying: '继续支付',
|
||||
succeedPaying: '支付成功',
|
||||
close: '关闭',
|
||||
startRefunding: '开始退款',
|
||||
|
|
@ -153,6 +154,7 @@ export const entityDesc: EntityDesc<Schema, Action, '', {
|
|||
refundPartially: '',
|
||||
closeRefund: '',
|
||||
stopRefunding: '',
|
||||
continuePaying: '',
|
||||
},
|
||||
color: {
|
||||
iState: {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,80 @@ const triggers: Trigger<EntityDict, 'account', BRC>[] = [
|
|||
return 1;
|
||||
},
|
||||
} as UpdateTriggerInTxn<EntityDict, 'account', BRC>,
|
||||
{
|
||||
name: '当account帐户的avail增加时,如果有等待中的pay,则继续完成支付',
|
||||
entity: 'account',
|
||||
action: ['deposit', 'withdrawBack', 'repay'],
|
||||
when: 'after',
|
||||
fn: async ({ operation }, context, option) => {
|
||||
const { id, filter } = operation;
|
||||
assert(typeof filter?.id === 'string');
|
||||
const [account] = await context.select('account', {
|
||||
data: {
|
||||
id: 1,
|
||||
avail: 1,
|
||||
pay$account: {
|
||||
$entity: 'pay',
|
||||
data: {
|
||||
id: 1,
|
||||
paid: 1,
|
||||
price: 1,
|
||||
},
|
||||
filter: {
|
||||
iState: 'paying',
|
||||
orderId: {
|
||||
$exists: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
id: filter!.id,
|
||||
}
|
||||
}, {});
|
||||
|
||||
const { avail, pay$account: pays } = account;
|
||||
let rest = avail!;
|
||||
let count = 0;
|
||||
if (pays && pays.length > 0) {
|
||||
for (const pay of pays) {
|
||||
if (rest === 0) {
|
||||
break;
|
||||
}
|
||||
const { price, paid } = pay;
|
||||
const paid2 = Math.min(price! - paid!, rest);
|
||||
|
||||
await context.operate('pay', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'continuePaying',
|
||||
data: {
|
||||
paid: paid! + paid2,
|
||||
accountOper$entity: [
|
||||
{
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'create',
|
||||
data: {
|
||||
id: await generateNewIdAsync(),
|
||||
type: 'consume',
|
||||
availPlus: -paid2,
|
||||
totalPlus: -paid2,
|
||||
accountId: filter!.id,
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
filter: {
|
||||
id: pay!.id!,
|
||||
}
|
||||
}, {});
|
||||
rest = rest - paid2;
|
||||
count ++;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
},
|
||||
} as UpdateTriggerInTxn<EntityDict, 'account', BRC>,
|
||||
];
|
||||
|
||||
export default triggers;
|
||||
|
|
@ -49,7 +49,7 @@ const triggers: Trigger<EntityDict, 'accountOper', BRC>[] = [
|
|||
},
|
||||
} as CreateTriggerInTxn<EntityDict, 'accountOper', BRC>,
|
||||
{
|
||||
name: '当account depoist时,根据系统配置,扣除掉损耗',
|
||||
name: '当account depoist时,根据系统配置,扣除掉损耗,如果有没支付完整的account类型的pay则继续支付',
|
||||
entity: 'accountOper',
|
||||
action: 'create',
|
||||
when: 'after',
|
||||
|
|
@ -83,30 +83,28 @@ const triggers: Trigger<EntityDict, 'accountOper', BRC>[] = [
|
|||
}, {});
|
||||
|
||||
const { price, application, channel } = pay;
|
||||
if (PAY_ORG_CHANNELS.includes(channel!)) {
|
||||
const ratio = getDepositRatio(channel!, application!);
|
||||
assert(PAY_ORG_CHANNELS.includes(channel!));
|
||||
const ratio = getDepositRatio(channel!, application!);
|
||||
|
||||
if (ratio > 0) {
|
||||
const loss = Math.ceil(price! * ratio / 100);
|
||||
await context.operate('accountOper', {
|
||||
let count = 0;
|
||||
if (ratio > 0) {
|
||||
const loss = Math.ceil(price! * ratio / 100);
|
||||
await context.operate('accountOper', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'create',
|
||||
data: {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'create',
|
||||
data: {
|
||||
id: await generateNewIdAsync(),
|
||||
type: 'loss',
|
||||
totalPlus: -loss,
|
||||
availPlus: -loss,
|
||||
entity,
|
||||
entityId,
|
||||
accountId: data.accountId!,
|
||||
}
|
||||
}, {});
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
type: 'loss',
|
||||
totalPlus: -loss,
|
||||
availPlus: -loss,
|
||||
entity,
|
||||
entityId,
|
||||
accountId: data.accountId!,
|
||||
}
|
||||
}, {});
|
||||
count ++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
} as CreateTriggerInTxn<EntityDict, 'accountOper', BRC>,
|
||||
];
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import PayClazz from "../../types/PayClazz";
|
|||
import { PAY_CHANNEL_ACCOUNT_NAME } from '../../types/PayConfig';
|
||||
import assert from 'assert';
|
||||
import { generateNewIdAsync } from "oak-domain/lib/utils/uuid";
|
||||
import { BRC } from "@project/types/RuntimeCxt";
|
||||
|
||||
export default class Account implements PayClazz {
|
||||
async refund(refund: Refund): Promise<undefined> {
|
||||
|
|
@ -20,28 +21,41 @@ export default class Account implements PayClazz {
|
|||
}
|
||||
channel = PAY_CHANNEL_ACCOUNT_NAME;
|
||||
|
||||
async prepay(pay: Schema, data: PayUpdateData) {
|
||||
async prepay(pay: Schema, data: PayUpdateData, context: BRC) {
|
||||
const { accountId, price } = pay;
|
||||
assert(accountId);
|
||||
/**
|
||||
* account类型的支付就是直接从account中扣除款项
|
||||
* 但是注意最多只能把avail扣空。
|
||||
* 如果一个pay没有完全支付,等account中充值了会自动继续进行支付
|
||||
*/
|
||||
data.iState = 'paid',
|
||||
const [account] = await context.select('account', {
|
||||
data: {
|
||||
id: 1,
|
||||
avail: 1,
|
||||
},
|
||||
filter: {
|
||||
id: accountId,
|
||||
}
|
||||
}, { forUpdate: true });
|
||||
const { avail } = account;
|
||||
|
||||
const paid = Math.min(price!, avail!);
|
||||
data.accountOper$entity = [
|
||||
{
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'create',
|
||||
data: {
|
||||
id: await generateNewIdAsync(),
|
||||
totalPlus: -price!,
|
||||
availPlus: -price!,
|
||||
totalPlus: -paid,
|
||||
availPlus: -paid,
|
||||
type: 'consume',
|
||||
accountId,
|
||||
},
|
||||
}
|
||||
];
|
||||
data.meta = {};
|
||||
data.paid = pay.price;
|
||||
data.paid = paid;
|
||||
}
|
||||
|
||||
getState(pay: Pay): Promise<[string, PayUpdateData]> {
|
||||
|
|
|
|||
Loading…
Reference in New Issue