import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import { DataSource } from 'typeorm'; import { IntegrationTestDatabase, setupIntegrationDatabaseHooksWithEntities } from '@d8d/shared-test-util'; import { UserEntityMt, RoleMt } from '@d8d/user-module-mt'; import { DeliveryAddressMt } from '@d8d/delivery-address-module-mt'; import { AreaEntityMt } from '@d8d/geo-areas-mt'; import { MerchantMt } from '@d8d/merchant-module-mt'; import { SupplierMt } from '@d8d/supplier-module-mt'; import { FileMt } from '@d8d/file-module-mt'; import { GoodsMt, GoodsCategoryMt } from '@d8d/goods-module-mt'; import { OrderMt, OrderGoodsMt, OrderRefundMt } from '../../src/entities'; import { OrderTimeoutSchedulerService } from '../../src/services/order-timeout-scheduler.service'; import { OrdersTestFactory } from '../factories/orders-test-factory'; // 设置集成测试钩子 setupIntegrationDatabaseHooksWithEntities([ UserEntityMt, RoleMt, OrderMt, OrderGoodsMt, OrderRefundMt, DeliveryAddressMt, MerchantMt, SupplierMt, FileMt, AreaEntityMt, GoodsMt, GoodsCategoryMt ]) describe('订单超时调度器集成测试', () => { let dataSource: DataSource; let schedulerService: OrderTimeoutSchedulerService; let testFactory: OrdersTestFactory; let testUser: UserEntityMt; beforeEach(async () => { // 获取数据源 dataSource = await IntegrationTestDatabase.getDataSource(); // 创建测试工厂 testFactory = new OrdersTestFactory(dataSource); // 创建测试用户 testUser = await testFactory.createTestUser(1); // 创建调度器服务(特定租户) schedulerService = new OrderTimeoutSchedulerService(dataSource, 1); }); afterEach(async () => { // 清理测试数据 await testFactory.cleanup(); }); describe('超时订单检测逻辑', () => { it('应该正确识别24小时前创建的未支付订单为超时订单', async () => { // 创建一个未支付订单 const testOrder = await testFactory.createTestOrder(testUser.id, { tenantId: 1, payState: 0, // 未支付 state: 0, // 未发货 expireTime: null // 无过期时间 }); // 更新订单创建时间为25小时前 const twentyFiveHoursAgo = new Date(); twentyFiveHoursAgo.setHours(twentyFiveHoursAgo.getHours() - 25); const orderRepository = dataSource.getRepository(OrderMt); await orderRepository.update( { id: testOrder.id, tenantId: 1 }, { createdAt: twentyFiveHoursAgo } ); // 手动触发处理以检查是否能检测到超时订单 const result = await schedulerService.triggerManualProcess(1); // 验证处理结果 expect(result.success).toBe(true); expect(result.processedOrders).toBeGreaterThan(0); expect(result.message).toContain('成功处理'); // 验证订单状态已更新为关闭 const updatedOrder = await orderRepository.findOne({ where: { id: testOrder.id, tenantId: 1 } }); expect(updatedOrder).toBeDefined(); expect(updatedOrder!.payState).toBe(5); // 订单关闭 expect(updatedOrder!.cancelReason).toBe('订单超时未支付,系统自动取消'); expect(updatedOrder!.cancelTime).toBeDefined(); }); it('不应该识别23小时前创建的未支付订单为超时订单', async () => { // 创建一个23小时前创建的未支付订单(不到24小时) const twentyThreeHoursAgo = new Date(); twentyThreeHoursAgo.setHours(twentyThreeHoursAgo.getHours() - 23); await testFactory.createTestOrder(testUser.id, { tenantId: 1, createdAt: twentyThreeHoursAgo, payState: 0, // 未支付 state: 0 // 未发货 }); // 手动触发处理 const result = await schedulerService.triggerManualProcess(1); // 验证没有处理任何订单 expect(result.success).toBe(true); expect(result.processedOrders).toBe(0); }); it('不应该识别已支付的订单为超时订单', async () => { // 创建一个25小时前创建的已支付订单 const twentyFiveHoursAgo = new Date(); twentyFiveHoursAgo.setHours(twentyFiveHoursAgo.getHours() - 25); const testOrder = await testFactory.createTestOrder(testUser.id, { tenantId: 1, createdAt: twentyFiveHoursAgo, payState: 2, // 已支付 state: 0 // 未发货 }); // 手动触发处理 const result = await schedulerService.triggerManualProcess(1); // 验证没有处理已支付订单 expect(result.success).toBe(true); expect(result.processedOrders).toBe(0); // 验证订单状态未改变 const orderRepository = dataSource.getRepository(OrderMt); const updatedOrder = await orderRepository.findOne({ where: { id: testOrder.id, tenantId: 1 } }); expect(updatedOrder).toBeDefined(); expect(updatedOrder!.payState).toBe(2); // 保持已支付状态 }); it('不应该识别已发货的订单为超时订单', async () => { // 创建一个25小时前创建的未支付但已发货的订单 const twentyFiveHoursAgo = new Date(); twentyFiveHoursAgo.setHours(twentyFiveHoursAgo.getHours() - 25); const testOrder = await testFactory.createTestOrder(testUser.id, { tenantId: 1, createdAt: twentyFiveHoursAgo, payState: 0, // 未支付 state: 1 // 已发货 }); // 手动触发处理 const result = await schedulerService.triggerManualProcess(1); // 验证没有处理已发货订单 expect(result.success).toBe(true); expect(result.processedOrders).toBe(0); }); it('应该考虑订单过期时间(expireTime)', async () => { // 创建一个25小时前创建的订单,已设置过期时间为10小时前 const twentyFiveHoursAgo = new Date(); twentyFiveHoursAgo.setHours(twentyFiveHoursAgo.getHours() - 25); const tenHoursAgo = new Date(); tenHoursAgo.setHours(tenHoursAgo.getHours() - 10); const testOrder = await testFactory.createTestOrder(testUser.id, { tenantId: 1, createdAt: twentyFiveHoursAgo, payState: 0, // 未支付 state: 0, // 未发货 expireTime: tenHoursAgo // 已过期 }); // 手动触发处理 const result = await schedulerService.triggerManualProcess(1); // 验证处理了过期订单(创建时间超过24小时且已过期) expect(result.success).toBe(true); expect(result.processedOrders).toBe(1); }); it('不应该处理未来过期时间的订单', async () => { // 创建一个25小时前创建的订单,但过期时间在未来 const twentyFiveHoursAgo = new Date(); twentyFiveHoursAgo.setHours(twentyFiveHoursAgo.getHours() - 25); const futureTime = new Date(); futureTime.setHours(futureTime.getHours() + 1); // 1小时后过期 await testFactory.createTestOrder(testUser.id, { tenantId: 1, createdAt: twentyFiveHoursAgo, payState: 0, // 未支付 state: 0, // 未发货 expireTime: futureTime // 未来才过期 }); // 手动触发处理 const result = await schedulerService.triggerManualProcess(1); // 验证没有处理(因为过期时间还没到) expect(result.success).toBe(true); expect(result.processedOrders).toBe(0); }); }); describe('手动触发处理功能', () => { it('应该能够手动触发处理指定租户的超时订单', async () => { // 为租户1创建超时订单 const twentyFiveHoursAgo = new Date(); twentyFiveHoursAgo.setHours(twentyFiveHoursAgo.getHours() - 25); const order1 = await testFactory.createTestOrder(testUser.id, { tenantId: 1, createdAt: twentyFiveHoursAgo, payState: 0, state: 0 }); // 手动触发处理租户1 const result = await schedulerService.triggerManualProcess(1); expect(result.success).toBe(true); expect(result.processedOrders).toBe(1); expect(result.message).toContain('成功处理 1 个超时订单'); }); it('应该正确处理多个超时订单', async () => { // 创建多个超时订单 const twentyFiveHoursAgo = new Date(); twentyFiveHoursAgo.setHours(twentyFiveHoursAgo.getHours() - 25); // 创建3个超时订单 for (let i = 0; i < 3; i++) { await testFactory.createTestOrder(testUser.id, { tenantId: 1, createdAt: twentyFiveHoursAgo, payState: 0, state: 0 }); } // 手动触发处理 const result = await schedulerService.triggerManualProcess(1); expect(result.success).toBe(true); expect(result.processedOrders).toBe(3); }); it('应该处理订单状态在检查期间发生变化的情况', async () => { // 创建一个超时订单 const twentyFiveHoursAgo = new Date(); twentyFiveHoursAgo.setHours(twentyFiveHoursAgo.getHours() - 25); const testOrder = await testFactory.createTestOrder(testUser.id, { tenantId: 1, createdAt: twentyFiveHoursAgo, payState: 0, state: 0 }); // 在手动处理前,先手动支付订单 const orderRepository = dataSource.getRepository(OrderMt); await orderRepository.update( { id: testOrder.id, tenantId: 1 }, { payState: 2, updatedAt: new Date() } ); // 手动触发处理 const result = await schedulerService.triggerManualProcess(1); // 验证没有处理已支付的订单 expect(result.success).toBe(true); expect(result.processedOrders).toBe(0); // 验证订单状态仍然是已支付 const updatedOrder = await orderRepository.findOne({ where: { id: testOrder.id, tenantId: 1 } }); expect(updatedOrder!.payState).toBe(2); }); it('应该恢复超时订单的商品库存', async () => { // 创建测试商品 const testGoods = await testFactory.createTestGoods(testUser.id, { tenantId: 1, stock: 10 }); // 创建超时订单 const twentyFiveHoursAgo = new Date(); twentyFiveHoursAgo.setHours(twentyFiveHoursAgo.getHours() - 25); const testOrder = await testFactory.createTestOrder(testUser.id, { tenantId: 1, createdAt: twentyFiveHoursAgo, payState: 0, state: 0 }); // 创建订单商品(购买2个) await testFactory.createTestOrderGoods(testOrder.id, testGoods.id, { tenantId: 1, num: 2 }); // 手动触发处理 const result = await schedulerService.triggerManualProcess(1); expect(result.success).toBe(true); expect(result.processedOrders).toBe(1); // 验证商品库存已恢复 const goodsRepository = dataSource.getRepository(GoodsMt); const updatedGoods = await goodsRepository.findOne({ where: { id: testGoods.id, tenantId: 1 } }); expect(Number(updatedGoods!.stock)).toBe(12); // 原库存10 + 退回2 = 12 }); it('处理失败时应该返回错误信息', async () => { // 创建未指定租户的调度器(tenantId为null) const invalidScheduler = new OrderTimeoutSchedulerService(dataSource); // 尝试处理但不指定租户ID const result = await invalidScheduler.triggerManualProcess(); expect(result.success).toBe(false); expect(result.processedOrders).toBe(0); expect(result.message).toContain('未指定租户ID'); }); }); describe('调度器生命周期管理', () => { it('应该能够成功启动和停止调度器', async () => { // 启动调度器 await schedulerService.start(); // 验证调度器状态 const status = schedulerService.getStatus(); expect(status.isRunning).toBe(true); expect(status.tenantId).toBe(1); expect(status.checkInterval).toBe('*/5 * * * *'); // 默认检查间隔 // 停止调度器 await schedulerService.stop(); // 验证调度器已停止 const stoppedStatus = schedulerService.getStatus(); expect(stoppedStatus.isRunning).toBe(false); }); it('重复启动调度器应该抛出错误', async () => { // 第一次启动 await schedulerService.start(); // 第二次启动应该失败 await expect(schedulerService.start()).rejects.toThrow('订单超时调度器已经在运行中'); // 清理:停止调度器 await schedulerService.stop(); }); it('停止未运行的调度器应该抛出错误', async () => { await expect(schedulerService.stop()).rejects.toThrow('订单超时调度器未在运行中'); }); it('健康检查应该返回正确的状态', async () => { // 调度器未启动时的健康检查 let health = await schedulerService.healthCheck(); expect(health.healthy).toBe(false); expect(health.isRunning).toBe(false); expect(health.timestamp).toBeInstanceOf(Date); // 启动调度器后的健康检查 await schedulerService.start(); health = await schedulerService.healthCheck(); expect(health.healthy).toBe(true); expect(health.isRunning).toBe(true); // 停止调度器 await schedulerService.stop(); }); }); describe('租户数据隔离', () => { it('应该只处理指定租户的超时订单', async () => { // 创建租户1的用户和订单 const tenant1User = await testFactory.createTestUser(1); const tenant2User = await testFactory.createTestUser(2); const twentyFiveHoursAgo = new Date(); twentyFiveHoursAgo.setHours(twentyFiveHoursAgo.getHours() - 25); // 租户1的超时订单 await testFactory.createTestOrder(tenant1User.id, { tenantId: 1, createdAt: twentyFiveHoursAgo, payState: 0, state: 0 }); // 租户2的超时订单 await testFactory.createTestOrder(tenant2User.id, { tenantId: 2, createdAt: twentyFiveHoursAgo, payState: 0, state: 0 }); // 创建租户1的调度器 const tenant1Scheduler = new OrderTimeoutSchedulerService(dataSource, 1); // 处理租户1 const result = await tenant1Scheduler.triggerManualProcess(1); // 验证只处理了租户1的订单 expect(result.success).toBe(true); expect(result.processedOrders).toBe(1); // 验证租户2的订单未被处理 const orderRepository = dataSource.getRepository(OrderMt); const tenant2Orders = await orderRepository.find({ where: { tenantId: 2, payState: 0 } }); expect(tenant2Orders.length).toBe(1); // 租户2的订单仍然是未支付状态 }); it('应该能够处理多个租户的超时订单', async () => { // 创建多个租户的用户和订单 const tenant1User = await testFactory.createTestUser(1); const tenant2User = await testFactory.createTestUser(2); const tenant3User = await testFactory.createTestUser(3); const twentyFiveHoursAgo = new Date(); twentyFiveHoursAgo.setHours(twentyFiveHoursAgo.getHours() - 25); // 每个租户创建一个超时订单 await testFactory.createTestOrder(tenant1User.id, { tenantId: 1, createdAt: twentyFiveHoursAgo, payState: 0, state: 0 }); await testFactory.createTestOrder(tenant2User.id, { tenantId: 2, createdAt: twentyFiveHoursAgo, payState: 0, state: 0 }); await testFactory.createTestOrder(tenant3User.id, { tenantId: 3, createdAt: twentyFiveHoursAgo, payState: 0, state: 0 }); // 创建未指定租户的调度器(可以处理多个租户) const globalScheduler = new OrderTimeoutSchedulerService(dataSource); // 为每个租户单独处理 const result1 = await globalScheduler.triggerManualProcess(1); const result2 = await globalScheduler.triggerManualProcess(2); const result3 = await globalScheduler.triggerManualProcess(3); // 验证每个租户都处理成功 expect(result1.success).toBe(true); expect(result2.success).toBe(true); expect(result3.success).toBe(true); expect(result1.processedOrders + result2.processedOrders + result3.processedOrders).toBe(3); // 验证所有订单都被关闭 const orderRepository = dataSource.getRepository(OrderMt); const openOrders = await orderRepository.find({ where: { payState: 0 } }); expect(openOrders.length).toBe(0); // 所有未支付订单都应被关闭 }); }); });