diff --git a/esf-relay/src/server.mjs b/esf-relay/src/server.mjs index 119c6b4..cba117f 100644 --- a/esf-relay/src/server.mjs +++ b/esf-relay/src/server.mjs @@ -6,6 +6,7 @@ const suppliedServiceBaseUrl = String(process.env.SUPPLIED_SERVICE_BASE_URL || ' const addressRoutes = new Map([ ['/supplierOpenApi/pointOrder/queryOrderInfo', '/supplierOpenApi/pointOrder/queryOrderInfo'], ['/supplierOpenApi/pointOrder/queryReconciliationInfo', '/supplierOpenApi/pointOrder/queryReconciliationInfo'], + ['/supplierOpenApi/pointOrder/applyForInvoices', '/supplierOpenApi/pointOrder/applyForInvoices'], ['/supplierOpenApi/pointOrder/unReconciledMsg', '/supplierOpenApi/pointOrder/unReconciledMsg'], ['/supplierOpenApi/pointOrder/reconciliationResult', '/supplierOpenApi/pointOrder/reconciliationResult'] ]) diff --git a/supplied-h5/src/store/user.js b/supplied-h5/src/store/user.js index 6fb1f66..60af0f4 100644 --- a/supplied-h5/src/store/user.js +++ b/supplied-h5/src/store/user.js @@ -20,6 +20,7 @@ const saveUser = data => { export const useUserStore = defineStore('joyfulSelectionUser', { state: () => ({ userId: '', + encryptedParam: '', supplierIdentification: '', supplierId: '', supplierName: '', @@ -58,10 +59,11 @@ export const useUserStore = defineStore('joyfulSelectionUser', { this.loadUser() } - const nextUserId = query.userId || '' + const encryptedParam = query.AplParmObjct || query.aplParmObjct || '' - if (nextUserId && nextUserId !== this.userId) { - this.userId = nextUserId + if (encryptedParam) { + this.encryptedParam = encryptedParam + this.userId = '' this.pointsBalance = 0 } if (!this.supplierIdentification) { @@ -90,7 +92,10 @@ export const useUserStore = defineStore('joyfulSelectionUser', { if (!this.loaded) { this.loadUser() } - if (!this.userId || !this.supplierIdentification || this.hasToken) { + if (!this.supplierIdentification || (!this.encryptedParam && this.hasToken)) { + return + } + if (!this.encryptedParam) { return } if (this.loadingToken && bridgeTokenPromise) { @@ -104,10 +109,12 @@ export const useUserStore = defineStore('joyfulSelectionUser', { } const data = await postFunction('supplier-token', { - userId: this.userId, + AplParmObjct: this.encryptedParam, supplierIdentification: this.supplierIdentification }) + this.userId = data?.userId || '' + this.encryptedParam = '' this.supplierId = data?.supplierId || this.supplierId this.supplierName = data?.supplierName || this.supplierName this.persistUser() diff --git a/supplied-service/prisma/script/supplier_db.sql b/supplied-service/prisma/script/supplier_db.sql new file mode 100644 index 0000000..90e6981 --- /dev/null +++ b/supplied-service/prisma/script/supplier_db.sql @@ -0,0 +1,551 @@ +/* + Navicat Premium Dump SQL + + Source Server : localhost + Source Server Type : MySQL + Source Server Version : 80046 (8.0.46) + Source Host : 127.0.0.1:10086 + Source Schema : supplier_db + + Target Server Type : MySQL + Target Server Version : 80046 (8.0.46) + File Encoding : 65001 + + Date: 02/09/2026 11:08:42 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for cartitem +-- ---------------------------- +DROP TABLE IF EXISTS `cartitem`; +CREATE TABLE `cartitem` ( + `id` int NOT NULL AUTO_INCREMENT, + `userId` int NOT NULL, + `productId` int NOT NULL, + `skuText` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, + `quantity` int NOT NULL DEFAULT 1, + `createdAt` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + `updatedAt` datetime(3) NOT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `CartItem_userId_productId_skuText_key`(`userId` ASC, `productId` ASC, `skuText` ASC) USING BTREE, + INDEX `CartItem_productId_fkey`(`productId` ASC) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of cartitem +-- ---------------------------- + +-- ---------------------------- +-- Table structure for supplier_after_sale +-- ---------------------------- +DROP TABLE IF EXISTS `supplier_after_sale`; +CREATE TABLE `supplier_after_sale` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '供应商售后单主键ID', + `serviceOrderId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '售后业务单号', + `supplierId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '供应商ID', + `userId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户ID', + `mainOrderId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '关联主单ID', + `subOrderId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '关联子单ID', + `serviceType` int NOT NULL COMMENT '售后类型:4退货,5换货', + `serviceState` int NOT NULL DEFAULT 0 COMMENT '售后状态:0商家待审核,1审核通过,2审核拒绝,3用户已寄回,4商家已收货,5已完成,6已取消', + `auditStatus` int NOT NULL DEFAULT 0 COMMENT '审核状态:0待审核,1审核通过,2审核拒绝', + `logisticsState` int NOT NULL DEFAULT 0 COMMENT '物流状态:0无物流,1待用户寄回,2用户已寄回,3商家已收货,4商家已发货,5用户已收货', + `amount` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '售后金额', + `reason` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '售后原因', + `auditRemark` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '审核备注', + `userLogisticsNo` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户寄回物流单号', + `userLogisticsCompany` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户寄回物流公司', + `merchantLogisticsNo` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '商家换货物流单号', + `merchantLogisticsCompany` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '商家换货物流公司', + `applyTime` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '申请时间', + `auditTime` datetime(3) NULL DEFAULT NULL COMMENT '审核时间', + `userShipTime` datetime(3) NULL DEFAULT NULL COMMENT '用户寄回时间', + `merchantReceiveTime` datetime(3) NULL DEFAULT NULL COMMENT '商家收货时间', + `merchantShipTime` datetime(3) NULL DEFAULT NULL COMMENT '商家发货时间', + `finishTime` datetime(3) NULL DEFAULT NULL COMMENT '完成时间', + `requestBody` json NULL COMMENT '原始业务请求体', + `createTime` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', + `updateTime` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间', + `deleted` int NOT NULL DEFAULT 0 COMMENT '删除状态:0未删除,1已删除', + `reasonType` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, + `problemDescription` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL, + `pickupMethod` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, + `pickupName` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, + `pickupMobile` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, + `pickupAddress` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL, + `images` json NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `supplier_after_sale_serviceOrderId_key`(`serviceOrderId` ASC) USING BTREE, + INDEX `supplier_after_sale_supplierId_mainOrderId_idx`(`supplierId` ASC, `mainOrderId` ASC) USING BTREE, + INDEX `supplier_after_sale_supplierId_userId_idx`(`supplierId` ASC, `userId` ASC) USING BTREE, + INDEX `supplier_after_sale_subOrderId_idx`(`subOrderId` ASC) USING BTREE, + INDEX `supplier_after_sale_serviceType_serviceState_idx`(`serviceType` ASC, `serviceState` ASC) USING BTREE, + INDEX `supplier_after_sale_auditStatus_logisticsState_idx`(`auditStatus` ASC, `logisticsState` ASC) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '供应商退款售后流程表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of supplier_after_sale +-- ---------------------------- + +-- ---------------------------- +-- Table structure for supplier_info +-- ---------------------------- +DROP TABLE IF EXISTS `supplier_info`; +CREATE TABLE `supplier_info` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `supplierIdentification` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '供应商唯一标识', + `supplierId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '供应商ID', + `supplierName` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '供应商名称', + `clientId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '平台分配的客户端ID', + `clientSecret` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '平台分配的客户端密钥', + `supplierApiUrl` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '供应商开放接口地址', + `sroNo` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'BAB7服务商编号', + `createTime` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', + `updateTime` datetime(3) NOT NULL COMMENT '更新时间', + `creator` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人', + `updator` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新人', + `version` int NOT NULL DEFAULT 0 COMMENT '版本号', + `deleted` int NOT NULL DEFAULT 0, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `supplier_info_supplierIdentification_key`(`supplierIdentification` ASC) USING BTREE, + UNIQUE INDEX `supplier_info_supplierId_key`(`supplierId` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '供应商信息表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of supplier_info +-- ---------------------------- +INSERT INTO `supplier_info` VALUES (1, 'joyful-selection', '99999999', '悦享优选', 'pmall_5f8c2d91a7b34e3db1c9f4e6a8d2b7c1', 'pmsec_8d7f4c1a9b3e6f2d5c8a1e7b9f4d6c2a3b8e1f5d9c7a6b4e2f1a8c3d6b9e7f', NULL, NULL, '2026-07-28 15:16:35.000', '2026-07-28 15:16:35.000', 'system', 'system', 0, 0); + +-- ---------------------------- +-- Table structure for supplier_invoice_info +-- ---------------------------- +DROP TABLE IF EXISTS `supplier_invoice_info`; +CREATE TABLE `supplier_invoice_info` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '发票信息主键ID', + `invoiceBizId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '发票业务唯一ID,优先使用发票号或事件订单号', + `supplierId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '供应商ID', + `userId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户ID', + `mainOrderId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '关联主单ID', + `subOrderId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '关联子单ID', + `settlementId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '结算单号', + `invoiceCode` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '发票代码或纳税识别号', + `invoiceIds` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '发票号码列表,多个用英文逗号分隔', + `orderIds` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '关联订单号列表,多个用英文逗号分隔', + `invoiceStatus` int NOT NULL DEFAULT 0 COMMENT '发票状态:0已记录,1已申请,2已开票,3失败', + `fileId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '发票文件ID', + `filePath` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '发票文件路径', + `requestBody` json NULL COMMENT '原始业务请求体', + `createTime` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', + `updateTime` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间', + `deleted` int NOT NULL DEFAULT 0 COMMENT '删除状态:0未删除,1已删除', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `supplier_invoice_info_invoiceBizId_key`(`invoiceBizId` ASC) USING BTREE, + INDEX `supplier_invoice_info_supplierId_mainOrderId_idx`(`supplierId` ASC, `mainOrderId` ASC) USING BTREE, + INDEX `supplier_invoice_info_settlementId_idx`(`settlementId` ASC) USING BTREE, + INDEX `supplier_invoice_info_invoiceCode_idx`(`invoiceCode` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '供应商发票信息表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of supplier_invoice_info +-- ---------------------------- +INSERT INTO `supplier_invoice_info` VALUES (1, 'INV_ORD20260729134300_001', '99999999', '17a7f8ad3ee947b4a26175049a9c253d', 'ORD20260729134300', 'ORD20260729134300_001', NULL, NULL, 'INV_ORD20260729134300_001', 'ORD20260729134300_001', 2, NULL, NULL, '{\"time\": 1785303898394, \"amount\": 20.02, \"reason\": \"发票信息\", \"userId\": \"17a7f8ad3ee947b4a26175049a9c253d\", \"orderId\": \"ORD20260729134300_001\", \"orderIds\": \"ORD20260729134300_001\", \"orderType\": 11, \"invoiceIds\": \"INV_ORD20260729134300_001\", \"subOrderId\": \"ORD20260729134300_001\", \"supplierId\": \"99999999\", \"mainOrderId\": \"ORD20260729134300\", \"invoiceBizId\": \"INV_ORD20260729134300_001\"}', '2026-07-29 05:45:01.657', '2026-07-29 05:45:01.657', 0); + +-- ---------------------------- +-- Table structure for supplier_order_main +-- ---------------------------- +DROP TABLE IF EXISTS `supplier_order_main`; +CREATE TABLE `supplier_order_main` ( + `id` int NOT NULL AUTO_INCREMENT, + `orderId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '主订单ID', + `orderNumberId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '主订单编号', + `supplierId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '供应商ID', + `userId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '平台用户ID', + `aplName` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '应用名称', + `addressId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '收货地址ID', + `remark` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '订单备注', + `createTime` datetime(3) NULL DEFAULT NULL COMMENT '订单创建时间', + `updateTime` datetime(3) NULL DEFAULT NULL COMMENT '订单更新时间', + `deliveryTime` datetime(3) NULL DEFAULT NULL COMMENT '发货时间', + `orderFinishTime` datetime(3) NULL DEFAULT NULL COMMENT '订单完成时间', + `completeTime` datetime(3) NULL DEFAULT NULL COMMENT '交易完成时间', + `deliveryStatus` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '配送状态', + `freightCost` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '运费积分金额', + `wPayOrAPayFreightCost` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '微信或支付宝支付运费金额', + `refund` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '积分退款金额', + `wPayOrAPayRefund` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '微信或支付宝退款金额', + `logisticsOrderId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '物流单号', + `crrgBsnNm` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '承运商名称', + `orderState` int NOT NULL DEFAULT 0 COMMENT '订单状态:0待付款,1待发货,2待收货,3已取消,4已完成兼容旧值,5已完成,6换货,7退货,8废除', + `isSplit` tinyint NOT NULL DEFAULT 0 COMMENT '主单是否拆单:0未拆单,1已拆单', + `splitTime` datetime(3) NULL DEFAULT NULL COMMENT '主单拆单时间', + `totalPrice` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '订单总金额', + `wPayOrAPay` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '微信或支付宝支付金额', + `pointsPrice` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '积分支付金额', + `invoiceCode` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '发票编码', + `createAt` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '记录创建时间', + `updateAt` datetime(3) NOT NULL COMMENT '记录更新时间', + `deleted` int NOT NULL DEFAULT 0 COMMENT '删除状态:0未删除,1已删除', + `payTime` datetime NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `supplier_order_main_orderId_key`(`orderId` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '供应商主订单表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of supplier_order_main +-- ---------------------------- +INSERT INTO `supplier_order_main` VALUES (1, 'ORD20260901165221', 'TR20260901165221752', '99999999', 'c281e124321c8db3a14f0a7c86046c3c', '积分商城', 'addr-1788252731181', '', '2026-09-01 08:52:21.752', '2026-09-01 08:55:56.487', '2026-09-01 08:55:23.144', NULL, '2026-09-01 08:55:56.487', '1', 0.00, 0.00, 0.00, 0.00, '20260901', '韵达快递', 5, 1, '2026-09-01 08:52:25.891', 10.01, 0.00, 10.01, NULL, '2026-09-01 08:52:21.766', '2026-09-01 08:55:56.526', 0, '2026-09-01 08:52:26'); + +-- ---------------------------- +-- Table structure for supplier_order_operate_log +-- ---------------------------- +DROP TABLE IF EXISTS `supplier_order_operate_log`; +CREATE TABLE `supplier_order_operate_log` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '订单操作日志主键ID', + `supplierId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '供应商ID', + `userId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户ID', + `mainOrderId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '主单ID', + `subOrderId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '子单ID', + `operateType` int NOT NULL COMMENT '操作类型:1创建待支付,2支付成功,3拆单,4取消主单,5取消子单,6取消已支付未拆单整单,7确认收货,8已完成,9退款,10换货,11废除子单,12发票信息', + `beforeState` int NULL DEFAULT NULL COMMENT '操作前订单状态', + `afterState` int NULL DEFAULT NULL COMMENT '操作后订单状态', + `beforeSplit` int NULL DEFAULT NULL COMMENT '操作前拆单状态:0未拆单,1已拆单', + `afterSplit` int NULL DEFAULT NULL COMMENT '操作后拆单状态:0未拆单,1已拆单', + `remark` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '操作备注', + `operator` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '操作人或系统标识', + `createTime` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', + `deleted` int NOT NULL DEFAULT 0 COMMENT '删除状态:0未删除,1已删除', + PRIMARY KEY (`id`) USING BTREE, + INDEX `supplier_order_operate_log_supplierId_mainOrderId_idx`(`supplierId` ASC, `mainOrderId` ASC) USING BTREE, + INDEX `supplier_order_operate_log_subOrderId_idx`(`subOrderId` ASC) USING BTREE, + INDEX `supplier_order_operate_log_operateType_idx`(`operateType` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '供应商订单操作日志表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of supplier_order_operate_log +-- ---------------------------- +INSERT INTO `supplier_order_operate_log` VALUES (1, '99999999', 'c281e124321c8db3a14f0a7c86046c3c', 'ORD20260901165221', 'ORD20260901165221_001', 7, 1, 2, NULL, NULL, NULL, 'api', '2026-09-01 08:55:23.165', 0); +INSERT INTO `supplier_order_operate_log` VALUES (2, '99999999', 'c281e124321c8db3a14f0a7c86046c3c', 'ORD20260901165221', 'ORD20260901165221_001', 8, 2, 5, NULL, NULL, '确认收货', 'system', '2026-09-01 08:55:56.515', 0); + +-- ---------------------------- +-- Table structure for supplier_order_product +-- ---------------------------- +DROP TABLE IF EXISTS `supplier_order_product`; +CREATE TABLE `supplier_order_product` ( + `id` int NOT NULL AUTO_INCREMENT, + `subOrderId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '所属子订单ID', + `sku` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商品SKU编码', + `number` int NOT NULL COMMENT '商品购买数量', + `price` decimal(10, 2) NOT NULL COMMENT '商品成交单价', + `name` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商品名称快照', + `taxRate` decimal(5, 4) NOT NULL DEFAULT 0.1300 COMMENT '商品税率快照', + `productImgUrl` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '商品图片地址', + `fileId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '平台文件ID或图片ID', + `filePath` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '文件存储路径', + `picInfo` json NULL COMMENT '商品图片列表快照,JSON数组', + `createAt` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '记录创建时间', + `updateAt` datetime(3) NOT NULL COMMENT '记录更新时间', + `deleted` int NOT NULL DEFAULT 0 COMMENT '删除状态:0未删除,1已删除', + PRIMARY KEY (`id`) USING BTREE, + INDEX `supplier_order_product_subOrderId_idx`(`subOrderId` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '供应商子订单商品表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of supplier_order_product +-- ---------------------------- +INSERT INTO `supplier_order_product` VALUES (1, 'ORD20260901165221_001', '2-cream-standard', 1, 10.01, '手机壳', 0.1300, 'https://ts3.tc.mm.bing.net/th/id/OIP-C.MkmCbLoY2S4zqzMeHvCDMQHaHD?w=128&h=108&c=7&qlt=90&bgcl=f61537&r=0&o=6&dpr=1.3&pid=13.1', 'https://ts3.tc.mm.bing.net/th/id/OIP-C.MkmCbLoY2S4zqzMeHvCDMQHaHD?w=128&h=108&c=7&qlt=90&bgcl=f61537&r=0&o=6&dpr=1.3&pid=13.1', 'https://ts3.tc.mm.bing.net/th/id/OIP-C.MkmCbLoY2S4zqzMeHvCDMQHaHD?w=128&h=108&c=7&qlt=90&bgcl=f61537&r=0&o=6&dpr=1.3&pid=13.1', '[\"https://ts3.tc.mm.bing.net/th/id/OIP-C.MkmCbLoY2S4zqzMeHvCDMQHaHD?w=128&h=108&c=7&qlt=90&bgcl=f61537&r=0&o=6&dpr=1.3&pid=13.1\"]', '2026-09-01 08:52:21.780', '2026-09-01 08:52:21.780', 0); + +-- ---------------------------- +-- Table structure for supplier_order_push_log +-- ---------------------------- +DROP TABLE IF EXISTS `supplier_order_push_log`; +CREATE TABLE `supplier_order_push_log` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '订单推送日志主键ID', + `supplierId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '供应商ID', + `userId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户ID', + `mainOrderId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '主单ID', + `orderId` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '推送订单ID,多个子单用英文逗号分隔', + `orderType` int NULL DEFAULT NULL COMMENT '推送事件类型:1取消主单,2拆单子单,3取消子单,7取消已支付未拆单整单', + `apiPath` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '推送接口路径', + `requestBody` json NULL COMMENT '推送请求体', + `responseBody` json NULL COMMENT '推送响应体', + `httpStatus` int NULL DEFAULT NULL COMMENT 'HTTP状态码', + `resultCode` int NULL DEFAULT NULL COMMENT '远程业务返回码', + `pushStatus` int NOT NULL DEFAULT 0 COMMENT '推送状态:0待推送,1推送成功,2推送失败', + `retryCount` int NOT NULL DEFAULT 0 COMMENT '重试次数', + `errorMessage` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '失败原因', + `lastPushTime` datetime(3) NULL DEFAULT NULL COMMENT '最后推送时间', + `nextRetryTime` datetime(3) NULL DEFAULT NULL COMMENT '下次重试时间', + `createTime` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', + `updateTime` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间', + `deleted` int NOT NULL DEFAULT 0 COMMENT '删除状态:0未删除,1已删除', + PRIMARY KEY (`id`) USING BTREE, + INDEX `supplier_order_push_log_supplierId_mainOrderId_idx`(`supplierId` ASC, `mainOrderId` ASC) USING BTREE, + INDEX `supplier_order_push_log_orderType_pushStatus_idx`(`orderType` ASC, `pushStatus` ASC) USING BTREE, + INDEX `supplier_order_push_log_nextRetryTime_idx`(`nextRetryTime` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '供应商订单远程推送日志表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of supplier_order_push_log +-- ---------------------------- +INSERT INTO `supplier_order_push_log` VALUES (1, '99999999', 'c281e124321c8db3a14f0a7c86046c3c', 'ORD20260901165221', 'ORD20260901165221_001', 6, '/pointOrder/pushOrderInfoMsg', '{\"time\": 1788252956480, \"userId\": \"c281e124321c8db3a14f0a7c86046c3c\", \"orderId\": \"ORD20260901165221_001\", \"orderType\": 6, \"supplierId\": \"99999999\", \"mainOrderId\": \"ORD20260901165221\"}', '{\"message\": \"成功\", \"resultCode\": 0}', 200, 0, 1, 0, NULL, '2026-09-01 08:55:56.601', NULL, '2026-09-01 08:55:56.534', '2026-09-01 08:55:56.604', 0); + +-- ---------------------------- +-- Table structure for supplier_order_sub +-- ---------------------------- +DROP TABLE IF EXISTS `supplier_order_sub`; +CREATE TABLE `supplier_order_sub` ( + `id` int NOT NULL AUTO_INCREMENT, + `orderId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '子订单ID', + `orderNumberId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '子订单编号', + `mainOrderId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '所属主订单ID', + `supplierId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '供应商ID', + `token` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '平台桥接访问令牌快照', + `userId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '平台用户ID', + `aplName` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '应用名称', + `addressId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '收货地址ID', + `remark` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '订单备注', + `createTime` datetime(3) NULL DEFAULT NULL COMMENT '订单创建时间', + `updateTime` datetime(3) NULL DEFAULT NULL COMMENT '订单更新时间', + `deliveryTime` datetime(3) NULL DEFAULT NULL COMMENT '发货时间', + `orderFinishTime` datetime(3) NULL DEFAULT NULL COMMENT '订单完成时间', + `completeTime` datetime(3) NULL DEFAULT NULL COMMENT '交易完成时间', + `deliveryStatus` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '配送状态', + `freightCost` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '运费积分金额', + `wPayOrAPayFreightCost` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '微信或支付宝支付运费金额', + `refund` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '积分退款金额', + `wPayOrAPayRefund` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '微信或支付宝退款金额', + `logisticsOrderId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '物流单号', + `crrgBsnNm` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '承运商名称', + `orderState` int NOT NULL DEFAULT 0 COMMENT '订单状态:0待付款,1待发货,2待收货,3已取消,4已完成兼容旧值,5已完成,6换货,7退货,8废除', + `totalPrice` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '子订单总金额', + `wPayOrAPay` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '微信或支付宝支付金额', + `pointsPrice` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '积分支付金额', + `invoiceCode` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '发票编码', + `createAt` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '记录创建时间', + `updateAt` datetime(3) NOT NULL COMMENT '记录更新时间', + `deleted` int NOT NULL DEFAULT 0 COMMENT '删除状态:0未删除,1已删除', + `payTime` datetime NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `supplier_order_sub_orderId_key`(`orderId` ASC) USING BTREE, + INDEX `supplier_order_sub_mainOrderId_idx`(`mainOrderId` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '供应商子订单表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of supplier_order_sub +-- ---------------------------- +INSERT INTO `supplier_order_sub` VALUES (1, 'ORD20260901165221_001', 'TR20260901165221752_001', 'ORD20260901165221', '99999999', NULL, 'c281e124321c8db3a14f0a7c86046c3c', '积分商城', 'addr-1788252731181', '', '2026-09-01 08:52:21.752', '2026-09-01 08:55:56.487', '2026-09-01 08:55:23.144', '2026-09-01 08:55:56.487', '2026-09-01 08:55:56.487', '1', 0.00, 0.00, 0.00, 0.00, '20260901', '韵达快递', 5, 10.01, 0.00, 10.01, NULL, '2026-09-01 08:52:21.776', '2026-09-01 08:55:56.509', 0, '2026-09-01 08:52:26'); + +-- ---------------------------- +-- Table structure for supplier_points_ledger +-- ---------------------------- +DROP TABLE IF EXISTS `supplier_points_ledger`; +CREATE TABLE `supplier_points_ledger` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '积分流水主键ID', + `ledgerNo` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '流水号,业务唯一', + `supplierId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '供应商ID', + `userId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户ID', + `mainOrderId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '关联主单ID', + `subOrderId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '关联子单ID', + `bizType` int NOT NULL COMMENT '业务类型:1支付消费,2取消返还,3退货退款,4人工调整', + `direction` int NOT NULL COMMENT '流水方向:1增加,2扣减', + `points` decimal(10, 2) NOT NULL COMMENT '积分变动金额', + `beforePoints` decimal(10, 2) NULL DEFAULT NULL COMMENT '变动前积分余额', + `afterPoints` decimal(10, 2) NULL DEFAULT NULL COMMENT '变动后积分余额', + `remark` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '流水备注', + `createTime` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', + `updateTime` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间', + `deleted` int NOT NULL DEFAULT 0 COMMENT '删除状态:0未删除,1已删除', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `supplier_points_ledger_ledgerNo_key`(`ledgerNo` ASC) USING BTREE, + INDEX `supplier_points_ledger_supplierId_userId_idx`(`supplierId` ASC, `userId` ASC) USING BTREE, + INDEX `supplier_points_ledger_mainOrderId_idx`(`mainOrderId` ASC) USING BTREE, + INDEX `supplier_points_ledger_subOrderId_idx`(`subOrderId` ASC) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '供应商用户积分消费流水表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of supplier_points_ledger +-- ---------------------------- + +-- ---------------------------- +-- Table structure for supplier_product +-- ---------------------------- +DROP TABLE IF EXISTS `supplier_product`; +CREATE TABLE `supplier_product` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '商品主键ID', + `sku` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商品SKU编码', + `number` int NOT NULL DEFAULT 0 COMMENT '默认购买数量或导入数量', + `price` decimal(10, 2) NOT NULL COMMENT '商品销售价格', + `name` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商品名称', + `taxRate` decimal(5, 4) NOT NULL DEFAULT 0.1300 COMMENT '商品税率', + `picInfo` json NULL COMMENT '商品图片列表,JSON数组', + `description` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '商品简短描述', + `detail` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '商品详情描述', + `originPrice` decimal(10, 2) NULL DEFAULT NULL COMMENT '商品原价', + `imageUrl` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '商品主图地址', + `categoryId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '商品分类ID', + `category` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '商品分类名称', + `tag` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '商品标签', + `sales` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '商品销量展示文案', + `stock` int NOT NULL DEFAULT 0 COMMENT '商品库存', + `sortOrder` int NOT NULL DEFAULT 0 COMMENT '排序值,越小越靠前', + `enabled` tinyint(1) NOT NULL DEFAULT 1 COMMENT '是否启用', + `deleted` int NOT NULL DEFAULT 0 COMMENT '删除状态:0未删除,1已删除', + `createdAt` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', + `updatedAt` datetime(3) NOT NULL COMMENT '更新时间', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `supplier_product_sku_key`(`sku` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 25 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '供应商商品信息表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of supplier_product +-- ---------------------------- +INSERT INTO `supplier_product` VALUES (1, 'SKU001', 2, 149.99, '蓝牙耳机', 0.1300, '[\"https://ts3.tc.mm.bing.net/th/id/OIP-C.im_WvX9bWGyvagEZp4Pz0AHaGX?w=142&h=108&c=7&qlt=90&bgcl=a01e15&r=0&o=6&dpr=1.3&pid=13.1\", \"https://ts3.tc.mm.bing.net/th/id/OIP-C.MkmCbLoY2S4zqzMeHvCDMQHaHD?w=128&h=108&c=7&qlt=90&bgcl=f61537&r=0&o=6&dpr=1.3&pid=13.1\"]', '轻盈佩戴,稳定连接,通勤运动都好用', '轻盈佩戴,稳定连接,通勤运动都好用。精选品质商品,适合积分商城兑换和日常福利采购场景。', 199.99, 'https://ts3.tc.mm.bing.net/th/id/OIP-C.im_WvX9bWGyvagEZp4Pz0AHaGX?w=142&h=108&c=7&qlt=90&bgcl=a01e15&r=0&o=6&dpr=1.3&pid=13.1', 'digitalAppliance', '数码家电', '积分兑换', '1.2万件', 180, 1, 1, 0, '2026-07-28 09:43:03.406', '2026-07-28 09:43:03.406'); +INSERT INTO `supplier_product` VALUES (2, 'SKU002', 1, 10.01, '手机壳', 0.1300, '[\"https://ts3.tc.mm.bing.net/th/id/OIP-C.MkmCbLoY2S4zqzMeHvCDMQHaHD?w=128&h=108&c=7&qlt=90&bgcl=f61537&r=0&o=6&dpr=1.3&pid=13.1\", \"https://ts2.tc.mm.bing.net/th/id/OIP-C.CKSBSB33LwT9qJD_Yhb1mwHaKk?w=132&h=108&c=7&qlt=90&bgcl=2e287d&r=0&o=6&dpr=1.3&pid=13.1\"]', '防滑耐磨,贴合机身,日常保护更安心', '防滑耐磨,贴合机身,日常保护更安心。精选品质商品,适合积分商城兑换和日常福利采购场景。', 19.90, 'https://ts3.tc.mm.bing.net/th/id/OIP-C.MkmCbLoY2S4zqzMeHvCDMQHaHD?w=128&h=108&c=7&qlt=90&bgcl=f61537&r=0&o=6&dpr=1.3&pid=13.1', 'digitalAppliance', '数码家电', '赠品', '5632件', 187, 2, 1, 0, '2026-07-28 09:43:03.427', '2026-07-28 09:43:03.427'); +INSERT INTO `supplier_product` VALUES (3, 'SKU003', 1, 99.00, '智能音箱 Pro', 0.1300, '[\"https://ts2.tc.mm.bing.net/th/id/OIP-C.CKSBSB33LwT9qJD_Yhb1mwHaKk?w=132&h=108&c=7&qlt=90&bgcl=2e287d&r=0&o=6&dpr=1.3&pid=13.1\", \"https://tse1-mm.cn.bing.net/th/id/OIP-C.StrDRqennoZNbzSPZapKZwHaEJ?w=286&h=180&c=7&r=0&o=7&cb=thfc1falcon&dpr=1.3&pid=1.7&rm=3\"]', '语音唤醒,沉浸音效,适合卧室和书房', '语音唤醒,沉浸音效,适合卧室和书房。精选品质商品,适合积分商城兑换和日常福利采购场景。', 139.00, 'https://ts2.tc.mm.bing.net/th/id/OIP-C.CKSBSB33LwT9qJD_Yhb1mwHaKk?w=132&h=108&c=7&qlt=90&bgcl=2e287d&r=0&o=6&dpr=1.3&pid=13.1', 'digitalAppliance', '数码家电', '热门推荐', '2.1万件', 194, 3, 1, 0, '2026-07-28 09:43:03.431', '2026-07-28 09:43:03.431'); +INSERT INTO `supplier_product` VALUES (4, 'SKU004', 1, 129.00, '奶油白迷你烤箱', 0.1300, '[\"https://tse1-mm.cn.bing.net/th/id/OIP-C.StrDRqennoZNbzSPZapKZwHaEJ?w=286&h=180&c=7&r=0&o=7&cb=thfc1falcon&dpr=1.3&pid=1.7&rm=3\", \"https://tse3-mm.cn.bing.net/th/id/OIP-C.ynSetSr3z884UC6sEp4yiwHaEJ?w=331&h=186&c=7&r=0&o=7&cb=thfc1falcon&dpr=1.3&pid=1.7&rm=3\"]', '小巧不占地,早餐烘焙好帮手', '小巧不占地,早餐烘焙好帮手。精选品质商品,适合积分商城兑换和日常福利采购场景。', 169.00, 'https://tse1-mm.cn.bing.net/th/id/OIP-C.StrDRqennoZNbzSPZapKZwHaEJ?w=286&h=180&c=7&r=0&o=7&cb=thfc1falcon&dpr=1.3&pid=1.7&rm=3', 'homeLiving', '居家生活', '新品上架', '1.2万件', 201, 4, 1, 0, '2026-07-28 09:43:03.435', '2026-07-28 09:43:03.435'); +INSERT INTO `supplier_product` VALUES (5, 'SKU005', 1, 69.90, '北欧陶瓷餐具套装', 0.1300, '[\"https://tse3-mm.cn.bing.net/th/id/OIP-C.ynSetSr3z884UC6sEp4yiwHaEJ?w=331&h=186&c=7&r=0&o=7&cb=thfc1falcon&dpr=1.3&pid=1.7&rm=3\", \"https://ts3.tc.mm.bing.net/th/id/OIP-C.im_WvX9bWGyvagEZp4Pz0AHaGX?w=142&h=108&c=7&qlt=90&bgcl=a01e15&r=0&o=6&dpr=1.3&pid=13.1\"]', '简约釉色,日常用餐更有仪式感', '简约釉色,日常用餐更有仪式感。精选品质商品,适合积分商城兑换和日常福利采购场景。', 99.90, 'https://tse3-mm.cn.bing.net/th/id/OIP-C.ynSetSr3z884UC6sEp4yiwHaEJ?w=331&h=186&c=7&r=0&o=7&cb=thfc1falcon&dpr=1.3&pid=1.7&rm=3', 'homeLiving', '居家生活', '品质精选', '6453件', 208, 5, 1, 0, '2026-07-28 09:43:03.438', '2026-07-28 09:43:03.438'); +INSERT INTO `supplier_product` VALUES (6, 'SKU006', 1, 179.00, '纯棉舒适四件套', 0.1300, '[\"https://ts3.tc.mm.bing.net/th/id/OIP-C.im_WvX9bWGyvagEZp4Pz0AHaGX?w=142&h=108&c=7&qlt=90&bgcl=a01e15&r=0&o=6&dpr=1.3&pid=13.1\", \"https://ts3.tc.mm.bing.net/th/id/OIP-C.MkmCbLoY2S4zqzMeHvCDMQHaHD?w=128&h=108&c=7&qlt=90&bgcl=f61537&r=0&o=6&dpr=1.3&pid=13.1\"]', '柔软亲肤,安心睡眠,四季可用', '柔软亲肤,安心睡眠,四季可用。精选品质商品,适合积分商城兑换和日常福利采购场景。', 229.00, 'https://ts3.tc.mm.bing.net/th/id/OIP-C.im_WvX9bWGyvagEZp4Pz0AHaGX?w=142&h=108&c=7&qlt=90&bgcl=a01e15&r=0&o=6&dpr=1.3&pid=13.1', 'homeLiving', '居家生活', '积分兑换', '8321件', 215, 6, 1, 0, '2026-07-28 09:43:03.441', '2026-07-28 09:43:03.441'); +INSERT INTO `supplier_product` VALUES (7, 'SKU007', 1, 99.90, '植萃保湿护肤礼盒', 0.1300, '[\"https://ts3.tc.mm.bing.net/th/id/OIP-C.MkmCbLoY2S4zqzMeHvCDMQHaHD?w=128&h=108&c=7&qlt=90&bgcl=f61537&r=0&o=6&dpr=1.3&pid=13.1\", \"https://ts2.tc.mm.bing.net/th/id/OIP-C.CKSBSB33LwT9qJD_Yhb1mwHaKk?w=132&h=108&c=7&qlt=90&bgcl=2e287d&r=0&o=6&dpr=1.3&pid=13.1\"]', '补水保湿,清爽不黏腻', '补水保湿,清爽不黏腻。精选品质商品,适合积分商城兑换和日常福利采购场景。', 139.90, 'https://ts3.tc.mm.bing.net/th/id/OIP-C.MkmCbLoY2S4zqzMeHvCDMQHaHD?w=128&h=108&c=7&qlt=90&bgcl=f61537&r=0&o=6&dpr=1.3&pid=13.1', 'beautyCare', '美妆个护', '礼盒精选', '4820件', 222, 7, 1, 0, '2026-07-28 09:43:03.443', '2026-07-28 09:43:03.443'); +INSERT INTO `supplier_product` VALUES (8, 'SKU008', 1, 59.90, '香氛洗护旅行套装', 0.1300, '[\"https://ts2.tc.mm.bing.net/th/id/OIP-C.CKSBSB33LwT9qJD_Yhb1mwHaKk?w=132&h=108&c=7&qlt=90&bgcl=2e287d&r=0&o=6&dpr=1.3&pid=13.1\", \"https://tse1-mm.cn.bing.net/th/id/OIP-C.StrDRqennoZNbzSPZapKZwHaEJ?w=286&h=180&c=7&r=0&o=7&cb=thfc1falcon&dpr=1.3&pid=1.7&rm=3\"]', '温和清洁,清新留香,出行便携', '温和清洁,清新留香,出行便携。精选品质商品,适合积分商城兑换和日常福利采购场景。', 89.90, 'https://ts2.tc.mm.bing.net/th/id/OIP-C.CKSBSB33LwT9qJD_Yhb1mwHaKk?w=132&h=108&c=7&qlt=90&bgcl=2e287d&r=0&o=6&dpr=1.3&pid=13.1', 'beautyCare', '美妆个护', '新品上架', '7932件', 229, 8, 1, 0, '2026-07-28 09:43:03.446', '2026-07-28 09:43:03.446'); +INSERT INTO `supplier_product` VALUES (9, 'SKU009', 1, 49.90, '便携补光化妆镜', 0.1300, '[\"https://tse1-mm.cn.bing.net/th/id/OIP-C.StrDRqennoZNbzSPZapKZwHaEJ?w=286&h=180&c=7&r=0&o=7&cb=thfc1falcon&dpr=1.3&pid=1.7&rm=3\", \"https://tse3-mm.cn.bing.net/th/id/OIP-C.ynSetSr3z884UC6sEp4yiwHaEJ?w=331&h=186&c=7&r=0&o=7&cb=thfc1falcon&dpr=1.3&pid=1.7&rm=3\"]', '高清镜面,柔光补妆,随身携带', '高清镜面,柔光补妆,随身携带。精选品质商品,适合积分商城兑换和日常福利采购场景。', 79.90, 'https://tse1-mm.cn.bing.net/th/id/OIP-C.StrDRqennoZNbzSPZapKZwHaEJ?w=286&h=180&c=7&r=0&o=7&cb=thfc1falcon&dpr=1.3&pid=1.7&rm=3', 'beautyCare', '美妆个护', '热门推荐', '3651件', 236, 9, 1, 0, '2026-07-28 09:43:03.450', '2026-07-28 09:43:03.450'); +INSERT INTO `supplier_product` VALUES (10, 'SKU010', 1, 59.90, '橙意零食礼盒', 0.1300, '[\"https://tse3-mm.cn.bing.net/th/id/OIP-C.ynSetSr3z884UC6sEp4yiwHaEJ?w=331&h=186&c=7&r=0&o=7&cb=thfc1falcon&dpr=1.3&pid=1.7&rm=3\", \"https://ts3.tc.mm.bing.net/th/id/OIP-C.im_WvX9bWGyvagEZp4Pz0AHaGX?w=142&h=108&c=7&qlt=90&bgcl=a01e15&r=0&o=6&dpr=1.3&pid=13.1\"]', '精选零食组合,办公室分享刚刚好', '精选零食组合,办公室分享刚刚好。精选品质商品,适合积分商城兑换和日常福利采购场景。', 89.90, 'https://tse3-mm.cn.bing.net/th/id/OIP-C.ynSetSr3z884UC6sEp4yiwHaEJ?w=331&h=186&c=7&r=0&o=7&cb=thfc1falcon&dpr=1.3&pid=1.7&rm=3', 'foodDrink', '食品饮料', '礼盒精选', '6324件', 243, 10, 1, 0, '2026-07-28 09:43:03.453', '2026-07-28 09:43:03.453'); +INSERT INTO `supplier_product` VALUES (11, 'SKU011', 1, 49.90, '咖啡随行杯组合', 0.1300, '[\"https://ts3.tc.mm.bing.net/th/id/OIP-C.im_WvX9bWGyvagEZp4Pz0AHaGX?w=142&h=108&c=7&qlt=90&bgcl=a01e15&r=0&o=6&dpr=1.3&pid=13.1\", \"https://ts3.tc.mm.bing.net/th/id/OIP-C.MkmCbLoY2S4zqzMeHvCDMQHaHD?w=128&h=108&c=7&qlt=90&bgcl=f61537&r=0&o=6&dpr=1.3&pid=13.1\"]', '锁温防漏,通勤随行更方便', '锁温防漏,通勤随行更方便。精选品质商品,适合积分商城兑换和日常福利采购场景。', 69.90, 'https://ts3.tc.mm.bing.net/th/id/OIP-C.im_WvX9bWGyvagEZp4Pz0AHaGX?w=142&h=108&c=7&qlt=90&bgcl=a01e15&r=0&o=6&dpr=1.3&pid=13.1', 'foodDrink', '食品饮料', '积分兑换', '3520件', 250, 11, 1, 0, '2026-07-28 09:43:03.456', '2026-07-28 09:43:03.456'); +INSERT INTO `supplier_product` VALUES (12, 'SKU012', 1, 69.90, '下午茶点心礼盒', 0.1300, '[\"https://ts3.tc.mm.bing.net/th/id/OIP-C.MkmCbLoY2S4zqzMeHvCDMQHaHD?w=128&h=108&c=7&qlt=90&bgcl=f61537&r=0&o=6&dpr=1.3&pid=13.1\", \"https://ts2.tc.mm.bing.net/th/id/OIP-C.CKSBSB33LwT9qJD_Yhb1mwHaKk?w=132&h=108&c=7&qlt=90&bgcl=2e287d&r=0&o=6&dpr=1.3&pid=13.1\"]', '多种风味,一盒满足甜咸口味', '多种风味,一盒满足甜咸口味。精选品质商品,适合积分商城兑换和日常福利采购场景。', 99.90, 'https://ts3.tc.mm.bing.net/th/id/OIP-C.MkmCbLoY2S4zqzMeHvCDMQHaHD?w=128&h=108&c=7&qlt=90&bgcl=f61537&r=0&o=6&dpr=1.3&pid=13.1', 'foodDrink', '食品饮料', '品质精选', '4891件', 257, 12, 1, 0, '2026-07-28 09:43:03.459', '2026-07-28 09:43:03.459'); +INSERT INTO `supplier_product` VALUES (13, 'SKU013', 1, 39.90, '安心儿童水杯', 0.1300, '[\"https://ts2.tc.mm.bing.net/th/id/OIP-C.CKSBSB33LwT9qJD_Yhb1mwHaKk?w=132&h=108&c=7&qlt=90&bgcl=2e287d&r=0&o=6&dpr=1.3&pid=13.1\", \"https://tse1-mm.cn.bing.net/th/id/OIP-C.StrDRqennoZNbzSPZapKZwHaEJ?w=286&h=180&c=7&r=0&o=7&cb=thfc1falcon&dpr=1.3&pid=1.7&rm=3\"]', '轻巧易握,防摔耐用,出门好携带', '轻巧易握,防摔耐用,出门好携带。精选品质商品,适合积分商城兑换和日常福利采购场景。', 59.90, 'https://ts2.tc.mm.bing.net/th/id/OIP-C.CKSBSB33LwT9qJD_Yhb1mwHaKk?w=132&h=108&c=7&qlt=90&bgcl=2e287d&r=0&o=6&dpr=1.3&pid=13.1', 'motherBaby', '母婴玩具', '新品上架', '4250件', 264, 13, 1, 0, '2026-07-28 09:43:03.462', '2026-07-28 09:43:03.462'); +INSERT INTO `supplier_product` VALUES (14, 'SKU014', 1, 89.00, '益智积木套装', 0.1300, '[\"https://tse1-mm.cn.bing.net/th/id/OIP-C.StrDRqennoZNbzSPZapKZwHaEJ?w=286&h=180&c=7&r=0&o=7&cb=thfc1falcon&dpr=1.3&pid=1.7&rm=3\", \"https://tse3-mm.cn.bing.net/th/id/OIP-C.ynSetSr3z884UC6sEp4yiwHaEJ?w=331&h=186&c=7&r=0&o=7&cb=thfc1falcon&dpr=1.3&pid=1.7&rm=3\"]', '色彩柔和,锻炼动手能力', '色彩柔和,锻炼动手能力。精选品质商品,适合积分商城兑换和日常福利采购场景。', 129.00, 'https://tse1-mm.cn.bing.net/th/id/OIP-C.StrDRqennoZNbzSPZapKZwHaEJ?w=286&h=180&c=7&r=0&o=7&cb=thfc1falcon&dpr=1.3&pid=1.7&rm=3', 'motherBaby', '母婴玩具', '热门推荐', '2186件', 271, 14, 1, 0, '2026-07-28 09:43:03.465', '2026-07-28 09:43:03.465'); +INSERT INTO `supplier_product` VALUES (15, 'SKU015', 1, 69.90, '柔光儿童夜灯', 0.1300, '[\"https://tse3-mm.cn.bing.net/th/id/OIP-C.ynSetSr3z884UC6sEp4yiwHaEJ?w=331&h=186&c=7&r=0&o=7&cb=thfc1falcon&dpr=1.3&pid=1.7&rm=3\", \"https://ts3.tc.mm.bing.net/th/id/OIP-C.im_WvX9bWGyvagEZp4Pz0AHaGX?w=142&h=108&c=7&qlt=90&bgcl=a01e15&r=0&o=6&dpr=1.3&pid=13.1\"]', '温和光线,陪伴睡前时光', '温和光线,陪伴睡前时光。精选品质商品,适合积分商城兑换和日常福利采购场景。', 99.90, 'https://tse3-mm.cn.bing.net/th/id/OIP-C.ynSetSr3z884UC6sEp4yiwHaEJ?w=331&h=186&c=7&r=0&o=7&cb=thfc1falcon&dpr=1.3&pid=1.7&rm=3', 'motherBaby', '母婴玩具', '品质精选', '3548件', 278, 15, 1, 0, '2026-07-28 09:43:03.469', '2026-07-28 09:43:03.469'); +INSERT INTO `supplier_product` VALUES (16, 'SKU016', 1, 149.00, '大容量电脑背包', 0.1300, '[\"https://ts3.tc.mm.bing.net/th/id/OIP-C.im_WvX9bWGyvagEZp4Pz0AHaGX?w=142&h=108&c=7&qlt=90&bgcl=a01e15&r=0&o=6&dpr=1.3&pid=13.1\", \"https://ts3.tc.mm.bing.net/th/id/OIP-C.MkmCbLoY2S4zqzMeHvCDMQHaHD?w=128&h=108&c=7&qlt=90&bgcl=f61537&r=0&o=6&dpr=1.3&pid=13.1\"]', '分区收纳,通勤出行更轻松', '分区收纳,通勤出行更轻松。精选品质商品,适合积分商城兑换和日常福利采购场景。', 199.00, 'https://ts3.tc.mm.bing.net/th/id/OIP-C.im_WvX9bWGyvagEZp4Pz0AHaGX?w=142&h=108&c=7&qlt=90&bgcl=a01e15&r=0&o=6&dpr=1.3&pid=13.1', 'fashionBag', '服饰鞋包', '积分兑换', '1.6万件', 285, 16, 1, 0, '2026-07-28 09:43:03.472', '2026-07-28 09:43:03.472'); +INSERT INTO `supplier_product` VALUES (17, 'SKU017', 1, 79.90, '轻巧通勤斜挎包', 0.1300, '[\"https://ts3.tc.mm.bing.net/th/id/OIP-C.MkmCbLoY2S4zqzMeHvCDMQHaHD?w=128&h=108&c=7&qlt=90&bgcl=f61537&r=0&o=6&dpr=1.3&pid=13.1\", \"https://ts2.tc.mm.bing.net/th/id/OIP-C.CKSBSB33LwT9qJD_Yhb1mwHaKk?w=132&h=108&c=7&qlt=90&bgcl=2e287d&r=0&o=6&dpr=1.3&pid=13.1\"]', '简洁百搭,日常出门更实用', '简洁百搭,日常出门更实用。精选品质商品,适合积分商城兑换和日常福利采购场景。', 109.90, 'https://ts3.tc.mm.bing.net/th/id/OIP-C.MkmCbLoY2S4zqzMeHvCDMQHaHD?w=128&h=108&c=7&qlt=90&bgcl=f61537&r=0&o=6&dpr=1.3&pid=13.1', 'fashionBag', '服饰鞋包', '新品上架', '5321件', 292, 17, 1, 0, '2026-07-28 09:43:03.475', '2026-07-28 09:43:03.475'); +INSERT INTO `supplier_product` VALUES (18, 'SKU018', 1, 69.90, '柔软撞色丝巾礼盒', 0.1300, '[\"https://ts2.tc.mm.bing.net/th/id/OIP-C.CKSBSB33LwT9qJD_Yhb1mwHaKk?w=132&h=108&c=7&qlt=90&bgcl=2e287d&r=0&o=6&dpr=1.3&pid=13.1\", \"https://tse1-mm.cn.bing.net/th/id/OIP-C.StrDRqennoZNbzSPZapKZwHaEJ?w=286&h=180&c=7&r=0&o=7&cb=thfc1falcon&dpr=1.3&pid=1.7&rm=3\"]', '细腻亲肤,点亮日常穿搭', '细腻亲肤,点亮日常穿搭。精选品质商品,适合积分商城兑换和日常福利采购场景。', 99.90, 'https://ts2.tc.mm.bing.net/th/id/OIP-C.CKSBSB33LwT9qJD_Yhb1mwHaKk?w=132&h=108&c=7&qlt=90&bgcl=2e287d&r=0&o=6&dpr=1.3&pid=13.1', 'fashionBag', '服饰鞋包', '礼盒精选', '2680件', 299, 18, 1, 0, '2026-07-28 09:43:03.479', '2026-07-28 09:43:03.479'); +INSERT INTO `supplier_product` VALUES (19, 'SKU019', 1, 49.90, '运动保温水杯', 0.1300, '[\"https://tse1-mm.cn.bing.net/th/id/OIP-C.StrDRqennoZNbzSPZapKZwHaEJ?w=286&h=180&c=7&r=0&o=7&cb=thfc1falcon&dpr=1.3&pid=1.7&rm=3\", \"https://tse3-mm.cn.bing.net/th/id/OIP-C.ynSetSr3z884UC6sEp4yiwHaEJ?w=331&h=186&c=7&r=0&o=7&cb=thfc1falcon&dpr=1.3&pid=1.7&rm=3\"]', '长效锁温,户外运动随行', '长效锁温,户外运动随行。精选品质商品,适合积分商城兑换和日常福利采购场景。', 79.90, 'https://tse1-mm.cn.bing.net/th/id/OIP-C.StrDRqennoZNbzSPZapKZwHaEJ?w=286&h=180&c=7&r=0&o=7&cb=thfc1falcon&dpr=1.3&pid=1.7&rm=3', 'sportsOutdoor', '运动户外', '热门推荐', '7342件', 306, 19, 1, 0, '2026-07-28 09:43:03.483', '2026-07-28 09:43:03.483'); +INSERT INTO `supplier_product` VALUES (20, 'SKU020', 1, 99.00, '筋膜放松按摩仪 Mini', 0.1300, '[\"https://tse3-mm.cn.bing.net/th/id/OIP-C.ynSetSr3z884UC6sEp4yiwHaEJ?w=331&h=186&c=7&r=0&o=7&cb=thfc1falcon&dpr=1.3&pid=1.7&rm=3\", \"https://ts3.tc.mm.bing.net/th/id/OIP-C.im_WvX9bWGyvagEZp4Pz0AHaGX?w=142&h=108&c=7&qlt=90&bgcl=a01e15&r=0&o=6&dpr=1.3&pid=13.1\"]', '轻巧便携,缓解久坐酸胀', '轻巧便携,缓解久坐酸胀。精选品质商品,适合积分商城兑换和日常福利采购场景。', 149.00, 'https://tse3-mm.cn.bing.net/th/id/OIP-C.ynSetSr3z884UC6sEp4yiwHaEJ?w=331&h=186&c=7&r=0&o=7&cb=thfc1falcon&dpr=1.3&pid=1.7&rm=3', 'sportsOutdoor', '运动户外', '积分兑换', '3921件', 313, 20, 1, 0, '2026-07-28 09:43:03.486', '2026-07-28 09:43:03.486'); +INSERT INTO `supplier_product` VALUES (21, 'SKU021', 1, 79.00, '户外露营灯', 0.1300, '[\"https://ts3.tc.mm.bing.net/th/id/OIP-C.im_WvX9bWGyvagEZp4Pz0AHaGX?w=142&h=108&c=7&qlt=90&bgcl=a01e15&r=0&o=6&dpr=1.3&pid=13.1\", \"https://ts3.tc.mm.bing.net/th/id/OIP-C.MkmCbLoY2S4zqzMeHvCDMQHaHD?w=128&h=108&c=7&qlt=90&bgcl=f61537&r=0&o=6&dpr=1.3&pid=13.1\"]', '多档照明,露营夜晚更安心', '多档照明,露营夜晚更安心。精选品质商品,适合积分商城兑换和日常福利采购场景。', 119.00, 'https://ts3.tc.mm.bing.net/th/id/OIP-C.im_WvX9bWGyvagEZp4Pz0AHaGX?w=142&h=108&c=7&qlt=90&bgcl=a01e15&r=0&o=6&dpr=1.3&pid=13.1', 'sportsOutdoor', '运动户外', '新品上架', '2568件', 320, 21, 1, 0, '2026-07-28 09:43:03.489', '2026-07-28 09:43:03.489'); +INSERT INTO `supplier_product` VALUES (22, 'SKU022', 1, 59.90, '悦享精选礼包', 0.1300, '[\"https://ts3.tc.mm.bing.net/th/id/OIP-C.MkmCbLoY2S4zqzMeHvCDMQHaHD?w=128&h=108&c=7&qlt=90&bgcl=f61537&r=0&o=6&dpr=1.3&pid=13.1\", \"https://ts2.tc.mm.bing.net/th/id/OIP-C.CKSBSB33LwT9qJD_Yhb1mwHaKk?w=132&h=108&c=7&qlt=90&bgcl=2e287d&r=0&o=6&dpr=1.3&pid=13.1\"]', '多场景精选好物,福利兑换优选', '多场景精选好物,福利兑换优选。精选品质商品,适合积分商城兑换和日常福利采购场景。', 89.90, 'https://ts3.tc.mm.bing.net/th/id/OIP-C.MkmCbLoY2S4zqzMeHvCDMQHaHD?w=128&h=108&c=7&qlt=90&bgcl=f61537&r=0&o=6&dpr=1.3&pid=13.1', 'recommend', '精选推荐', '精选推荐', '8820件', 327, 22, 1, 0, '2026-07-28 09:43:03.492', '2026-07-28 09:43:03.492'); +INSERT INTO `supplier_product` VALUES (23, 'SKU023', 1, 119.00, '品质生活组合装', 0.1300, '[\"https://ts2.tc.mm.bing.net/th/id/OIP-C.CKSBSB33LwT9qJD_Yhb1mwHaKk?w=132&h=108&c=7&qlt=90&bgcl=2e287d&r=0&o=6&dpr=1.3&pid=13.1\", \"https://tse1-mm.cn.bing.net/th/id/OIP-C.StrDRqennoZNbzSPZapKZwHaEJ?w=286&h=180&c=7&r=0&o=7&cb=thfc1falcon&dpr=1.3&pid=1.7&rm=3\"]', '居家、通勤、日用好物组合', '居家、通勤、日用好物组合。精选品质商品,适合积分商城兑换和日常福利采购场景。', 159.00, 'https://ts2.tc.mm.bing.net/th/id/OIP-C.CKSBSB33LwT9qJD_Yhb1mwHaKk?w=132&h=108&c=7&qlt=90&bgcl=2e287d&r=0&o=6&dpr=1.3&pid=13.1', 'recommend', '精选推荐', '热门推荐', '6902件', 334, 23, 1, 0, '2026-07-28 09:43:03.495', '2026-07-28 09:43:03.495'); +INSERT INTO `supplier_product` VALUES (24, 'SKU024', 1, 39.90, '积分兑换惊喜盒', 0.1300, '[\"https://tse1-mm.cn.bing.net/th/id/OIP-C.StrDRqennoZNbzSPZapKZwHaEJ?w=286&h=180&c=7&r=0&o=7&cb=thfc1falcon&dpr=1.3&pid=1.7&rm=3\", \"https://tse3-mm.cn.bing.net/th/id/OIP-C.ynSetSr3z884UC6sEp4yiwHaEJ?w=331&h=186&c=7&r=0&o=7&cb=thfc1falcon&dpr=1.3&pid=1.7&rm=3\"]', '随机精选商品,兑换更有新鲜感', '随机精选商品,兑换更有新鲜感。精选品质商品,适合积分商城兑换和日常福利采购场景。', 69.90, 'https://tse1-mm.cn.bing.net/th/id/OIP-C.StrDRqennoZNbzSPZapKZwHaEJ?w=286&h=180&c=7&r=0&o=7&cb=thfc1falcon&dpr=1.3&pid=1.7&rm=3', 'recommend', '精选推荐', '限时秒杀', '5310件', 341, 24, 1, 0, '2026-07-28 09:43:03.498', '2026-07-28 09:43:03.498'); + +-- ---------------------------- +-- Table structure for supplier_reconciliation_order +-- ---------------------------- +DROP TABLE IF EXISTS `supplier_reconciliation_order`; +CREATE TABLE `supplier_reconciliation_order` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '主键', + `supplierId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '供应商ID', + `orderId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '子订单ID', + `mainOrderId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '主订单ID', + `userId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户ID', + `reconciliationStatus` tinyint NOT NULL DEFAULT 0 COMMENT '对账状态:0未对账,1对账成功,2对账异常', + `diffType` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '差异类型', + `diffReason` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '差异说明', + `platformTotalPrice` decimal(12, 2) NULL DEFAULT NULL COMMENT '平台订单总金额', + `platformCashAmount` decimal(12, 2) NULL DEFAULT NULL COMMENT '平台现金支付金额', + `platformPointsPrice` decimal(12, 2) NULL DEFAULT NULL COMMENT '平台积分支付金额', + `platformRefundPrice` decimal(12, 2) NULL DEFAULT NULL COMMENT '平台退款金额', + `supplierTotalPrice` decimal(12, 2) NULL DEFAULT NULL COMMENT '供应商订单总金额', + `supplierCashAmount` decimal(12, 2) NULL DEFAULT NULL COMMENT '供应商现金支付金额', + `supplierPointsPrice` decimal(12, 2) NULL DEFAULT NULL COMMENT '供应商积分支付金额', + `supplierRefundPrice` decimal(12, 2) NULL DEFAULT NULL COMMENT '供应商退款金额', + `reconciliationBatchNo` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '对账批次号', + `reconciliationTime` datetime(3) NULL DEFAULT NULL COMMENT '对账完成时间', + `resultPayload` json NULL COMMENT '对账回调原始结果', + `createAt` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + `updateAt` datetime(3) NOT NULL, + `deleted` int NOT NULL DEFAULT 0, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `uk_supplier_order`(`supplierId` ASC, `orderId` ASC) USING BTREE, + INDEX `idx_supplier_status`(`supplierId` ASC, `reconciliationStatus` ASC) USING BTREE, + INDEX `idx_batch_no`(`reconciliationBatchNo` ASC) USING BTREE, + INDEX `idx_main_order`(`mainOrderId` ASC) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '供应商子订单对账结果表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of supplier_reconciliation_order +-- ---------------------------- + +-- ---------------------------- +-- Table structure for supplier_refund_exchange +-- ---------------------------- +DROP TABLE IF EXISTS `supplier_refund_exchange`; +CREATE TABLE `supplier_refund_exchange` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '售后记录主键ID', + `serviceOrderId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '退货/换货业务单号', + `supplierId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '供应商ID', + `userId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户ID', + `mainOrderId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '关联主单ID', + `subOrderId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '关联子单ID', + `serviceType` int NOT NULL COMMENT '售后类型:4退货,5换货', + `serviceState` int NOT NULL DEFAULT 0 COMMENT '售后状态:0已创建,1处理中,2已完成,3已取消', + `amount` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '售后金额', + `reason` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '售后原因', + `requestBody` json NULL COMMENT '原始业务请求体', + `createTime` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', + `updateTime` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间', + `deleted` int NOT NULL DEFAULT 0 COMMENT '删除状态:0未删除,1已删除', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `supplier_refund_exchange_serviceOrderId_key`(`serviceOrderId` ASC) USING BTREE, + INDEX `supplier_refund_exchange_supplierId_mainOrderId_idx`(`supplierId` ASC, `mainOrderId` ASC) USING BTREE, + INDEX `supplier_refund_exchange_subOrderId_idx`(`subOrderId` ASC) USING BTREE, + INDEX `supplier_refund_exchange_serviceType_serviceState_idx`(`serviceType` ASC, `serviceState` ASC) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '供应商退货换货售后记录表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of supplier_refund_exchange +-- ---------------------------- + +-- ---------------------------- +-- Table structure for supplier_user_address +-- ---------------------------- +DROP TABLE IF EXISTS `supplier_user_address`; +CREATE TABLE `supplier_user_address` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '地址主键ID', + `addressId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '地址业务ID', + `supplierId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '供应商ID', + `userId` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户ID', + `name` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '收货人姓名', + `tel` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '收货人手机号', + `province` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '省', + `city` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '市', + `county` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '区县', + `addressDetail` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '详细地址', + `areaCode` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '地区编码', + `postalCode` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '邮政编码', + `isDefault` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否默认地址', + `createTime` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', + `updateTime` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间', + `deleted` int NOT NULL DEFAULT 0 COMMENT '删除状态:0未删除,1已删除', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `supplier_user_address_addressId_key`(`addressId` ASC) USING BTREE, + INDEX `supplier_user_address_supplierId_userId_idx`(`supplierId` ASC, `userId` ASC) USING BTREE, + INDEX `supplier_user_address_supplierId_userId_isDefault_idx`(`supplierId` ASC, `userId` ASC, `isDefault` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '供应商用户收货地址表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of supplier_user_address +-- ---------------------------- +INSERT INTO `supplier_user_address` VALUES (1, 'addr-1785395646503', '99999999', 'b2397ac9a67f476c9833471d27709db1', '谌盼兮', '13545478393', '吉林省', '白山市', '临江市', '未来科技城', '220681', '430213', 1, '2026-07-30 07:14:06.541', '2026-07-30 07:29:34.829', 0); +INSERT INTO `supplier_user_address` VALUES (2, 'addr-1788252731181', '99999999', 'c281e124321c8db3a14f0a7c86046c3c', '谌盼兮', '19090230905', '湖北省', '武汉市', '洪山区', '中国地质大学', '420111', '430074', 1, '2026-09-01 08:52:11.226', '2026-09-01 08:52:11.226', 0); + +-- ---------------------------- +-- Table structure for user +-- ---------------------------- +DROP TABLE IF EXISTS `user`; +CREATE TABLE `user` ( + `id` int NOT NULL AUTO_INCREMENT, + `mobile` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `nickname` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, + `avatarUrl` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, + `points` int NOT NULL DEFAULT 0, + `createdAt` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + `updatedAt` datetime(3) NOT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `User_mobile_key`(`mobile` ASC) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of user +-- ---------------------------- + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/supplied-service/src/controllers/order.controller.ts b/supplied-service/src/controllers/order.controller.ts index e9de22b..b6ab69d 100644 --- a/supplied-service/src/controllers/order.controller.ts +++ b/supplied-service/src/controllers/order.controller.ts @@ -16,6 +16,8 @@ import { orderListService } from '../services/order-list.service.js' import { orderPushEventService, type OrderPushEventInput } from '../services/order-push-event.service.js' import { supplierOpenOrderService, + type SupplierInvoiceApplyRequest, + type SupplierInvoiceQueryRequest, type SupplierOrderQueryRequest, type SupplierReconciliationQueryRequest } from '../services/supplier-open-order.service.js' @@ -261,3 +263,37 @@ export const supplierReconciliationInfoController = withErrorBoundary(async (req return Response.json(await supplierOpenOrderService.queryReconciliationInfo(body)) }) + +export const supplierInvoiceApplyController = withErrorBoundary(async (request: Request, _context: Context) => { + const methodResponse = guardMethod(request, ['POST']) + if (methodResponse) return methodResponse + + let body: SupplierInvoiceApplyRequest + try { + body = supplierOpenOrderService.parseBody(await readJson(request)) as SupplierInvoiceApplyRequest + } catch (error) { + return Response.json({ + resultCode: 400, + message: error instanceof Error ? error.message : '请求参数解密失败' + }) + } + + return Response.json(await supplierOpenOrderService.applyForInvoices(body)) +}) + +export const supplierInvoiceInfoController = withErrorBoundary(async (request: Request, _context: Context) => { + const methodResponse = guardMethod(request, ['POST']) + if (methodResponse) return methodResponse + + let body: SupplierInvoiceQueryRequest + try { + body = supplierOpenOrderService.parseBody(await readJson(request)) as SupplierInvoiceQueryRequest + } catch (error) { + return Response.json({ + resultCode: 400, + message: error instanceof Error ? error.message : '请求参数解密失败' + }) + } + + return Response.json(await supplierOpenOrderService.queryInvoicesInfo(body)) +}) diff --git a/supplied-service/src/controllers/supplier.controller.ts b/supplied-service/src/controllers/supplier.controller.ts index e1452dd..efdcd1c 100644 --- a/supplied-service/src/controllers/supplier.controller.ts +++ b/supplied-service/src/controllers/supplier.controller.ts @@ -10,7 +10,7 @@ type SupplierInfoRequest = { } type SupplierTokenRequest = { - userId?: string + AplParmObjct?: string supplierIdentification?: string } @@ -39,10 +39,10 @@ export const supplierTokenController = withErrorBoundary(async (request: Request if (methodResponse) return methodResponse const body = await readJson(request) - const userId = String(body.userId || '').trim() + const encryptedParam = String(body.AplParmObjct || '').trim() const supplierIdentification = String(body.supplierIdentification || '').trim().toLowerCase() - return toHttpResponse(await supplierService.warmSupplierToken(userId, supplierIdentification)) + return toHttpResponse(await supplierService.warmSupplierToken(encryptedParam, supplierIdentification)) }) export const supplierPointsController = withErrorBoundary(async (request: Request, _context: Context) => { diff --git a/supplied-service/src/server.ts b/supplied-service/src/server.ts index 96ba5cd..5f45b8f 100644 --- a/supplied-service/src/server.ts +++ b/supplied-service/src/server.ts @@ -33,6 +33,8 @@ import { orderPayController, orderPushEventController, orderShipController, + supplierInvoiceApplyController, + supplierInvoiceInfoController, supplierOrderInfoController, supplierReconciliationInfoController } from './controllers/order.controller.js' @@ -107,6 +109,8 @@ const handlers: Record = { 'merchant-product-delete': merchantProductDeleteController, 'supplier-debug-tokens': supplierDebugTokensController, 'supplier-info': supplierInfoController, + 'supplier-invoice-apply': supplierInvoiceApplyController, + 'supplier-invoice-info': supplierInvoiceInfoController, 'supplier-order-info': supplierOrderInfoController, 'supplier-reconciliation-info': supplierReconciliationInfoController, 'supplier-reconciliation-result': supplierReconciliationResultController, @@ -200,6 +204,8 @@ router.all('/api/:name', async ctx => dispatch(ctx.params.name)(ctx)) router.all('/api/admin/:name', async ctx => dispatch(ctx.params.name)(ctx)) router.post('/supplierOpenApi/pointOrder/queryOrderInfo', dispatch('supplier-order-info')) router.post('/supplierOpenApi/pointOrder/queryReconciliationInfo', dispatch('supplier-reconciliation-info')) +router.post('/supplierOpenApi/pointOrder/applyForInvoices', dispatch('supplier-invoice-apply')) +router.post('/supplierOpenApi/pointOrder/queryInvoicesInfo', dispatch('supplier-invoice-info')) router.post('/supplierOpenApi/pointOrder/reconciliationResult', dispatch('supplier-reconciliation-result')) router.post('/supplierOpenApi/pointOrder/unReconciledMsg', dispatch('supplier-unreconciled-msg')) router.post('/supplierOpenApi/debug/tokens', dispatch('supplier-debug-tokens')) diff --git a/supplied-service/src/services/supplier-open-order.service.ts b/supplied-service/src/services/supplier-open-order.service.ts index 6799db5..c40b5d4 100644 --- a/supplied-service/src/services/supplier-open-order.service.ts +++ b/supplied-service/src/services/supplier-open-order.service.ts @@ -2,6 +2,7 @@ import { supplierDao } from '../dao/supplier.dao.js' import { orderDao } from '../dao/order.dao.js' import { ORDER_EVENT_TYPE, ORDER_STATE } from '../constants/order.js' import { logger } from '../utils/logger.js' +import { prisma } from '../utils/prisma.js' import { sm4CbcDecrypt, verifySupplierOpenSignature } from '../utils/supplier-open-crypto.js' export type SupplierOrderQueryRequest = { @@ -11,6 +12,7 @@ export type SupplierOrderQueryRequest = { orderId?: string timestamp?: number AplParmObjct?: string + RsrvFld1?: string } export type SupplierReconciliationQueryRequest = { @@ -24,10 +26,51 @@ export type SupplierReconciliationQueryRequest = { pageSize?: number timestamp?: number AplParmObjct?: string + RsrvFld1?: string } -const parseBody = (body: SupplierOrderQueryRequest) => { - if (!body.AplParmObjct) return body +export type SupplierInvoiceApplyRequest = { + supplierId?: string + token?: string + settlementId?: string + orderIds?: string + orderNum?: number + orderTotalPrice?: number | string + invoiceDate?: string + invoiceTitle?: string + invoiceCode?: string + invoiceAddress?: string + invoiceContact?: string + invoiceNakedPrice?: number | string + invoiceTaxPrice?: number | string + invoiceType?: string + invoiceContent?: string + remark?: string + bankAccount?: string + registeredAddress?: string + bankName?: string + timestamp?: number + AplParmObjct?: string + RsrvFld1?: string +} + +export type SupplierInvoiceQueryRequest = { + supplierId?: string + token?: string + settlementId?: string + timestamp?: number + AplParmObjct?: string +} + +type SupplierOpenEncryptedRequest = { + AplParmObjct?: string + ApiParamObject?: string + RsrvFld1?: string +} + +const parseBody = (body: T) => { + const encryptedData = body.AplParmObjct || body.ApiParamObject + if (!encryptedData) return body const keyHex = process.env.SUPPLIER_OPEN_SM4_KEY_HEX || process.env.SM4CBC_KEY_HEX || '' const ivHex = process.env.SUPPLIER_OPEN_SM4_IV_HEX || process.env.SM4CBC_IV_HEX || '' @@ -35,7 +78,10 @@ const parseBody = (body: SupplierOrderQueryRequest) => { throw new Error('供应商开放接口SM4配置缺失') } - return JSON.parse(sm4CbcDecrypt(body.AplParmObjct, keyHex, ivHex)) as SupplierOrderQueryRequest + return { + ...JSON.parse(sm4CbcDecrypt(encryptedData, keyHex, ivHex)), + RsrvFld1: body.RsrvFld1 + } as T } const toText = (date?: Date | null) => { @@ -223,5 +269,160 @@ export const supplierOpenOrderService = { })) return { resultCode: 0, message: '成功', totalPage, currentPage: page, orderInfos } + }, + + async applyForInvoices(body: SupplierInvoiceApplyRequest) { + const supplierId = String(body.supplierId || '').trim() + const token = String(body.token || '').trim() + const settlementId = String(body.settlementId || '').trim() + const orderIds = [...new Set(String(body.orderIds || '') + .split(',') + .map(item => item.trim()) + .filter(Boolean))] + const orderNum = Number(body.orderNum || 0) + const orderTotalPrice = Number(body.orderTotalPrice) + const timestamp = Number(body.timestamp || 0) + + logger.info('supplier open invoice apply', { supplierId, settlementId, orderCount: orderIds.length, timestamp }) + + if (!supplierId || !token || !timestamp || !settlementId || !orderIds.length) { + return { resultCode: 400, message: '供应商、结算单、订单或签名信息不能为空' } + } + if (!body.invoiceDate || !body.invoiceTitle || !body.invoiceCode || !body.invoiceAddress || !body.invoiceContact) { + return { resultCode: 400, message: '开票日期、抬头、纳税人识别号、地址和联系电话不能为空' } + } + if (!Number.isInteger(orderNum) || orderNum <= 0 || !Number.isFinite(orderTotalPrice) || orderTotalPrice < 0) { + return { resultCode: 400, message: '订单数量或订单总金额不正确' } + } + + const supplier = await supplierDao.findActiveBySupplierId(supplierId) + if (!supplier) { + return { resultCode: 404, message: '供应商信息不存在' } + } + if (!verifySupplierOpenSignature(supplier.supplierId, String(body.RsrvFld1 || supplier.clientId), timestamp, token)) { + return { resultCode: 401, message: '签名无效或已过期' } + } + + const orders = await orderDao.findOpenApiSubOrders(supplierId, orderIds) + if (orders.length !== orderIds.length || orderNum !== orderIds.length) { + return { resultCode: 400, message: '订单数量校验不通过' } + } + + const actualTotalPrice = orders.reduce((total, order) => total + Number(order.totalPrice || 0), 0) + if (Math.abs(actualTotalPrice - orderTotalPrice) >= 0.01) { + return { resultCode: 400, message: '订单总金额校验不通过' } + } + + const orderIdText = orderIds.join(',') + const firstOrder = orders[0] + await orderDao.transaction(async tx => { + await tx.supplierInvoiceInfo.upsert({ + where: { invoiceBizId: settlementId }, + update: { + supplierId, + userId: firstOrder?.userId || null, + mainOrderId: firstOrder?.mainOrderId || null, + subOrderId: firstOrder?.orderId || null, + settlementId, + invoiceCode: body.invoiceCode, + orderIds: orderIdText, + invoiceStatus: 1, + requestBody: JSON.parse(JSON.stringify(body)), + deleted: 0 + }, + create: { + invoiceBizId: settlementId, + supplierId, + userId: firstOrder?.userId || null, + mainOrderId: firstOrder?.mainOrderId || null, + subOrderId: firstOrder?.orderId || null, + settlementId, + invoiceCode: body.invoiceCode, + orderIds: orderIdText, + invoiceStatus: 1, + requestBody: JSON.parse(JSON.stringify(body)) + } + }) + }) + + return { resultCode: 0, message: '开票申请已受理' } + }, + + async queryInvoicesInfo(body: SupplierInvoiceQueryRequest) { + const supplierId = String(body.supplierId || '').trim() + const token = String(body.token || '').trim() + const settlementId = String(body.settlementId || '').trim() + const timestamp = Number(body.timestamp || 0) + + if (!supplierId || !token || !timestamp || !settlementId) { + return { resultCode: 400, message: '供应商、结算单或签名信息不能为空' } + } + + const supplier = await supplierDao.findActiveBySupplierId(supplierId) + if (!supplier) { + return { resultCode: 404, message: '供应商信息不存在' } + } + if (!verifySupplierOpenSignature(supplier.supplierId, supplier.clientId, timestamp, token)) { + return { resultCode: 401, message: '签名无效或已过期' } + } + + const records = await prisma.supplierInvoiceInfo.findMany({ + where: { supplierId, settlementId, deleted: 0 }, + orderBy: { createTime: 'asc' } + }) + const completedRecords = records.filter(item => item.invoiceStatus === 2) + const failedRecords = records.filter(item => item.invoiceStatus === 3) + const application = records.find(item => item.invoiceStatus === 1) + const applicationOrderIds = String(application?.orderIds || '') + const invoiceInfos = completedRecords.flatMap(record => { + const payload = record.requestBody && typeof record.requestBody === 'object' + ? record.requestBody as Record + : {} + const invoiceIds = String(record.invoiceIds || record.invoiceBizId) + .split(',') + .map(item => item.trim()) + .filter(Boolean) + + return invoiceIds.map(invoiceId => ({ + invoiceId, + invoiceCode: record.invoiceCode || '', + invoiceDate: payload.invoiceDate || null, + invoiceNakeAmount: Number(payload.invoiceNakeAmount || payload.invoiceNakedPrice || 0), + invoiceTaxRate: Number(payload.invoiceTaxRate || 0), + invoiceTaxAmount: Number(payload.invoiceTaxAmount || payload.invoiceTaxPrice || 0), + invoiceAmount: Number(payload.invoiceAmount || 0), + invoiceType: Number(payload.invoiceType || 0), + url: payload.url || record.filePath || '', + imageEncode: payload.imageEncode || '', + fileType: payload.fileType || '', + checkCode: payload.checkCode || '', + invoiceAddress: payload.invoiceAddress || '' + })) + }) + + const completedOrderIds = completedRecords + .flatMap(item => String(item.orderIds || '').split(',')) + .map(item => item.trim()) + .filter(Boolean) + const sucOrderIds = completedRecords.length && !completedOrderIds.length + ? applicationOrderIds.split(',').map(item => item.trim()).filter(Boolean) + : completedOrderIds + const failOrderIds = failedRecords.length ? applicationOrderIds : '' + const failMsg = failedRecords.length + ? String((failedRecords[0].requestBody as Record | null)?.failMsg || '开票失败') + : completedRecords.length ? '' : '发票尚未开具' + + return { + resultCode: 0, + message: '成功', + getInvoiceInfo: { + bSuccess: completedRecords.length ? 0 : 1, + settlementId, + sucOrderIds: [...new Set(sucOrderIds)].join(','), + failOrderIds, + failMsg, + invoiceInfos + } + } } } diff --git a/supplied-service/src/services/supplier.service.ts b/supplied-service/src/services/supplier.service.ts index e2d00b3..c55fe90 100644 --- a/supplied-service/src/services/supplier.service.ts +++ b/supplied-service/src/services/supplier.service.ts @@ -2,7 +2,7 @@ import { supplierDao } from '../dao/supplier.dao.js' import { bridgeFetchWithAuth, findSupplierByIdentification, getBridgeApiBase, getBridgeToken, warmBridgeToken } from '../utils/bridge-auth.js' import { sha256 } from '../utils/crypto.js' import { logger } from '../utils/logger.js' -import { createSupplierOpenSignature } from '../utils/supplier-open-crypto.js' +import { createSupplierOpenSignature, sm4CbcDecrypt } from '../utils/supplier-open-crypto.js' import { serviceFail, serviceOk } from '../types/service-result.js' type BridgePointsResponse = { @@ -33,11 +33,29 @@ export const supplierService = { }) }, - async warmSupplierToken(userId: string, supplierIdentification: string) { - logger.info('supplier token warmup', { userId, supplierIdentification }) + async warmSupplierToken(encryptedParam: string, supplierIdentification: string) { + logger.info('supplier token warmup', { supplierIdentification }) - if (!userId || !supplierIdentification) { - return serviceFail('用户信息和供应商唯一标识不能为空') + if (!encryptedParam || !supplierIdentification) { + return serviceFail('加密用户参数和供应商唯一标识不能为空') + } + + const keyHex = process.env.SUPPLIER_OPEN_SM4_KEY_HEX || process.env.SM4CBC_KEY_HEX || '' + const ivHex = process.env.SUPPLIER_OPEN_SM4_IV_HEX || process.env.SM4CBC_IV_HEX || '' + if (!keyHex || !ivHex) { + return serviceFail('供应商开放接口SM4配置缺失', 500, 'SM4_CONFIG_MISSING') + } + + let userId = '' + try { + const decrypted = sm4CbcDecrypt(encryptedParam.replace(/ /g, '+'), keyHex, ivHex) + const params = JSON.parse(decrypted) as { userId?: string } + userId = String(params.userId || '').trim() + } catch (error) { + return serviceFail('用户参数解密失败', 400, 'INVALID_USER_PARAM') + } + if (!userId) { + return serviceFail('用户信息不能为空', 400, 'USER_ID_MISSING') } const supplier = await findSupplierByIdentification(supplierIdentification).catch(error => error) diff --git a/supplied-service/tools/mock-invoice-pdf.js b/supplied-service/tools/mock-invoice-pdf.js new file mode 100644 index 0000000..f7250c3 --- /dev/null +++ b/supplied-service/tools/mock-invoice-pdf.js @@ -0,0 +1,359 @@ +import { createHash } from 'node:crypto' +import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs' +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +const PAGE_WIDTH = 842 +const PAGE_HEIGHT = 595 + +const findFont = requestedPath => { + const candidates = [ + requestedPath, + process.env.INVOICE_PDF_FONT_PATH, + 'C:/Windows/Fonts/simhei.ttf', + 'C:/Windows/Fonts/msyh.ttf', + '/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttf', + '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf' + ].filter(Boolean) + const fontPath = candidates.find(candidate => existsSync(candidate)) + if (!fontPath) { + throw new Error('未找到可用字体,请通过 INVOICE_PDF_FONT_PATH 指定支持中文的 TTF 字体') + } + if (path.extname(fontPath).toLowerCase() !== '.ttf') { + throw new Error('模拟发票工具仅支持 TTF 字体') + } + return fontPath +} + +const readTableDirectory = font => { + const tableCount = font.readUInt16BE(4) + const tables = new Map() + for (let index = 0; index < tableCount; index += 1) { + const offset = 12 + index * 16 + tables.set(font.toString('ascii', offset, offset + 4), { + offset: font.readUInt32BE(offset + 8), + length: font.readUInt32BE(offset + 12) + }) + } + return tables +} + +const createGlyphMapper = font => { + const cmapTable = readTableDirectory(font).get('cmap') + if (!cmapTable) throw new Error('字体缺少 cmap 字符映射表') + const base = cmapTable.offset + const count = font.readUInt16BE(base + 2) + let format12Offset = null + let format4Offset = null + + for (let index = 0; index < count; index += 1) { + const record = base + 4 + index * 8 + const platformId = font.readUInt16BE(record) + const encodingId = font.readUInt16BE(record + 2) + const subtableOffset = base + font.readUInt32BE(record + 4) + const format = font.readUInt16BE(subtableOffset) + if (format === 12 && (platformId === 0 || (platformId === 3 && encodingId === 10))) { + format12Offset = subtableOffset + } + if (format === 4 && (platformId === 0 || platformId === 3)) { + format4Offset = subtableOffset + } + } + + const fromFormat12 = codePoint => { + if (format12Offset === null) return 0 + const groupCount = font.readUInt32BE(format12Offset + 12) + let low = 0 + let high = groupCount - 1 + while (low <= high) { + const middle = (low + high) >> 1 + const group = format12Offset + 16 + middle * 12 + const start = font.readUInt32BE(group) + const end = font.readUInt32BE(group + 4) + if (codePoint < start) high = middle - 1 + else if (codePoint > end) low = middle + 1 + else return font.readUInt32BE(group + 8) + codePoint - start + } + return 0 + } + + const fromFormat4 = codePoint => { + if (format4Offset === null || codePoint > 0xffff) return 0 + const segmentCount = font.readUInt16BE(format4Offset + 6) / 2 + const endCodes = format4Offset + 14 + const startCodes = endCodes + segmentCount * 2 + 2 + const deltas = startCodes + segmentCount * 2 + const rangeOffsets = deltas + segmentCount * 2 + for (let index = 0; index < segmentCount; index += 1) { + const end = font.readUInt16BE(endCodes + index * 2) + if (codePoint > end) continue + const start = font.readUInt16BE(startCodes + index * 2) + if (codePoint < start) return 0 + const delta = font.readInt16BE(deltas + index * 2) + const rangeOffsetPosition = rangeOffsets + index * 2 + const rangeOffset = font.readUInt16BE(rangeOffsetPosition) + if (rangeOffset === 0) return (codePoint + delta) & 0xffff + const glyphPosition = rangeOffsetPosition + rangeOffset + (codePoint - start) * 2 + if (glyphPosition + 2 > font.length) return 0 + const glyph = font.readUInt16BE(glyphPosition) + return glyph === 0 ? 0 : (glyph + delta) & 0xffff + } + return 0 + } + + return codePoint => fromFormat12(codePoint) || fromFormat4(codePoint) +} + +const utf16Hex = value => { + const buffer = Buffer.from(value, 'utf16le') + buffer.swap16() + return buffer.toString('hex').toUpperCase() +} + +const createPdfWriter = () => { + const objects = [] + return { + add(body) { + objects.push(Buffer.isBuffer(body) ? body : Buffer.from(body, 'binary')) + return objects.length + }, + save(outputPath) { + const header = Buffer.from('%PDF-1.7\n%\xFF\xFF\xFF\xFF\n', 'binary') + const chunks = [header] + const offsets = [0] + let cursor = header.length + objects.forEach((body, index) => { + offsets.push(cursor) + const prefix = Buffer.from(`${index + 1} 0 obj\n`, 'ascii') + const suffix = Buffer.from('\nendobj\n', 'ascii') + chunks.push(prefix, body, suffix) + cursor += prefix.length + body.length + suffix.length + }) + const xrefOffset = cursor + const xref = [`xref\n0 ${objects.length + 1}\n`, '0000000000 65535 f \n'] + for (let index = 1; index <= objects.length; index += 1) { + xref.push(`${String(offsets[index]).padStart(10, '0')} 00000 n \n`) + } + xref.push(`trailer\n<< /Size ${objects.length + 1} /Root 1 0 R >>\nstartxref\n${xrefOffset}\n%%EOF\n`) + chunks.push(Buffer.from(xref.join(''), 'ascii')) + mkdirSync(path.dirname(outputPath), { recursive: true }) + writeFileSync(outputPath, Buffer.concat(chunks)) + } + } +} + +const streamObject = (dictionary, data) => Buffer.concat([ + Buffer.from(`<< ${dictionary} /Length ${data.length} >>\nstream\n`, 'ascii'), + data, + Buffer.from('\nendstream', 'ascii') +]) + +const money = value => Number(value || 0).toFixed(2) + +const chineseAmount = value => { + const digits = '零壹贰叁肆伍陆柒捌玖' + const units = ['', '拾', '佰', '仟', '万', '拾', '佰', '仟', '亿', '拾', '佰', '仟'] + const amount = Math.max(Number(value || 0), 0) + const integer = Math.floor(amount) + const chars = String(integer).split('').reverse() + let result = '' + let pendingZero = false + chars.forEach((char, index) => { + const digit = Number(char) + if (digit === 0) { + pendingZero = result.length > 0 + if (index === 4 || index === 8) result = units[index] + result + } else { + result = `${digits[digit]}${units[index]}${pendingZero ? '零' : ''}${result}` + pendingZero = false + } + }) + const cents = Math.round((amount - integer) * 100) + const jiao = Math.floor(cents / 10) + const fen = cents % 10 + return `${result || '零'}元${jiao ? `${digits[jiao]}角` : ''}${fen ? `${digits[fen]}分` : '整'}` +} + +const normalizeInvoice = input => { + const digest = createHash('sha256').update(JSON.stringify(input)).digest('hex').toUpperCase() + const total = Number(input.orderTotalPrice || 0) + const taxRate = Number(input.taxRate ?? 0.13) + const naked = Number(input.invoiceNakedPrice ?? total / (1 + taxRate)) + const tax = Number(input.invoiceTaxPrice ?? total - naked) + return { + ...input, + invoiceDate: input.invoiceDate || new Date().toISOString().slice(0, 10), + invoiceNumber: input.invoiceNumber || digest.slice(0, 20), + checkCode: input.checkCode || digest.slice(20, 40), + sellerName: input.sellerName || '模拟积分商城供应商', + sellerTaxId: input.sellerTaxId || input.supplierId || 'SIMULATED-SUPPLIER', + sellerAddress: input.sellerAddress || '模拟供应商注册地址', + sellerContact: input.sellerContact || '400-000-0000', + invoiceContent: input.invoiceContent || '积分商城商品', + orderNum: Number(input.orderNum || 1), + total, + naked, + tax, + taxRate + } +} + +export const generateMockInvoicePdf = (invoiceInput, outputPath, options = {}) => { + const invoice = normalizeInvoice(invoiceInput) + const fontPath = findFont(options.fontPath) + const font = readFileSync(fontPath) + const glyphFor = createGlyphMapper(font) + const glyphUnicode = new Map() + const commands = [] + + const encode = value => Array.from(String(value ?? '')).map(character => { + const glyph = glyphFor(character.codePointAt(0)) + if (glyph && !glyphUnicode.has(glyph)) glyphUnicode.set(glyph, character) + return glyph.toString(16).padStart(4, '0') + }).join('').toUpperCase() + const text = (value, x, y, size = 10, color = '0 0 0') => { + commands.push(`BT /F1 ${size} Tf ${color} rg 1 0 0 1 ${x} ${y} Tm <${encode(value)}> Tj ET`) + } + const line = (x1, y1, x2, y2, width = 0.7, color = '0.45 0.12 0.12') => { + commands.push(`${color} RG ${width} w ${x1} ${y1} m ${x2} ${y2} l S`) + } + const rect = (x, y, width, height, color = '0.45 0.12 0.12') => { + commands.push(`${color} RG 0.7 w ${x} ${y} ${width} ${height} re S`) + } + const wrappedText = (value, x, y, maxChars, size = 9, lineHeight = 13) => { + const characters = Array.from(String(value || '')) + for (let index = 0; index < characters.length; index += maxChars) { + text(characters.slice(index, index + maxChars).join(''), x, y - index / maxChars * lineHeight, size) + } + } + + commands.push('1 1 1 rg 0 0 842 595 re f') + text('增值税电子普通发票(模拟)', 280, 548, 22, '0.65 0.05 0.05') + line(274, 540, 568, 540, 1.2, '0.65 0.05 0.05') + text('仅供系统联调测试,禁止报销', 302, 522, 11, '0.75 0.1 0.1') + + text(`发票号码:${invoice.invoiceNumber}`, 610, 555, 9) + text(`开票日期:${invoice.invoiceDate}`, 610, 538, 9) + text(`校验码:${invoice.checkCode}`, 610, 521, 9) + text(`结算单号:${invoice.settlementId || ''}`, 50, 521, 9) + + rect(42, 188, 758, 320) + line(42, 414, 800, 414) + line(42, 320, 800, 320) + line(42, 280, 800, 280) + line(42, 226, 800, 226) + line(120, 414, 120, 508) + line(120, 320, 120, 414) + line(475, 414, 475, 508) + line(475, 320, 475, 414) + + text('购买方', 68, 457, 12, '0.55 0.05 0.05') + text('销售方', 68, 365, 12, '0.55 0.05 0.05') + text('名称:', 132, 488, 9) + wrappedText(invoice.invoiceTitle, 180, 488, 26, 9) + text('纳税人识别号:', 132, 464, 9) + text(invoice.invoiceCode, 235, 464, 9) + text('地址、电话:', 487, 488, 9) + wrappedText(`${invoice.invoiceAddress || ''} ${invoice.invoiceContact || ''}`, 574, 488, 20, 8) + text('开户行及账号:', 487, 448, 9) + wrappedText(`${invoice.bankName || ''} ${invoice.bankAccount || ''}`, 574, 448, 20, 8) + + text('名称:', 132, 395, 9) + text(invoice.sellerName, 180, 395, 9) + text('纳税人识别号:', 132, 371, 9) + text(invoice.sellerTaxId, 235, 371, 9) + text('地址、电话:', 487, 395, 9) + wrappedText(`${invoice.sellerAddress} ${invoice.sellerContact}`, 574, 395, 20, 8) + text('开户行及账号:', 487, 350, 9) + wrappedText(`${invoice.sellerBankName || '模拟银行'} ${invoice.sellerBankAccount || '0000000000000000'}`, 574, 350, 20, 8) + + const columns = [42, 290, 382, 440, 495, 580, 655, 720, 800] + columns.slice(1, -1).forEach(x => line(x, 226, x, 320)) + ;['货物或应税劳务、服务名称', '规格型号', '单位', '数量', '单价', '金额', '税率', '税额'].forEach((label, index) => { + text(label, columns[index] + 7, 300, index === 0 ? 9 : 8) + }) + wrappedText(`*商品*${invoice.invoiceContent}`, 50, 263, 28, 9) + text(invoice.invoiceType || '电子发票', 300, 263, 8) + text('项', 401, 263, 9) + text(invoice.orderNum, 458, 263, 9) + text(money(invoice.naked / invoice.orderNum), 503, 263, 8) + text(money(invoice.naked), 590, 263, 8) + text(`${(invoice.taxRate * 100).toFixed(0)}%`, 674, 263, 8) + text(money(invoice.tax), 730, 263, 8) + + text('合计', 240, 203, 10) + text(`¥${money(invoice.naked)}`, 590, 203, 10) + text(`¥${money(invoice.tax)}`, 730, 203, 10) + text(`价税合计(大写):${chineseAmount(invoice.total)}`, 52, 164, 11) + text(`(小写):¥${money(invoice.total)}`, 600, 164, 11) + text(`订单号:${invoice.orderIds || ''}`, 52, 132, 8) + wrappedText(`备注:${invoice.remark || '模拟开票,仅用于开发与联调测试'}`, 52, 111, 70, 8) + text('收款人:模拟收款员', 52, 65, 9) + text('复核:模拟复核员', 285, 65, 9) + text('开票人:模拟开票员', 510, 65, 9) + text('销售方(章)', 682, 65, 9, '0.7 0.05 0.05') + + commands.push('q 0.92 0.92 0.92 rg BT /F1 34 Tf 0.866 0.5 -0.5 0.866 235 170 Tm') + commands.push(`<${encode('模拟发票 禁止报销')}> Tj ET Q`) + commands.push('0.75 0.08 0.08 RG 2 w 798 88 m 798 111.196 779.196 130 756 130 c 732.804 130 714 111.196 714 88 c 714 64.804 732.804 46 756 46 c 779.196 46 798 64.804 798 88 c S') + + const content = Buffer.from(commands.join('\n'), 'ascii') + const toUnicodeEntries = [...glyphUnicode.entries()].map(([glyph, character]) => + `<${glyph.toString(16).padStart(4, '0').toUpperCase()}> <${utf16Hex(character)}>`) + const toUnicodeBlocks = [] + for (let index = 0; index < toUnicodeEntries.length; index += 100) { + const entries = toUnicodeEntries.slice(index, index + 100) + toUnicodeBlocks.push(`${entries.length} beginbfchar`, ...entries, 'endbfchar') + } + const toUnicode = Buffer.from([ + '/CIDInit /ProcSet findresource begin', '12 dict begin', 'begincmap', + '/CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def', + '/CMapName /MockInvoiceUnicode def', '/CMapType 2 def', + '1 begincodespacerange', '<0000> ', 'endcodespacerange', + ...toUnicodeBlocks, + 'endcmap', 'CMapName currentdict /CMap defineresource pop', 'end', 'end' + ].join('\n'), 'ascii') + + const writer = createPdfWriter() + writer.add('<< /Type /Catalog /Pages 2 0 R >>') + writer.add('<< /Type /Pages /Kids [3 0 R] /Count 1 >>') + writer.add(`<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${PAGE_WIDTH} ${PAGE_HEIGHT}] /Resources << /Font << /F1 5 0 R >> >> /Contents 4 0 R >>`) + writer.add(streamObject('', content)) + writer.add('<< /Type /Font /Subtype /Type0 /BaseFont /MockInvoiceFont /Encoding /Identity-H /DescendantFonts [6 0 R] /ToUnicode 9 0 R >>') + writer.add('<< /Type /Font /Subtype /CIDFontType2 /BaseFont /MockInvoiceFont /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> /FontDescriptor 7 0 R /DW 1000 /CIDToGIDMap /Identity >>') + writer.add('<< /Type /FontDescriptor /FontName /MockInvoiceFont /Flags 4 /FontBBox [-1000 -1000 3000 3000] /ItalicAngle 0 /Ascent 1000 /Descent -300 /CapHeight 800 /StemV 80 /FontFile2 8 0 R >>') + writer.add(streamObject(`/Length1 ${font.length}`, font)) + writer.add(streamObject('', toUnicode)) + writer.save(outputPath) + return outputPath +} + +const parseArguments = argv => { + const options = {} + for (let index = 0; index < argv.length; index += 1) { + if (argv[index] === '--input') options.input = argv[++index] + else if (argv[index] === '--data') options.data = argv[++index] + else if (argv[index] === '--output') options.output = argv[++index] + else if (argv[index] === '--font') options.font = argv[++index] + } + return options +} + +if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { + try { + const options = parseArguments(process.argv.slice(2)) + if (!options.input && !options.data) { + throw new Error('请使用 --input 或 --data 传入开票信息') + } + const input = options.input + ? JSON.parse(readFileSync(path.resolve(options.input), 'utf8')) + : JSON.parse(options.data) + const defaultName = `mock-invoice-${String(input.settlementId || Date.now()).replace(/[^a-zA-Z0-9_-]/g, '_')}.pdf` + const outputPath = path.resolve(options.output || path.join('output', 'pdf', defaultName)) + generateMockInvoicePdf(input, outputPath, { fontPath: options.font }) + console.log(outputPath) + } catch (error) { + console.error(error instanceof Error ? error.message : String(error)) + process.exitCode = 1 + } +}