|
|
@@ -0,0 +1,479 @@
|
|
|
+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); // 所有未支付订单都应被关闭
|
|
|
+ });
|
|
|
+ });
|
|
|
+});
|