开始退款逻辑中不检查0元退款的opers

This commit is contained in:
lxy 2025-09-25 14:24:18 +08:00
parent 033e068663
commit 02979c3a41
9 changed files with 230 additions and 197 deletions

View File

@ -32,6 +32,9 @@ export const desc = {
params: {
length: 64
}
},
latestAt: {
type: "datetime"
}
},
actionType: "crud",

View File

@ -2,7 +2,7 @@ import { ForeignKey } from "oak-domain/lib/types/DataType";
import { Q_DateValue, Q_BooleanValue, Q_NumberValue, Q_StringValue, NodeId, ExprOp, ExpressionKey } from "oak-domain/lib/types/Demand";
import { MakeAction as OakMakeAction, EntityShape } from "oak-domain/lib/types/Entity";
import { GenericAction } from "oak-domain/lib/actions/action";
import { String, Boolean } from "oak-domain/lib/types/DataType";
import { String, Boolean, Datetime } from "oak-domain/lib/types/DataType";
export type OpSchema = EntityShape & {
name: String<32>;
isArticle: Boolean;
@ -10,6 +10,7 @@ export type OpSchema = EntityShape & {
isLeaf?: Boolean | null;
entity: String<32>;
entityId: String<64>;
latestAt?: Datetime | null;
} & {
[A in ExpressionKey]?: any;
};
@ -25,6 +26,7 @@ export type OpFilter = {
isLeaf: Q_BooleanValue;
entity: Q_StringValue;
entityId: Q_StringValue;
latestAt: Q_DateValue;
} & ExprOp<OpAttr | string>;
export type OpProjection = {
"#id"?: NodeId;
@ -39,6 +41,7 @@ export type OpProjection = {
isLeaf?: number;
entity?: number;
entityId?: number;
latestAt?: number;
} & Partial<ExprOp<OpAttr | string>>;
export type OpSortAttr = Partial<{
id: number;
@ -51,6 +54,7 @@ export type OpSortAttr = Partial<{
isLeaf: number;
entity: number;
entityId: number;
latestAt: number;
[k: string]: any;
} | ExprOp<OpAttr | string>>;
export type OpAction = OakMakeAction<GenericAction | string>;

View File

@ -7,6 +7,7 @@
"entity": "对象",
"entityId": "对象Id",
"isLeaf": "结点下是否存在叶子结点",
"files": "文件"
"files": "文件",
"latestAt": "最近编辑"
}
}

View File

@ -25,6 +25,7 @@ async function startRefunding(context, data) {
},
order: {
id: 1,
price: 1,
paid: 1,
refunded: 1,
settled: 1,
@ -112,50 +113,14 @@ async function startRefunding(context, data) {
}
}, { forUpdate: true });
const { accountOper$entity: opers, } = data;
// 对opers检查
if (order.settled === (order.paid - order.refunded)) {
// 已全部结算
// 已全部结算的订单由refund传入的opers决定退款检查opers的金额总和等于refund的金额
let amount = 0;
// 有明确的来源account
assert(opers && opers instanceof Array, '已结算的订单退款一定要有相应的account来源');
opers.forEach(({ action, data }) => {
assert(action === 'create');
const { type, totalPlus, availPlus, accountId } = data;
assert(type === 'refund');
assert(totalPlus === availPlus);
const settlement = settledSettlements?.find((ele) => ele?.accountId === accountId);
assert(!!settlement, '请从订单分账的相关账户中选择订单退款的account来源');
amount += -totalPlus;
});
assert(amount === data.price);
//将order的settled更新
const newSettled = order.settled - data.price;
await context.operate('order', {
id: await generateNewIdAsync(),
action: 'update',
data: {
settled: newSettled,
},
filter: {
id: order.id,
}
}, {});
}
else if (order.settled === 0) {
//未结算
//未结算的订单无opers
assert(!(opers && opers.length > 0));
}
else {
//部分结算
/**
* 部分结算的订单:
* 1. opers的金额总和 <= 订单已结算金额
* 2. opers的金额总和 + unsettled的金额 >= 退款金额
*/
let opersPrice = 0;
if (opers && opers.length > 0) {
// 对opers检查 退款0元无需检查
if (!(data.price === 0 && order.price === 0 && pay.paid === 0)) {
if (order.settled === (order.paid - order.refunded)) {
// 已全部结算
// 已全部结算的订单由refund传入的opers决定退款检查opers的金额总和等于refund的金额
let amount = 0;
// 有明确的来源account
assert(opers && opers instanceof Array, '已结算的订单退款一定要有相应的account来源');
opers.forEach(({ action, data }) => {
assert(action === 'create');
const { type, totalPlus, availPlus, accountId } = data;
@ -163,29 +128,70 @@ async function startRefunding(context, data) {
assert(totalPlus === availPlus);
const settlement = settledSettlements?.find((ele) => ele?.accountId === accountId);
assert(!!settlement, '请从订单分账的相关账户中选择订单退款的account来源');
opersPrice += -totalPlus;
amount += -totalPlus;
});
assert(amount === data.price);
//将order的settled更新
const newSettled = order.settled - data.price;
await context.operate('order', {
id: await generateNewIdAsync(),
action: 'update',
data: {
settled: newSettled,
},
filter: {
id: order.id,
}
}, {});
}
const { paid, refunded, settled, settlePlan$order: settlePlans } = order;
const orderUnsettledPrice = paid - refunded - settled;
if (opersPrice > settled) {
throw new refundOpersExceed(opersPrice - settled);
else if (order.settled === 0) {
//未结算
//未结算的订单无opers
assert(!(opers && opers.length > 0));
}
if (opersPrice < data.price - orderUnsettledPrice) {
throw new refundOpersNotEnough(opersPrice - (data.price - orderUnsettledPrice));
}
//将order的settled更新
const newSettled = order.settled - opersPrice;
await context.operate('order', {
id: await generateNewIdAsync(),
action: 'update',
data: {
settled: newSettled,
},
filter: {
id: order.id,
else {
//部分结算
/**
* 部分结算的订单:
* 1. opers的金额总和 <= 订单已结算金额
* 2. opers的金额总和 + unsettled的金额 >= 退款金额
*/
let opersPrice = 0;
if (opers && opers.length > 0) {
opers.forEach(({ action, data }) => {
assert(action === 'create');
const { type, totalPlus, availPlus, accountId } = data;
assert(type === 'refund');
assert(totalPlus === availPlus);
const settlement = settledSettlements?.find((ele) => ele?.accountId === accountId);
assert(!!settlement, '请从订单分账的相关账户中选择订单退款的account来源');
opersPrice += -totalPlus;
});
}
}, {});
const { paid, refunded, settled, settlePlan$order: settlePlans } = order;
const orderUnsettledPrice = paid - refunded - settled;
if (opersPrice > settled) {
throw new refundOpersExceed(opersPrice - settled);
}
if (opersPrice < data.price - orderUnsettledPrice) {
throw new refundOpersNotEnough(opersPrice - (data.price - orderUnsettledPrice));
}
//将order的settled更新
const newSettled = order.settled - opersPrice;
await context.operate('order', {
id: await generateNewIdAsync(),
action: 'update',
data: {
settled: newSettled,
},
filter: {
id: order.id,
}
}, {});
}
}
else {
assert(order.price === 0 && data.price === 0, '0元订单退款金额不可大于订单金额');
}
}
data.pay = {

View File

@ -35,6 +35,9 @@ exports.desc = {
params: {
length: 64
}
},
latestAt: {
type: "datetime"
}
},
actionType: "crud",

View File

@ -2,7 +2,7 @@ import { ForeignKey } from "oak-domain/lib/types/DataType";
import { Q_DateValue, Q_BooleanValue, Q_NumberValue, Q_StringValue, NodeId, ExprOp, ExpressionKey } from "oak-domain/lib/types/Demand";
import { MakeAction as OakMakeAction, EntityShape } from "oak-domain/lib/types/Entity";
import { GenericAction } from "oak-domain/lib/actions/action";
import { String, Boolean } from "oak-domain/lib/types/DataType";
import { String, Boolean, Datetime } from "oak-domain/lib/types/DataType";
export type OpSchema = EntityShape & {
name: String<32>;
isArticle: Boolean;
@ -10,6 +10,7 @@ export type OpSchema = EntityShape & {
isLeaf?: Boolean | null;
entity: String<32>;
entityId: String<64>;
latestAt?: Datetime | null;
} & {
[A in ExpressionKey]?: any;
};
@ -25,6 +26,7 @@ export type OpFilter = {
isLeaf: Q_BooleanValue;
entity: Q_StringValue;
entityId: Q_StringValue;
latestAt: Q_DateValue;
} & ExprOp<OpAttr | string>;
export type OpProjection = {
"#id"?: NodeId;
@ -39,6 +41,7 @@ export type OpProjection = {
isLeaf?: number;
entity?: number;
entityId?: number;
latestAt?: number;
} & Partial<ExprOp<OpAttr | string>>;
export type OpSortAttr = Partial<{
id: number;
@ -51,6 +54,7 @@ export type OpSortAttr = Partial<{
isLeaf: number;
entity: number;
entityId: number;
latestAt: number;
[k: string]: any;
} | ExprOp<OpAttr | string>>;
export type OpAction = OakMakeAction<GenericAction | string>;

View File

@ -7,6 +7,7 @@
"entity": "对象",
"entityId": "对象Id",
"isLeaf": "结点下是否存在叶子结点",
"files": "文件"
"files": "文件",
"latestAt": "最近编辑"
}
}

View File

@ -28,6 +28,7 @@ async function startRefunding(context, data) {
},
order: {
id: 1,
price: 1,
paid: 1,
refunded: 1,
settled: 1,
@ -115,50 +116,14 @@ async function startRefunding(context, data) {
}
}, { forUpdate: true });
const { accountOper$entity: opers, } = data;
// 对opers检查
if (order.settled === (order.paid - order.refunded)) {
// 已全部结算
// 已全部结算的订单由refund传入的opers决定退款检查opers的金额总和等于refund的金额
let amount = 0;
// 有明确的来源account
(0, assert_1.default)(opers && opers instanceof Array, '已结算的订单退款一定要有相应的account来源');
opers.forEach(({ action, data }) => {
(0, assert_1.default)(action === 'create');
const { type, totalPlus, availPlus, accountId } = data;
(0, assert_1.default)(type === 'refund');
(0, assert_1.default)(totalPlus === availPlus);
const settlement = settledSettlements?.find((ele) => ele?.accountId === accountId);
(0, assert_1.default)(!!settlement, '请从订单分账的相关账户中选择订单退款的account来源');
amount += -totalPlus;
});
(0, assert_1.default)(amount === data.price);
//将order的settled更新
const newSettled = order.settled - data.price;
await context.operate('order', {
id: await (0, uuid_1.generateNewIdAsync)(),
action: 'update',
data: {
settled: newSettled,
},
filter: {
id: order.id,
}
}, {});
}
else if (order.settled === 0) {
//未结算
//未结算的订单无opers
(0, assert_1.default)(!(opers && opers.length > 0));
}
else {
//部分结算
/**
* 部分结算的订单:
* 1. opers的金额总和 <= 订单已结算金额
* 2. opers的金额总和 + unsettled的金额 >= 退款金额
*/
let opersPrice = 0;
if (opers && opers.length > 0) {
// 对opers检查 退款0元无需检查
if (!(data.price === 0 && order.price === 0 && pay.paid === 0)) {
if (order.settled === (order.paid - order.refunded)) {
// 已全部结算
// 已全部结算的订单由refund传入的opers决定退款检查opers的金额总和等于refund的金额
let amount = 0;
// 有明确的来源account
(0, assert_1.default)(opers && opers instanceof Array, '已结算的订单退款一定要有相应的account来源');
opers.forEach(({ action, data }) => {
(0, assert_1.default)(action === 'create');
const { type, totalPlus, availPlus, accountId } = data;
@ -166,29 +131,70 @@ async function startRefunding(context, data) {
(0, assert_1.default)(totalPlus === availPlus);
const settlement = settledSettlements?.find((ele) => ele?.accountId === accountId);
(0, assert_1.default)(!!settlement, '请从订单分账的相关账户中选择订单退款的account来源');
opersPrice += -totalPlus;
amount += -totalPlus;
});
(0, assert_1.default)(amount === data.price);
//将order的settled更新
const newSettled = order.settled - data.price;
await context.operate('order', {
id: await (0, uuid_1.generateNewIdAsync)(),
action: 'update',
data: {
settled: newSettled,
},
filter: {
id: order.id,
}
}, {});
}
const { paid, refunded, settled, settlePlan$order: settlePlans } = order;
const orderUnsettledPrice = paid - refunded - settled;
if (opersPrice > settled) {
throw new Exception_1.refundOpersExceed(opersPrice - settled);
else if (order.settled === 0) {
//未结算
//未结算的订单无opers
(0, assert_1.default)(!(opers && opers.length > 0));
}
if (opersPrice < data.price - orderUnsettledPrice) {
throw new Exception_1.refundOpersNotEnough(opersPrice - (data.price - orderUnsettledPrice));
}
//将order的settled更新
const newSettled = order.settled - opersPrice;
await context.operate('order', {
id: await (0, uuid_1.generateNewIdAsync)(),
action: 'update',
data: {
settled: newSettled,
},
filter: {
id: order.id,
else {
//部分结算
/**
* 部分结算的订单:
* 1. opers的金额总和 <= 订单已结算金额
* 2. opers的金额总和 + unsettled的金额 >= 退款金额
*/
let opersPrice = 0;
if (opers && opers.length > 0) {
opers.forEach(({ action, data }) => {
(0, assert_1.default)(action === 'create');
const { type, totalPlus, availPlus, accountId } = data;
(0, assert_1.default)(type === 'refund');
(0, assert_1.default)(totalPlus === availPlus);
const settlement = settledSettlements?.find((ele) => ele?.accountId === accountId);
(0, assert_1.default)(!!settlement, '请从订单分账的相关账户中选择订单退款的account来源');
opersPrice += -totalPlus;
});
}
}, {});
const { paid, refunded, settled, settlePlan$order: settlePlans } = order;
const orderUnsettledPrice = paid - refunded - settled;
if (opersPrice > settled) {
throw new Exception_1.refundOpersExceed(opersPrice - settled);
}
if (opersPrice < data.price - orderUnsettledPrice) {
throw new Exception_1.refundOpersNotEnough(opersPrice - (data.price - orderUnsettledPrice));
}
//将order的settled更新
const newSettled = order.settled - opersPrice;
await context.operate('order', {
id: await (0, uuid_1.generateNewIdAsync)(),
action: 'update',
data: {
settled: newSettled,
},
filter: {
id: order.id,
}
}, {});
}
}
else {
(0, assert_1.default)(order.price === 0 && data.price === 0, '0元订单退款金额不可大于订单金额');
}
}
data.pay = {

View File

@ -30,6 +30,7 @@ async function startRefunding(context: BRC, data: EntityDict['refund']['CreateOp
},
order: {
id: 1,
price: 1,
paid: 1,
refunded: 1,
settled: 1,
@ -121,53 +122,14 @@ async function startRefunding(context: BRC, data: EntityDict['refund']['CreateOp
}, { forUpdate: true });
const { accountOper$entity: opers, } = data;
// 对opers检查
if (order.settled === (order.paid - order.refunded)) {
// 已全部结算
// 已全部结算的订单由refund传入的opers决定退款检查opers的金额总和等于refund的金额
let amount = 0;
// 有明确的来源account
assert(opers && opers instanceof Array, '已结算的订单退款一定要有相应的account来源');
opers.forEach(
({ action, data }) => {
assert(action === 'create');
const { type, totalPlus, availPlus, accountId } = data as EntityDict['accountOper']['CreateOperationData'];
assert(type === 'refund');
assert(totalPlus === availPlus);
const settlement = settledSettlements?.find((ele) => ele?.accountId === accountId);
assert(!!settlement, '请从订单分账的相关账户中选择订单退款的account来源');
amount += -totalPlus!;
}
);
assert(amount === data.price);
//将order的settled更新
const newSettled = order.settled - data.price!;
await context.operate('order', {
id: await generateNewIdAsync(),
action: 'update',
data: {
settled: newSettled,
},
filter: {
id: order.id,
}
}, {});
}
else if (order.settled === 0) {
//未结算
//未结算的订单无opers
assert(!(opers && opers.length > 0));
} else {
//部分结算
/**
* :
* 1. opers的金额总和 <=
* 2. opers的金额总和 + unsettled的金额 >= 退
*/
let opersPrice = 0;
if (opers && opers.length > 0) {
// 对opers检查 退款0元无需检查
if (!(data.price === 0 && order.price === 0 && pay.paid === 0)) {
if (order.settled === (order.paid - order.refunded)) {
// 已全部结算
// 已全部结算的订单由refund传入的opers决定退款检查opers的金额总和等于refund的金额
let amount = 0;
// 有明确的来源account
assert(opers && opers instanceof Array, '已结算的订单退款一定要有相应的account来源');
opers.forEach(
({ action, data }) => {
assert(action === 'create');
@ -178,32 +140,75 @@ async function startRefunding(context: BRC, data: EntityDict['refund']['CreateOp
const settlement = settledSettlements?.find((ele) => ele?.accountId === accountId);
assert(!!settlement, '请从订单分账的相关账户中选择订单退款的account来源');
opersPrice += -totalPlus!;
amount += -totalPlus!;
}
);
assert(amount === data.price);
//将order的settled更新
const newSettled = order.settled - data.price!;
await context.operate('order', {
id: await generateNewIdAsync(),
action: 'update',
data: {
settled: newSettled,
},
filter: {
id: order.id,
}
}, {});
}
const { paid, refunded, settled, settlePlan$order: settlePlans } = order;
const orderUnsettledPrice = paid - refunded - settled;
if (opersPrice > settled) {
throw new refundOpersExceed(opersPrice - settled);
}
else if (order.settled === 0) {
//未结算
//未结算的订单无opers
assert(!(opers && opers.length > 0));
} else {
//部分结算
/**
* :
* 1. opers的金额总和 <=
* 2. opers的金额总和 + unsettled的金额 >= 退
*/
let opersPrice = 0;
if (opers && opers.length > 0) {
opers.forEach(
({ action, data }) => {
assert(action === 'create');
const { type, totalPlus, availPlus, accountId } = data as EntityDict['accountOper']['CreateOperationData'];
assert(type === 'refund');
assert(totalPlus === availPlus);
if (opersPrice < data.price! - orderUnsettledPrice) {
throw new refundOpersNotEnough(opersPrice - (data.price! - orderUnsettledPrice));
}
const settlement = settledSettlements?.find((ele) => ele?.accountId === accountId);
assert(!!settlement, '请从订单分账的相关账户中选择订单退款的account来源');
//将order的settled更新
const newSettled = order.settled - opersPrice;
await context.operate('order', {
id: await generateNewIdAsync(),
action: 'update',
data: {
settled: newSettled,
},
filter: {
id: order.id,
opersPrice += -totalPlus!;
}
);
}
}, {});
const { paid, refunded, settled, settlePlan$order: settlePlans } = order;
const orderUnsettledPrice = paid - refunded - settled;
if (opersPrice > settled) {
throw new refundOpersExceed(opersPrice - settled);
}
if (opersPrice < data.price! - orderUnsettledPrice) {
throw new refundOpersNotEnough(opersPrice - (data.price! - orderUnsettledPrice));
}
//将order的settled更新
const newSettled = order.settled - opersPrice;
await context.operate('order', {
id: await generateNewIdAsync(),
action: 'update',
data: {
settled: newSettled,
},
filter: {
id: order.id,
}
}, {});
}
} else {
assert(order.price === 0 && data.price === 0, '0元订单退款金额不可大于订单金额')
}
}