UNPKG

@pisell/pisellos

Version:

一个可扩展的前端模块化SDK框架,支持插件系统

1,233 lines (1,231 loc) 47.7 kB
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") });