@pisell/pisellos
Version:
一个可扩展的前端模块化SDK框架,支持插件系统
1,233 lines (1,231 loc) • 47.7 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/modules/Payment/index.ts
var Payment_exports = {};
__export(Payment_exports, {
PaymentModule: () => PaymentModule,
generateRequestUniqueId: () => generateRequestUniqueId
});
module.exports = __toCommonJS(Payment_exports);
var import_BaseModule = require("../BaseModule");
var import_types = require("./types");
var import_utils = require("../Cart/utils");
var import_cash = require("./cash");
var import_eftpos = require("./eftpos");
var import_walletpass = require("./walletpass");
var import_decimal = require("decimal.js");
__reExport(Payment_exports, require("./types"), module.exports);
function formatAmount(amount) {
try {
const decimal = new import_decimal.Decimal(amount);
return decimal.toFixed(2);
} catch (error) {
console.warn(`[PaymentModule] 金额格式化失败: ${amount},使用默认值 0.00`);
return "0.00";
}
}
function formatDateTime(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
const hours = String(date.getHours()).padStart(2, "0");
const minutes = String(date.getMinutes()).padStart(2, "0");
const seconds = String(date.getSeconds()).padStart(2, "0");
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
function generateRequestUniqueId() {
const now = /* @__PURE__ */ new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, "0");
const day = String(now.getDate()).padStart(2, "0");
const hour = String(now.getHours()).padStart(2, "0");
const minute = String(now.getMinutes()).padStart(2, "0");
const second = String(now.getSeconds()).padStart(2, "0");
const millisecond = String(now.getMilliseconds()).padStart(3, "0");
const randomDigits = Math.floor(Math.random() * 9e3) + 1e3;
return `${year}${month}${day}${hour}${minute}${second}${millisecond}${randomDigits}`;
}
var PaymentModule = class extends import_BaseModule.BaseModule {
constructor(name, version) {
super(name || "payment", version);
this.defaultName = "pay";
this.defaultVersion = "1.0.0";
// LoggerManager 实例
this.voucherUpdateLockByOrderUuid = /* @__PURE__ */ new Map();
this.otherParams = {};
this.cash = new import_cash.CashPaymentImpl(this);
this.eftpos = new import_eftpos.EftposPaymentImpl(this);
this.wallet = new import_walletpass.WalletPassPaymentImpl(this);
}
runVoucherUpdateLocked(orderUuid, task) {
const previous = this.voucherUpdateLockByOrderUuid.get(orderUuid) || Promise.resolve();
const next = previous.catch(() => void 0).then(task).finally(() => {
if (this.voucherUpdateLockByOrderUuid.get(orderUuid) === next) {
this.voucherUpdateLockByOrderUuid.delete(orderUuid);
}
});
this.voucherUpdateLockByOrderUuid.set(orderUuid, next);
return next;
}
normalizeVoucherPaymentItems(voucherPaymentItems) {
const normalized = /* @__PURE__ */ new Map();
for (const item of voucherPaymentItems || []) {
if (!(item == null ? void 0 : item.voucher_id)) {
throw new Error(`代金券支付项缺少 voucher_id: ${JSON.stringify(item)}`);
}
const orderPaymentType = item.order_payment_type || "normal";
const key = `${item.voucher_id}|${orderPaymentType}`;
normalized.set(key, { ...item, order_payment_type: orderPaymentType });
}
return {
items: Array.from(normalized.values()),
originalCount: (voucherPaymentItems == null ? void 0 : voucherPaymentItems.length) || 0,
normalizedCount: normalized.size
};
}
async initialize(core, options) {
this.core = core;
this.store = options.store;
this.otherParams = options.otherParams || {};
this.request = core.getPlugin("request");
const appPlugin = core.getPlugin("app");
this.window = core.getPlugin("window");
if (!this.request) {
throw new Error("支付模块需要 request 插件支持");
}
if (!appPlugin) {
throw new Error("支付模块需要 app 插件支持");
}
this.app = appPlugin.getApp();
this.dbManager = this.app.dbManager;
this.logger = this.app.logger;
await this.ensurePaymentTables();
this.registerNetworkHandlers();
console.log("[PaymentModule] 初始化完成");
this.logInfo("PaymentModule initialized successfully");
}
/**
* 记录信息日志
*/
logInfo(title, metadata) {
var _a, _b;
if ((_a = this.otherParams) == null ? void 0 : _a.fatherModule) {
const fatherModule = this.core.getModule((_b = this.otherParams) == null ? void 0 : _b.fatherModule);
if (fatherModule) {
fatherModule.logInfo(`${title}`, metadata);
return;
}
}
if (this.logger) {
this.logger.addLog({
type: "info",
title: `[PaymentModule] ${title}`,
metadata: metadata || {}
});
}
}
/**
* 记录警告日志
*/
logWarning(title, metadata) {
if (this.logger) {
this.logger.addLog({
type: "warning",
title: `[PaymentModule] ${title}`,
metadata: metadata || {}
});
}
}
/**
* 记录错误日志
*/
logError(title, error, metadata) {
if (this.logger) {
this.logger.addLog({
type: "error",
title: `[PaymentModule] ${title}`,
metadata: {
error: error ? {
message: error.message,
stack: error.stack,
code: error.code,
...error
} : void 0,
...metadata || {}
}
});
}
}
/**
* 记录调试日志
*/
logDebug(title, metadata) {
if (this.logger) {
this.logger.addLog({
type: "debug",
title: `[PaymentModule] ${title}`,
metadata: metadata || {}
});
}
}
/**
* 网络恢复以后,尝试执行队列
*
*/
registerNetworkHandlers() {
const network = this.app.plugins.get("network");
this.logInfo("Registering network status listener");
network == null ? void 0 : network.addListener("networkStatusChange", async (status) => {
console.log("网络状态:", status.connected);
this.logInfo("Network status changed", {
connected: status.connected,
previousStatus: status.previousStatus
});
if (status.connected) {
this.logInfo("Network reconnected");
}
});
}
async filterPayMethods(payMethods) {
payMethods = payMethods.filter((method) => method.status === 1 && method.disable === 0);
const walletPassMethod = payMethods.find((method) => method.code === "WALLET_PASS");
if (walletPassMethod) {
payMethods.forEach((method) => {
if (method.code === "PRODUCTVOUCHER" || method.code === "GIFTCARD" || method.code === "POINTCARD") {
method.channel_application = walletPassMethod.channel_application;
}
});
} else {
payMethods = payMethods.filter((method) => method.code !== "PRODUCTVOUCHER" && method.code !== "GIFTCARD" && method.code !== "POINTCARD");
}
return payMethods;
}
/**
* 获取支付方式列表
*/
async getPayMethodListAsync() {
this.logInfo("Starting getPayMethodListAsync");
try {
let cachedMethods = [];
try {
cachedMethods = await this.dbManager.getAll("pay_method");
} catch (dbError) {
console.warn("[PaymentModule] pay_method 表不存在,将从服务器获取数据");
}
const hasCache = cachedMethods.length > 0;
if (hasCache) {
this.refreshPaymentMethodsInBackground(cachedMethods);
return cachedMethods;
}
const response = await this.request.get("/pay/custom-payment/all");
response.data = this.filterPayMethods(response.data);
const payMethods = response.data || [];
try {
for (const method of payMethods) {
await this.dbManager.update("pay_method", method);
}
} catch (dbError) {
console.warn("[PaymentModule] 无法缓存支付方式,pay_method 表不存在");
}
await this.core.effects.emit(
`${this.name}:onPaymentMethodsLoaded`,
payMethods
);
this.logInfo("getPayMethodListAsync completed successfully", {
payMethods
});
return payMethods;
} catch (error) {
console.error("[PaymentModule] 获取支付方式列表失败", error);
this.logError("getPayMethodListAsync failed", error);
return [];
}
}
/**
* 后台刷新支付方式列表
*/
async refreshPaymentMethodsInBackground(cachedMethods) {
this.logInfo("Starting refreshPaymentMethodsInBackground", {
cachedMethodsCount: cachedMethods.length
});
try {
console.log("[PaymentModule] 后台刷新支付方式列表...");
const response = await this.request.get("/pay/custom-payment/all");
response.data = this.filterPayMethods(response.data);
const newPayMethods = response.data || [];
const hasChanges = this.hasPaymentMethodsChanged(
cachedMethods,
newPayMethods
);
if (hasChanges) {
console.log("[PaymentModule] 支付方式列表已更新");
this.logInfo("Payment methods updated in background", {
oldCount: cachedMethods.length,
newCount: newPayMethods.length
});
try {
for (const method of cachedMethods) {
await this.dbManager.delete("pay_method", method.id);
}
for (const method of newPayMethods) {
await this.dbManager.update("pay_method", method);
}
} catch (dbError) {
console.warn("[PaymentModule] 无法更新支付方式缓存", dbError);
}
const eventData = {
oldMethods: cachedMethods,
newMethods: newPayMethods
};
await this.core.effects.emit(
`${this.name}:onPaymentMethodsChanged`,
eventData
);
} else {
console.log("[PaymentModule] 支付方式列表无变化");
}
} catch (error) {
console.error("[PaymentModule] 后台刷新支付方式失败", error);
}
}
/**
* 检查支付方式列表是否有变化
*/
hasPaymentMethodsChanged(oldMethods, newMethods) {
if (oldMethods.length !== newMethods.length) {
return true;
}
const oldMethodsMap = new Map(
oldMethods.map((method) => [method.id, method])
);
for (const newMethod of newMethods) {
const oldMethod = oldMethodsMap.get(newMethod.id);
if (!oldMethod) {
return true;
}
if (oldMethod.code !== newMethod.code || oldMethod.name !== newMethod.name || oldMethod.type !== newMethod.type || oldMethod.description !== newMethod.description || oldMethod.status !== newMethod.status || oldMethod.disable !== newMethod.disable || oldMethod.is_surcharge !== newMethod.is_surcharge || oldMethod.fixed !== newMethod.fixed || oldMethod.percentage !== newMethod.percentage || oldMethod.enabled !== newMethod.enabled || JSON.stringify(oldMethod.channel_application || []) !== JSON.stringify(newMethod.channel_application || []) || JSON.stringify(oldMethod.companies || []) !== JSON.stringify(newMethod.companies || []) || JSON.stringify(oldMethod.metadata || {}) !== JSON.stringify(newMethod.metadata || {})) {
console.log(`[PaymentModule] 支付方式 ${newMethod.id} (${newMethod.code}) 发生变化:`, {
id: newMethod.id,
changes: {
code: oldMethod.code !== newMethod.code ? { old: oldMethod.code, new: newMethod.code } : void 0,
name: oldMethod.name !== newMethod.name ? { old: oldMethod.name, new: newMethod.name } : void 0,
type: oldMethod.type !== newMethod.type ? { old: oldMethod.type, new: newMethod.type } : void 0,
description: oldMethod.description !== newMethod.description ? { old: oldMethod.description, new: newMethod.description } : void 0,
status: oldMethod.status !== newMethod.status ? { old: oldMethod.status, new: newMethod.status } : void 0,
disable: oldMethod.disable !== newMethod.disable ? { old: oldMethod.disable, new: newMethod.disable } : void 0,
is_surcharge: oldMethod.is_surcharge !== newMethod.is_surcharge ? { old: oldMethod.is_surcharge, new: newMethod.is_surcharge } : void 0,
fixed: oldMethod.fixed !== newMethod.fixed ? { old: oldMethod.fixed, new: newMethod.fixed } : void 0,
percentage: oldMethod.percentage !== newMethod.percentage ? { old: oldMethod.percentage, new: newMethod.percentage } : void 0,
enabled: oldMethod.enabled !== newMethod.enabled ? { old: oldMethod.enabled, new: newMethod.enabled } : void 0,
channel_application: JSON.stringify(oldMethod.channel_application || []) !== JSON.stringify(newMethod.channel_application || []) ? { old: oldMethod.channel_application, new: newMethod.channel_application } : void 0,
companies: JSON.stringify(oldMethod.companies || []) !== JSON.stringify(newMethod.companies || []) ? { old: oldMethod.companies, new: newMethod.companies } : void 0,
metadata: JSON.stringify(oldMethod.metadata || {}) !== JSON.stringify(newMethod.metadata || {}) ? { old: oldMethod.metadata, new: newMethod.metadata } : void 0
}
});
return true;
}
}
return false;
}
/**
* 获取订单列表
*/
async getOrderListAsync() {
try {
return await this.dbManager.getAll("order");
} catch (error) {
console.error("[PaymentModule] 获取订单列表失败", error);
return [];
}
}
/**
* 根据订单UUID获取支付订单(新方法)
*/
async getPaymentOrderByUuidAsync(orderUuid) {
try {
return await this.dbManager.get("order", orderUuid, true);
} catch (error) {
console.error("[PaymentModule] 获取支付订单失败", error);
return null;
}
}
/**
* 创建支付订单(新方法,专注支付数据)
*/
async createPaymentOrderAsync(params) {
this.logInfo("Starting createPaymentOrderAsync", {
orderId: params.order_id,
totalAmount: params.total_amount
});
try {
const newOrder = {
uuid: (0, import_utils.getUniqueId)("pay_order_"),
id: params.order_id,
order_id: params.order_id,
order_info: params.order_info,
payment_status: import_types.PaymentStatus.Processing,
payment: [],
adjust_offline_payments: [],
total_amount: params.total_amount,
expect_amount: params.total_amount,
tax_fee: "0.00",
is_deposit: params.is_deposit || 0,
deposit_amount: params.deposit_amount || "0.00"
};
if (params.existPayment && params.existPayment.length > 0) {
const currentTime = formatDateTime(/* @__PURE__ */ new Date());
newOrder.payment = params.existPayment.map((payment) => ({
...payment,
isSynced: true,
// 标记为已同步
status: payment.status || "active",
// 如果云端支付项已有时间戳,则保留;否则使用当前时间
created_at: payment.created_at || currentTime,
updated_at: payment.updated_at || currentTime
}));
this.recalculateOrderAmount(newOrder);
this.logInfo("创建订单时包含云端支付项", {
orderId: params.order_id,
existPaymentCount: params.existPayment.length,
totalAmount: newOrder.total_amount,
expectAmount: newOrder.expect_amount,
paymentDetails: params.existPayment.map((p) => ({
uuid: p.uuid,
code: p.code,
amount: p.amount,
status: p.status,
created_at: p.created_at,
updated_at: p.updated_at
}))
});
}
const dbAddStartTime = Date.now();
await this.dbManager.add("order", newOrder, true);
const dbAddDuration = Date.now() - dbAddStartTime;
this.logInfo("Database add operation completed", {
operation: "dbManager.add",
table: "order",
orderUuid: newOrder.uuid,
orderId: newOrder.order_id,
duration: `${dbAddDuration}ms`,
performance: dbAddDuration > 100 ? "slow" : dbAddDuration > 50 ? "medium" : "fast"
});
setTimeout(() => {
this.core.effects.emit(`${this.name}:onOrderAdded`, newOrder);
}, 0);
return newOrder;
} catch (error) {
console.error("[PaymentModule] 创建支付订单失败", error);
this.logError("createPaymentOrderAsync failed", error, {
orderId: params.order_id
});
throw error;
}
}
/**
* 删除支付订单(新方法)
*/
async deletePaymentOrderAsync(orderUuid) {
try {
const order = await this.dbManager.get("order", orderUuid);
if (order) {
await this.dbManager.delete("order", orderUuid);
await this.core.effects.emit(`${this.name}:onOrderDeleted`, order);
console.log("[PaymentModule] 支付订单删除成功:", orderUuid);
}
} catch (error) {
console.error("[PaymentModule] 删除支付订单失败", error);
throw error;
}
}
/**
* 更新订单
*/
async updateOrderAsync(orderUuid, params) {
try {
const order = await this.dbManager.get("order", orderUuid);
if (order) {
const updatedOrder = { ...order, ...params };
this.recalculateOrderAmount(updatedOrder);
await this.dbManager.update("order", updatedOrder);
await this.core.effects.emit(`${this.name}:onOrderUpdated`, updatedOrder);
await this.core.effects.emit(`${this.name}:onOrderChanged`, {
action: "update",
order: updatedOrder,
originalOrder: order
});
}
} catch (error) {
console.error("[PaymentModule] 更新订单失败", error);
throw error;
}
}
/**
* 基于UUID替换订单ID
*
* 此方法用于将本地虚拟订单ID替换为真实的订单ID。
* 当前端模拟下单流程完成后,后端返回真实订单ID时调用此方法。
*
* @param orderUuid 订单的UUID
* @param newOrderId 新的订单ID (来自后端)
* @returns 更新后的订单对象,如果订单不存在则返回null
*/
async replaceOrderIdByUuidAsync(orderUuid, newOrderId) {
this.logInfo("Starting replaceOrderIdByUuidAsync", {
orderUuid,
newOrderId
});
try {
const existingOrder = await this.dbManager.get("order", orderUuid);
if (!existingOrder) {
this.logWarning("Order not found for UUID replacement", { orderUuid });
return null;
}
const allOrders = await this.dbManager.getAll("order");
const duplicateOrder = allOrders.find(
(order) => String(order.order_id) === String(newOrderId) && order.uuid !== orderUuid
);
if (duplicateOrder) {
this.logWarning("New order ID already exists", {
newOrderId,
existingOrderUuid: duplicateOrder.uuid
});
throw new Error(`订单ID ${newOrderId} 已存在`);
}
const originalOrderId = existingOrder.id;
const updatedOrder = {
...existingOrder,
id: newOrderId,
order_id: newOrderId,
// 🔧 修复:同时更新根级的 order_id 字段
order_info: {
...existingOrder.order_info,
order_id: newOrderId,
// 保留原始本地订单ID作为参考
original_local_order_id: originalOrderId,
updated_at: (/* @__PURE__ */ new Date()).toISOString()
}
};
await this.dbManager.update("order", updatedOrder);
await this.core.effects.emit(`${this.name}:onOrderUpdated`, updatedOrder);
await this.core.effects.emit(`${this.name}:onOrderChanged`, {
action: "order_id_replaced",
order: updatedOrder,
originalOrder: existingOrder,
metadata: {
originalOrderId,
newOrderId
}
});
this.logInfo("Order ID replacement completed successfully", {
orderUuid,
originalOrderId,
newOrderId
});
console.log(`[PaymentModule] 订单ID替换成功: ${originalOrderId} → ${newOrderId}`);
return updatedOrder;
} catch (error) {
console.error("[PaymentModule] 替换订单ID失败", error);
this.logError("replaceOrderIdByUuidAsync failed", error, {
orderUuid,
newOrderId
});
throw error;
}
}
/**
* 获取支付项(新方法)
*
* @param orderUuid 订单UUID
* @param includeVoided 是否包含已撤销的支付项,默认为false
* @returns 支付项数组
*/
async getPaymentItemsAsync(orderUuid, includeVoided = false) {
if (!orderUuid) {
throw new Error("orderUuid is required");
}
const order = await this.getPaymentOrderByUuidAsync(orderUuid);
const allPayments = (order == null ? void 0 : order.payment) || [];
if (includeVoided) {
return allPayments;
}
return allPayments.filter((payment) => payment.status !== "voided");
}
/**
* 获取所有支付项(包括已撤销的)
*
* @param orderUuid 订单UUID
* @returns 所有支付项数组(包括撤销的)
*/
async getAllPaymentItemsAsync(orderUuid) {
return this.getPaymentItemsAsync(orderUuid, true);
}
/**
* 为某个订单添加支付项(新方法)
*/
async addPaymentItemAsync(orderUuid, paymentItem) {
var _a, _b, _c, _d;
this.logInfo("Starting addPaymentItemAsync", {
orderUuid,
paymentAmount: paymentItem.amount,
paymentCode: paymentItem.code,
orderPaymentType: paymentItem.order_payment_type
});
try {
this.logInfo("准备获取订单", {
orderUuid
});
const order = await this.getPaymentOrderByUuidAsync(orderUuid);
this.logInfo("获取订单信息成功", {
orderUuid,
order
});
if (!order) {
throw new Error(`Order not found: ${orderUuid}`);
}
const expectAmount = new import_decimal.Decimal(order.expect_amount);
const paidDepositAmount = order.payment.reduce((sum, payment) => {
if (payment.order_payment_type === "deposit" && payment.status !== "voided") {
return sum.plus(new import_decimal.Decimal(payment.amount));
}
return sum;
}, new import_decimal.Decimal(0));
const expectedDepositAmount = new import_decimal.Decimal(order.deposit_amount || "0").sub(paidDepositAmount);
if (expectAmount.lte(0) && expectedDepositAmount.eq(0)) {
const warningMessage = `订单 ${orderUuid} 待付金额已为0,不允许添加新的支付项`;
console.warn("[PaymentModule] Payment lock triggered:", {
orderUuid,
expectAmount: order.expect_amount,
attemptedPaymentAmount: paymentItem.amount,
attemptedPaymentCode: paymentItem.code,
reason: "Order already fully paid"
});
this.logError("addPaymentItemAsync blocked by payment lock", new Error(warningMessage), {
orderUuid,
expectAmount: order.expect_amount,
paymentItem
});
throw new Error(warningMessage);
}
let paymentUuid;
if ((_a = paymentItem.metadata) == null ? void 0 : _a.unique_payment_number) {
paymentUuid = paymentItem.metadata.unique_payment_number;
} else {
paymentUuid = (0, import_utils.getUniqueId)("payment_");
}
const currentTime = formatDateTime(/* @__PURE__ */ new Date());
const newPaymentItem = {
uuid: paymentUuid,
custom_payment_id: paymentItem.custom_payment_id || 0,
name: paymentItem.name,
code: paymentItem.code,
type: paymentItem.type,
amount: formatAmount(paymentItem.amount),
voucher_id: paymentItem.voucher_id || "",
rounding_amount: paymentItem.rounding_amount || "0.00",
service_charge: paymentItem.service_charge,
status: "active",
// 新支付项默认为活跃状态
order_payment_type: paymentItem.order_payment_type || "normal",
// 默认为正常支付
created_at: currentTime,
// 创建时间
updated_at: currentTime,
// 更新时间
wallet_pass_usage_unit: paymentItem == null ? void 0 : paymentItem.wallet_pass_usage_unit,
wallet_pass_use_value: paymentItem == null ? void 0 : paymentItem.wallet_pass_use_value,
metadata: {
...paymentItem.metadata,
// 保留传入的所有 metadata 字段
unique_payment_number: paymentUuid
// 设置唯一支付号为支付项的 uuid
}
};
order.payment.push(newPaymentItem);
this.recalculateOrderAmount(order);
this.logInfo("开始更新订单支付项", {
orderUuid,
order
});
await this.dbManager.update("order", order, true);
this.logInfo("更新订单支付项完成", {
orderUuid
});
this.core.effects.emit(`${this.name}:onPaymentAdded`, {
orderUuid,
payment: newPaymentItem
});
this.logInfo("addPaymentItemAsync completed successfully", {
orderUuid,
paymentUuid: newPaymentItem.uuid,
uniquePaymentNumber: (_b = newPaymentItem.metadata) == null ? void 0 : _b.unique_payment_number,
newExpectAmount: order.expect_amount,
paymentAmount: newPaymentItem.amount,
paymentCode: newPaymentItem.code,
orderPaymentType: newPaymentItem.order_payment_type,
metadataFields: Object.keys(newPaymentItem.metadata || {}),
// 现金支付找零信息
actualPaidAmount: (_c = newPaymentItem.metadata) == null ? void 0 : _c.actual_paid_amount,
changeGivenAmount: (_d = newPaymentItem.metadata) == null ? void 0 : _d.change_given_amount
});
} catch (error) {
console.error("[PaymentModule] 添加支付项失败", error);
this.logError("addPaymentItemAsync failed", error, {
orderUuid,
paymentItem
});
throw error;
}
}
/**
* 删除一个支付项 - 标记删除而非物理删除
*/
async deletePaymentAsync(orderUuid, paymentUuid) {
this.logInfo("Starting deletePaymentAsync (mark as voided)", { orderUuid, paymentUuid });
try {
const order = await this.dbManager.get("order", orderUuid);
if (!order) {
throw new Error("订单不存在");
}
const paymentItem = order.payment.find(
(payment) => payment.uuid === paymentUuid
);
if (!paymentItem) {
throw new Error(`支付项不存在: ${paymentUuid}`);
}
if (paymentItem.status === "voided") {
console.warn(`[PaymentModule] 支付项 ${paymentUuid} 已经被删除,跳过操作`);
return;
}
paymentItem.status = "voided";
paymentItem.origin_amount = paymentItem.amount;
paymentItem.amount = "0.00";
console.log(`[PaymentModule] 支付项标记为删除:`, {
uuid: paymentItem.uuid,
originalAmount: paymentItem.origin_amount,
status: paymentItem.status
});
this.recalculateOrderAmount(order);
await this.dbManager.update("order", order);
await this.core.effects.emit(`${this.name}:onPaymentDeleted`, {
order,
paymentItem
});
await this.core.effects.emit(`${this.name}:onOrderChanged`, {
action: "payment_delete",
order,
paymentItem
});
this.logInfo("deletePaymentAsync completed successfully (marked as voided)", {
orderUuid,
paymentUuid,
originalAmount: paymentItem.origin_amount,
newExpectAmount: order.expect_amount
});
} catch (error) {
console.error("[PaymentModule] 删除支付项失败", error);
this.logError("deletePaymentAsync failed", error, {
orderUuid,
paymentUuid
});
throw error;
}
}
/**
* 批量更新代金券类支付项(覆盖更新)
*
* 删除所有现有的带 voucher_id 的支付项,然后添加新的代金券支付项
* 这是一个覆盖式更新,确保代金券支付项的一致性
*/
async updateVoucherPaymentItemsAsync(orderUuid, voucherPaymentItems) {
return this.runVoucherUpdateLocked(orderUuid, async () => {
const { items: normalizedVoucherItems, originalCount, normalizedCount } = this.normalizeVoucherPaymentItems(voucherPaymentItems);
this.logInfo("Starting updateVoucherPaymentItemsAsync", {
orderUuid,
originalVoucherCount: originalCount,
normalizedVoucherCount: normalizedCount,
voucherItems: normalizedVoucherItems.map((item) => ({
code: item.code,
amount: item.amount,
voucher_id: item.voucher_id,
order_payment_type: item.order_payment_type
}))
});
if (originalCount !== normalizedCount) {
console.warn("[PaymentModule] voucherPaymentItems detected duplicates (deduped by voucher_id + order_payment_type)", {
orderUuid,
originalCount,
normalizedCount
});
}
try {
const order = await this.getPaymentOrderByUuidAsync(orderUuid);
if (!order) {
throw new Error(`订单不存在: ${orderUuid}`);
}
const existingVoucherItems = order.payment.filter(
(payment) => payment.voucher_id && payment.status !== "voided" && !payment.isSynced
);
console.log("[PaymentModule] 发现现有代金券支付项:", {
orderUuid,
existingVoucherCount: existingVoucherItems.length,
existingItems: existingVoucherItems.map((item) => ({
uuid: item.uuid,
code: item.code,
amount: item.amount,
voucher_id: item.voucher_id,
isSynced: item.isSynced
}))
});
for (const voucherItem of existingVoucherItems) {
console.log(`[PaymentModule] 删除现有代金券支付项: ${voucherItem.uuid}`);
await this.deletePaymentAsync(orderUuid, voucherItem.uuid);
}
const orderAfterDelete = await this.getPaymentOrderByUuidAsync(orderUuid);
if (!orderAfterDelete) {
throw new Error(`订单不存在: ${orderUuid}`);
}
const existingActiveVoucherKeys = new Set(
orderAfterDelete.payment.filter((p) => p.voucher_id && p.status !== "voided" && !p.isSynced).map((p) => `${p.voucher_id}|${p.order_payment_type || "normal"}`)
);
console.log("[PaymentModule] 添加新的代金券支付项:", {
orderUuid,
newItemCount: normalizedVoucherItems.length
});
for (const voucherItem of normalizedVoucherItems) {
const orderPaymentType = voucherItem.order_payment_type || "normal";
const key = `${voucherItem.voucher_id}|${orderPaymentType}`;
if (existingActiveVoucherKeys.has(key)) {
console.warn("[PaymentModule] Skip adding voucher payment item because it already exists (active)", {
orderUuid,
voucher_id: voucherItem.voucher_id,
order_payment_type: orderPaymentType
});
continue;
}
console.log(`[PaymentModule] 添加代金券支付项:`, {
code: voucherItem.code,
amount: voucherItem.amount,
voucher_id: voucherItem.voucher_id,
order_payment_type: orderPaymentType
});
await this.addPaymentItemAsync(orderUuid, voucherItem);
existingActiveVoucherKeys.add(key);
}
const updatedOrder = await this.getPaymentOrderByUuidAsync(orderUuid);
await this.core.effects.emit(`${this.name}:onPaymentAdded`, {
orderUuid,
order: updatedOrder,
payment: null
// 批量操作不提供单个支付项
});
this.logInfo("updateVoucherPaymentItemsAsync completed successfully", {
orderUuid,
removedVoucherCount: existingVoucherItems.length,
addedVoucherCount: normalizedVoucherItems.length,
finalExpectAmount: updatedOrder == null ? void 0 : updatedOrder.expect_amount
});
} catch (error) {
console.error("[PaymentModule] 批量更新代金券支付项失败:", error);
this.logError("updateVoucherPaymentItemsAsync failed", error, {
orderUuid,
voucherPaymentItems
});
throw error;
}
});
}
/**
* 更新一个支付项
*/
async updatePaymentAsync(orderUuid, paymentUuid, params) {
try {
const order = await this.dbManager.get("order", orderUuid);
if (!order) {
throw new Error("订单不存在");
}
const paymentItem = order.payment.find(
(payment) => payment.uuid === paymentUuid
);
if (paymentItem) {
const formattedParams = { ...params };
if ("amount" in formattedParams && formattedParams.amount !== void 0) {
formattedParams.amount = formatAmount(formattedParams.amount);
}
Object.assign(paymentItem, formattedParams);
this.recalculateOrderAmount(order);
await this.dbManager.update("order", order);
await this.core.effects.emit(`${this.name}:onPaymentUpdated`, {
order,
paymentItem
});
await this.core.effects.emit(`${this.name}:onOrderChanged`, {
action: "payment_update",
order,
paymentItem
});
}
} catch (error) {
console.error("[PaymentModule] 更新支付项失败", error);
throw error;
}
}
/**
* 提交支付
*/
async submitPayAsync(orderUuid) {
this.logInfo("Starting submitPayAsync", { orderUuid });
try {
let orderToSubmit;
if (orderUuid) {
const order = await this.dbManager.get("order", orderUuid);
orderToSubmit = order ? [order] : [];
} else {
const allOrders = await this.dbManager.getAll("order");
orderToSubmit = allOrders.filter(
(order) => order.payment_status === import_types.PaymentStatus.Processing || order.payment_status === import_types.PaymentStatus.PartiallyPaid
);
}
for (const order of orderToSubmit) {
await this.submitSingleOrderPayment(order);
}
const result = { status: "success" };
this.logInfo("submitPayAsync completed", {
result: result.status,
processedOrdersCount: orderToSubmit.length
});
return result;
} catch (error) {
console.error("[PaymentModule] 提交支付失败", error);
this.logError("submitPayAsync failed", error, { orderUuid });
return { status: "failed" };
}
}
/**
* 提交单个订单的支付(推送到任务队列)
*/
async submitSingleOrderPayment(order) {
try {
const paymentData = {
payments: order.payment.map((payment) => ({
uuid: payment.uuid,
amount: payment.amount,
code: payment.code,
custom_payment_id: payment.custom_payment_id,
name: payment.name,
type: payment.type,
voucher_id: payment.voucher_id,
metadata: payment.metadata,
rounding_amount: payment.rounding_amount,
service_charge: payment.service_charge
})),
payment_status: "pending"
};
const totalPaidAmount = paymentData.payments.reduce(
(sum, payment) => sum.plus(payment.amount),
new import_decimal.Decimal(0)
);
const orderTotalAmount = new import_decimal.Decimal(order.total_amount);
if (totalPaidAmount.gte(orderTotalAmount)) {
order.payment_status = import_types.PaymentStatus.Finished;
console.log(
`[PaymentModule] 订单 ${order.uuid} 支付完成,金额: ${totalPaidAmount.toString()}/${orderTotalAmount.toString()}`
);
} else {
order.payment_status = import_types.PaymentStatus.PartiallyPaid;
console.log(
`[PaymentModule] 订单 ${order.uuid} 部分支付,金额: ${totalPaidAmount.toString()}/${orderTotalAmount.toString()}`
);
}
paymentData.payment_status = order.payment_status === import_types.PaymentStatus.Finished ? "paid" : "partially_paid";
await this.dbManager.update("order", order);
if (paymentData.payments.length === 0) {
console.log(`[PaymentModule] 订单 ${order.uuid} 支付列表为空,跳过支付处理`);
this.logWarning("Empty payment list", {
orderUuid: order.uuid,
orderId: order.order_id
});
return;
}
await this.core.effects.emit(`${this.name}:onPaymentSubmitted`, order);
await this.core.effects.emit(`${this.name}:onOrderChanged`, {
action: "submit",
order
});
} catch (error) {
console.error(`[PaymentModule] 订单 ${order.uuid} 支付提交失败`, error);
throw error;
}
}
/**
* 获取订单剩余待付金额
*/
async getRemainingOrderAmountAsync(orderUuid) {
const order = await this.getPaymentOrderByUuidAsync(orderUuid);
if (!order) {
throw new Error("订单不存在");
}
return new import_decimal.Decimal(order.expect_amount).toNumber();
}
/**
* 在比如现金支付界面的地方,用户输入了一个金额,在下方显示剩余多少金额,通过此方法获取
*/
async getRemainingOrderAmountWithInputAsync(inputAmount, orderUuid) {
const order = await this.getPaymentOrderByUuidAsync(orderUuid);
if (!order) {
throw new Error("订单不存在");
}
if (inputAmount === null || inputAmount === void 0) {
return new import_decimal.Decimal(order.expect_amount).toNumber();
}
if (typeof inputAmount === "string") {
if (inputAmount.trim() === "") {
return new import_decimal.Decimal(order.expect_amount).toNumber();
}
}
if (typeof inputAmount === "number") {
if (isNaN(inputAmount) || !isFinite(inputAmount)) {
console.warn(
`[PaymentModule] 输入金额不是有效数字: ${inputAmount},返回原始待付金额`
);
return new import_decimal.Decimal(order.expect_amount).toNumber();
}
}
try {
const inputDecimal = new import_decimal.Decimal(inputAmount);
return new import_decimal.Decimal(order.expect_amount).minus(inputDecimal).toNumber();
} catch (error) {
console.warn(
`[PaymentModule] 输入金额格式无效: ${inputAmount},返回原始待付金额`
);
return new import_decimal.Decimal(order.expect_amount).toNumber();
}
}
/**
* 重新计算订单金额
*/
recalculateOrderAmount(order) {
const totalAmount = new import_decimal.Decimal(order.total_amount);
const paidAmount = order.payment.reduce(
(sum, payment) => {
try {
if (payment.status === "voided") {
return sum;
}
const paymentAmount = new import_decimal.Decimal(payment.amount || 0);
const roundingAmount = new import_decimal.Decimal(payment.rounding_amount || 0);
const effectiveAmount = paymentAmount.minus(roundingAmount);
return sum.plus(effectiveAmount);
} catch (error) {
console.warn(`[PaymentModule] 无效的支付金额: amount=${payment.amount}, rounding_amount=${payment.rounding_amount},跳过计算`);
return sum;
}
},
new import_decimal.Decimal(0)
);
const remainingAmount = order.is_deposit === 1 ? new import_decimal.Decimal(order.deposit_amount).minus(paidAmount) : totalAmount.minus(paidAmount);
order.expect_amount = import_decimal.Decimal.max(0, remainingAmount).toFixed(2);
console.log(`[PaymentModule] 重新计算订单金额:`, {
orderUuid: order.uuid,
totalAmount: order.total_amount,
effectivePaidAmount: paidAmount.toFixed(2),
remainingAmount: order.expect_amount,
activePayments: order.payment.filter((p) => p.status !== "voided").length,
voidedPayments: order.payment.filter((p) => p.status === "voided").length,
paymentDetails: order.payment.filter((p) => p.status !== "voided").map((p) => ({
code: p.code,
amount: p.amount,
rounding_amount: p.rounding_amount || "0.00",
effective_amount: new import_decimal.Decimal(p.amount || 0).minus(new import_decimal.Decimal(p.rounding_amount || 0)).toFixed(2)
})),
说明: "有效支付金额包含抹零计算(amount + |rounding_amount|)"
});
}
/**
* 获取现金支付方式
*/
async getCashPaymentMethod() {
try {
const payMethods = await this.dbManager.getAll("pay_method");
return payMethods.find(
(method) => method.code === import_types.PaymentMethodType.Cash
) || null;
} catch (error) {
console.error("[PaymentModule] 获取现金支付方式失败", error);
return null;
}
}
/**
* 获取Eftpos支付方式
*/
async getEftposPaymentMethod() {
try {
const payMethods = await this.dbManager.getAll("pay_method");
return payMethods.find(
(method) => method.code === import_types.PaymentMethodType.Eftpos
) || null;
} catch (error) {
console.error("[PaymentModule] 获取Eftpos支付方式失败", error);
return null;
}
}
/**
* 获取钱包支付方式
*/
async getWalletPaymentMethod() {
try {
const payMethods = await this.dbManager.getAll("pay_method");
return payMethods.find(
(method) => method.code === import_types.PaymentMethodType.Wallet
) || null;
} catch (error) {
console.error("[PaymentModule] 获取钱包支付方式失败", error);
return null;
}
}
/**
* 确保支付模块所需的数据库表已创建
*/
async ensurePaymentTables() {
try {
await this.dbManager.getAll("pay_method");
} catch (error) {
console.warn(
"[PaymentModule] pay_method 表不存在,请在数据库配置中添加以下配置:"
);
console.warn('{ name: "pay_method", keyPath: "id" }');
}
try {
await this.dbManager.getAll("order");
} catch (error) {
console.warn(
"[PaymentModule] order 表不存在,请在数据库配置中添加以下配置:"
);
console.warn('{ name: "order", keyPath: "uuid" }');
}
}
/**
* 获取部分支付的订单
*/
async getPartiallyPaidOrdersAsync() {
try {
const allOrders = await this.dbManager.getAll("order");
return allOrders.filter(
(order) => order.payment_status === import_types.PaymentStatus.PartiallyPaid
);
} catch (error) {
console.error("[PaymentModule] 获取部分支付订单失败", error);
return [];
}
}
// === 新的 API 方法实现 ===
/**
* 智能金额舍入
*
* 根据指定的舍入间隔和规则对金额进行舍入处理
*
* @param originalAmount 原始金额
* @param interval 舍入间隔 (0.05, 0.1, 0.5, 1)
* @param rule 舍入规则 (standard, standard_down, always_up, always_down)
* @returns 舍入结果详情(包含原始金额、舍入后金额和舍入差额)
*/
async roundAmountAsync(originalAmount, interval, rule) {
try {
const amount = new import_decimal.Decimal(originalAmount);
const roundingInterval = new import_decimal.Decimal(interval);
if (roundingInterval.lte(0)) {
throw new Error("舍入间隔必须大于 0");
}
const supportedIntervals = [0.05, 0.1, 0.5, 1];
const intervalValue = roundingInterval.toNumber();
if (!supportedIntervals.includes(intervalValue)) {
console.warn(`[PaymentModule] 不支持的舍入间隔: ${intervalValue}, 支持的间隔: ${supportedIntervals.join(", ")}`);
}
const baseValue = amount.div(roundingInterval);
console.log(`[PaymentModule] 舍入计算 - 原始金额: ${amount.toString()}, 间隔: ${intervalValue}, 基础值: ${baseValue.toString()}, 规则: ${rule}`);
let roundedValue;
switch (rule) {
case import_types.RoundingRule.Standard:
case "standard":
roundedValue = this.standardRound(baseValue, true);
break;
case import_types.RoundingRule.StandardDown:
case "standard_down":
roundedValue = this.standardRound(baseValue, false);
break;
case import_types.RoundingRule.AlwaysUp:
case "always_up":
roundedValue = baseValue.ceil();
break;
case import_types.RoundingRule.AlwaysDown:
case "always_down":
roundedValue = baseValue.floor();
break;
default:
throw new Error(`不支持的舍入规则: ${rule}`);
}
const finalAmount = roundedValue.mul(roundingInterval);
const originalAmountStr = amount.toFixed(2);
if (finalAmount.eq(0)) {
console.log(`[PaymentModule] 最终金额为0,返回原始金额: ${originalAmountStr}`);
return {
originalAmount: originalAmountStr,
roundedAmount: originalAmountStr,
roundingDifference: "0.00"
};
}
const roundedAmountStr = finalAmount.toFixed(2);
const roundingDifference = finalAmount.sub(amount);
const roundingDifferenceStr = roundingDifference.toFixed(2);
console.log(`[PaymentModule] 舍入结果 - 原始金额: ${originalAmountStr}, 舍入后金额: ${roundedAmountStr}, 舍入差额: ${roundingDifferenceStr}`);
return {
originalAmount: originalAmountStr,
roundedAmount: roundedAmountStr,
roundingDifference: roundingDifferenceStr
};
} catch (error) {
console.error("[PaymentModule] 金额舍入失败:", error);
throw new Error(`金额舍入失败: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* 标准舍入处理(处理中点情况)
*
* @param value 要舍入的值
* @param midpointUp 中点是否向上舍入
* @returns 舍入后的值
*/
standardRound(value, midpointUp) {
const floorValue = value.floor();
const fractionalPart = value.minus(floorValue);
if (fractionalPart.eq(0.5)) {
return midpointUp ? floorValue.plus(1) : floorValue;
} else if (fractionalPart.gt(0.5)) {
return floorValue.plus(1);
} else {
return floorValue;
}
}
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
PaymentModule,
generateRequestUniqueId,
...require("./types")
});